In the previous sections, I reproduced other people's blogs, describing the basic concepts and use of core text in detail, but it seems that the demo he offers is process-oriented and code is not easy to manage and maintain. In the next few sections, I'll step through the core text code to make it look less cluttered (because the core text is a pure C syntax). Next, let's look at an "IOS Text Design and Rendering Architecture" architecture diagram.
, the bottom core graphics is the core painting, I have explained in detail in the Quartz 2D chapter, then the above layer is the core Text. Let's take a look at the demo of a core text I've implemented.
1. We take a look at the original code implementation process, we can see that the code, the transformation of the coordinate system, the initialization of the path, string processing, frame creation and the final drawing all put together to deal with, when encountering complex business requirements, the code is bloated and unfavorable to maintenance.
Cgcontextref context = Uigraphicsgetcurrentcontext (); Cgcontextsettextmatrix (context, cgaffinetransformidentity); CGCONTEXTTRANSLATECTM (context, 0, self.bounds.size.height); CGCONTEXTSCALECTM (Context, 1.0, -1.0); Cgmutablepathref path = cgpathcreatemutable (); Cgpathaddrect (Path, NULL, self.bounds); nsattributedstring *attrstring = [[Nsattributedstring alloc] initwithstring:@ "Big talk ..." This truncates the string and then sets the text and color for each paragraph ctframesetterref framesetter = ctframesettercreatewithattributedstring (( CFATTRIBUTEDSTRINGREF) attrstring); Ctframeref frame = Ctframesettercreateframe (Framesetter, Cfrangemake (0, Attrstring.length), Path, NULL); Ctframedraw (frame, context); Cfrelease (frame); Cfrelease (path); Cfrelease (Framesetter);
2. Consider encapsulating your code
For a complex typesetting engine, it is possible to split its functionality into the following classes to complete.
1) A display class that is only responsible for displaying content and is not responsible for typesetting.
2) A model class that is used to host all the data needed for the display.
3) A typesetting class for the layout of text content.
4) A configuration class for implementing some configurable items at the time of typesetting.
As described above, we can split the above code into 4 classes:
1) Ctframeparserconfig class, used to configure drawing parameters such as text color, size, line spacing, and so on.
2) Ctframeparser class, used to generate the Ctframeref instance required for the final drawing interface.
3) The Coretextdata class, which holds the Ctframeref instances generated by the Ctframeparser class and the height required for ctframeref actual drawing.
4) Ctdisplayview class, holding an instance of the Coretextdata class, is responsible for drawing ctframeref to the interface.
Let me introduce the classes described above:
1) Ctframeparserconfig class, mainly initializes the text width, size, line spacing, color information. The code is as follows:
@interface Ctframeparserconfig:nsobject@property (nonatomic,assign) cgfloat width; @property (nonatomic,assign) CGFloat fontSize; @property (nonatomic,assign) cgfloat linespace; @property (Nonatomic,strong) Uicolor *textcolor; @end # Define RGB (a,b,c) [Uicolor colorwithred: (a/255.0) Green: (b/255.0) Blue: (c/255.0) alpha:1.0] #import " CTFrameParserConfig.h "@implementation ctframeparserconfig-(instancetype) init { if (self = [super init]) { Self.width = 300.0f; Self.fontsize = 16.0f; Self.linespace = 8.0f; Self.textcolor = RGB (108, 108, 108); } return self ;} @end
2) The Coretextdata class, which defines two properties, is used to store the height of the ctframeref and the resulting view (since the height is calculated dynamically based on the text content) of the drawing required.
@interface Coretextdata:nsobject@property (nonatomic,assign) ctframeref ctframe; @property (nonatomic,assign) CGFloat Height; @end @implementation coretextdata-(void) Setctframe: (ctframeref) Ctframe { if (_ctframe! = ctframe) { if (_ctframe! = nil) { cfrelease (_ctframe); } Cfretain (ctframe); _ctframe = Ctframe; }} -(void) Dealloc { if (_ctframe! = nil) { cfrelease (_ctframe); _ctframe = nil; }} @end
3) The Ctdisplayview class, which defines the attribute data, is used to receive the model data that is passed in from outside, and then completes the drawing work in the Drwrect method.
I. Rotate the coordinate system before drawing, because the default coordinate system origin of the core text is in the lower-left corner.
II. Call the Ctframedraw method directly to complete the drawing work.
@interface Ctdisplayview:uiview@property (Nonatomic,strong) coretextdata *data; @end-(void) DrawRect: (cgrect) Rect { [Super Drawrect:rect]; Cgcontextref context = Uigraphicsgetcurrentcontext (); Cgcontextsettextmatrix (context, cgaffinetransformidentity); CGCONTEXTTRANSLATECTM (context, 0, self.bounds.size.height); CGCONTEXTSCALECTM (Context, 1.0, -1.0); if (self.data) { Ctframedraw (self.data.ctFrame, context);} }
4) Ctframeparser class, this class is the core class, the main completion is used to generate the final drawing interface required Ctframeref instance. First look at the code in the CTFrameParser.h file:
@interface ctframeparser:nsobject/* the entire paragraph of text */+ (Coretextdata *) Parsecontent: (NSString *) content config: ( Ctframeparserconfig *) config;/* Customize your own typesetting */+ (Coretextdata *) Parsetemplatefile: (NSString *) path config: ( Ctframeparserconfig *) config; @end
I. The whole paragraph of text layout, direct operation of the content string passed over, the result is the final display of the effect is: all the font size, color, line spacing are consistent. For example: Fiction application.
First look at the Parsecontent method:
+ (Coretextdata *) Parsecontent: (NSString *) content config: (ctframeparserconfig *) Config { nsdictionary * attributes = [self attributeswithconfig:config]; nsattributedstring *contentstring = [[Nsattributedstring alloc] initwithstring:content attributes:attributes]; return [self parseattributedcontent:contentstring config:config];}
The first sentence in the code Attrbuteswithconfig method is to initialize the default information values passed from the Ctframeparserconfig.
+ (Nsdictionary *) Attributeswithconfig: (ctframeparserconfig *) Config {cgfloat fontSize = config.fontsize; Ctfontref fontref = Ctfontcreatewithname ((cfstringref) @ "ARIALMT", FontSize, NULL); CGFloat linespacing = config.linespace; Const Cfindex knumberofsettings = 3; Ctparagraphstylesetting Thesettings[knumberofsettings] = {{kctparagraphstylespecifierlinespacingadjustment,sizeof (CGFloat), &linespacing}, {kctparagraphstylespecifiermaximumlinespacing,sizeof (cgfloat), &lineSpacing}, {kctparagraphstylespecifierminimumlinespacing,sizeof (cgfloat), &linespacing}}; Ctparagraphstyleref theparagraphref = ctparagraphstylecreate (thesettings, knumberofsettings); Uicolor *textcolor = Config.textcolor; Nsmutabledictionary *dict = [Nsmutabledictionary dictionary]; dict[(ID) kctforegroundcolorattributename] = (ID) textcolor.cgcolor; dict[(ID) kctfontattributename] = (__bridge id) fontref; dict[(ID) kctparagraphstyleattributename] = (__bridge id) theparagraphref; Cfrelease (THEPARAGRAPHREF); Cfrelease (FONTREF); return dict;}
the Parseattributecontent method in the code is to return the final Coretextdata model data, which is eventually used for Ctdisplayview drawing.
+ (Coretextdata *) Parseattributedcontent: (nsattributedstring *) content config: (ctframeparserconfig *) Config { // Create Ctframesetterref instance ctframesetterref framesetter = ctframesettercreatewithattributedstring (( CFATTRIBUTEDSTRINGREF) (content); Obtain the height of the area to be drawn cgsize restrictsize = Cgsizemake (Config.width, Cgfloat_max); Cgsize coretextsize = ctframesettersuggestframesizewithconstraints (framesetter, cfrangemake (0, 0), nil, restrictSize, NIL); CGFloat textHeight = coretextsize.height; Generate Ctframeref instance ctframeref frame = [Self createframewithframesetter:framesetter config:config height:textheight ]; Save the generated ctframeref instance and the calculated drawing height to the Coretextdata instance, and return to coretextdata *data = [[Coretextdata alloc] init]; Data.ctframe = frame; Data.height = textHeight; Memory release cfrelease (frame); Cfrelease (framesetter); return data;}
II. Custom typography, as I posted at the beginning of the article.
/* Customize your own typesetting */+ (Coretextdata *) Parsetemplatefile: (NSString *) path config: (ctframeparserconfig *) Config { Nsattributedstring *content = [self Loadtemplatefile:path config:config]; return [self parseattributedcontent:content config:config];}
The first thing to look at is the Loadtempllatefile method, which is used to load the data source, "typesetting the entire text", the data source is a content string, and now this case, the data source is custom, it is Plist,json data form. For demonstration convenience, I am taking the plist form here. The plist structure of the preparation is broadly as follows:
The Loadtemplatefile method is specifically implemented as follows:
+ (nsattributedstring *) Loadtemplatefile: (NSString *) path config: (ctframeparserconfig *) Config { nsmutableattributedstring *result = [[Nsmutableattributedstring alloc] init]; JSON way to get data // Nsarray *array = [nsjsonserialization jsonobjectwithdata:data options: Nsjsonreadingallowfragments Error:nil]; Nsarray *array = [Nsarray Arraywithcontentsoffile:path]; if (array) { if ([Array Iskindofclass:[nsarray class]]) {for (nsdictionary *dict in array) { nsattributedstr ing *as = [self parseattributedcontentfromnsdictionary:dict config:config]; [Result Appendattributedstring:as]; }} return result;}
As you can see, in the code, traverse every dictionary data in the plist file, and then call the Parseattributedcontentfromnsdictionary method for the specific processing of the text, so as to ensure that the style of each paragraph is inconsistent, To achieve a custom effect. The Parseattributedcontentfromnsdictionary method code is as follows:
+ (nsattributedstring *) Parseattributedcontentfromnsdictionary: (nsdictionary *) dict config: (ctframeparserconfig *) Config {nsmutabledictionary *attributes = (nsmutabledictionary *) [self attributeswithconfig:config]; Uicolor *color = [self colorfromtemplate:dict[@ "color]]; if (color) {attributes[(id) kctforegroundcolorattributename] = (ID) color. Cgcolor; } cgfloat fontSize = [dict[@ "size"] floatvalue]; if (FontSize > 0) {ctfontref fontref = Ctfontcreatewithname ((cfstringref) @ "ARIALMT", FontSize, NULL); attributes[(ID) kctfontattributename] = (__bridge ID) (FONTREF); Cfrelease (FONTREF); } nsstring *content = dict[@ "Content"]; return [[Nsattributedstring alloc] initwithstring:content attributes:attributes];} + (Uicolor *) Colorfromtemplate: (NSString *) name {if ([name isequaltostring:@ "Blue"]) {return [Uicolor Bluecolo R]; } else if ([name isequaltostring:@ "Green"]) {return [Uicolor Greencolor]; } ELSE if ([Name isequaltostring:@ "Red"]) {return [Uicolor Redcolor]; } else if ([name isequaltostring:@ "Purple"]) {return [Uicolor Purplecolor]; } else {return nil; }}
at this point, all the definitions and implementations of the classes are complete, and finally we call the following code:
Ctframeparserconfig *config = [[Ctframeparserconfig alloc] init]; Config.width = Self.ctView.width; NSString *path = [[NSBundle mainbundle] pathforresource:@ "tempdata.plist" oftype:nil]; Coretextdata *data = [Ctframeparser parsetemplatefile:path config:config]; Pass the data to Ctdisplayview, then draw the content self.ctView.data = data; Set the height of the ctdisplayview self.ctView.height = data.height;
See the above code, is not instantaneous feel easy to read, and blocked those dull C language interface. So we want to implement a custom template, as long as we provide good plist or JSON file data form and content.
Is the UML of the framework:
1. Ctframeparser generates Coretextdata instances by Ctframeparserconfig instances.
2. Ctdisplayview obtains all the information needed to draw by holding a coretextdata instance.
3. The Viewcontroller class configures the Ctframeparserconfig instance, obtains the generated Coretextdata instance, and assigns it to its Ctdisplayview member to achieve the effect of displaying the specified content on the interface.
IOS Coretext---code encapsulation