In the previous section, I explained in detail the use of object-oriented thinking to encapsulate the pure C language code of core text. In this section, I will also encapsulate the effects of the "text-to-picture" process. However, the code for this section is based on the previous section, so if you have not browsed through the previous section, click here. First look at the final:
Now, let's move on to the code in the previous section and continue to expand.
1. Added picture information, so we need to modify the structure of the data source (plist)
1) Added type information for each item, "TXT" for plain text, "img" for pictures, and picture information including Name,width,height. Name is the address of the picture, I here is stored in the sandbox, the actual development time, you can load remote pictures.
2) Be sure to provide the width and height information for the image, because the core text layout is to calculate the placeholder size of each element. If you do not provide a picture of the width and height information, the client after loading remote pictures, but also to calculate the width and height, inefficient, if the network is poor, the picture has not been loaded, then the core text layout is obviously confusing If the service-side data provides width and height information, even if the picture is not loaded, it can have the same size of white space is occupied, does not affect the overall layout.
2. Define the Coretextimagedata model to store the picture's name and location information
@interface Coretextimagedata:nsobject@property (nonatomic,copy) nsstring *name;//This coordinate is coretext's coordinate system, Instead of the Uikit coordinate system @property (nonatomic,assign) cgrect imageposition; @end
3. The Coretextdata class should contain Coretextimagedata model information, which is used in the array imagearray, as it is possible to include multiple images. So change the Coretextdata class, the CORETEXTDATA.H code is as follows:
@interface Coretextdata:nsobject@property (nonatomic,assign) ctframeref ctframe; @property (nonatomic,assign) CGFloat Height; @property (nonatomic,strong) Nsarray *imagearray; @end
4. Transform the Parsetemplatefile method in the Ctframeparser class so that it contains coretextimagedata information
+ (Coretextdata *) Parsetemplatefile: (NSString *) path config: (ctframeparserconfig *) Config { Nsmutablearray * Imagearray = [Nsmutablearray array]; Nsattributedstring *content = [self Loadtemplatefile:path config:config imagearray:imagearray]; Coretextdata *data = [self parseattributedcontent:content config:config]; Data.imagearray = Imagearray; return data;}
5. Add the code that supports image in the Loadtemplatefile method, so that the information about IMG in plist is saved to the Coretextimagedata model.
But the problem is, the Core text itself does not support the display of the image function! However, we can substitute a special whitespace character where we want to display the text, and set the ctrundelegate information of the font to the width and height of the picture to be displayed, so that the last generated instance of Ctframe will reserve the position of the picture when it is drawn. Because Ctdisplayview's drawing code is inside the DrawRect, so we can easily draw the picture, with quartz 2D Cgcontextdrawimage method directly drawn out on the line. The process I describe here is implemented in the parseimagedatafromnsdictionary of the call.
+ (nsattributedstring *) Loadtemplatefile: (NSString *) path config: (ctframeparserconfig *) config Imagearray: ( Nsmutablearray *) imagearray{nsmutableattributedstring *result = [[Nsmutableattributedstring alloc] init]; JSON way to get data//Nsarray *array = [Nsjsonserialization jsonobjectwithdata:data options:nsjsonreadingallowfragmen TS Error:nil]; Nsarray *array = [Nsarray Arraywithcontentsoffile:path]; if (array) {if ([array Iskindofclass:[nsarray class]]) {for (nsdictionary *dict in array) { NSString *type = dict[@ "type"]; if ([Type isequaltostring:@ "TXT"]) {nsattributedstring *as = [Self parseattributedcontentfromnsdiction Ary:dict Config:config]; [Result Appendattributedstring:as]; } else if ([Type isequaltostring:@ "IMG"]) {coretextimagedata *imagedata = [[Coretextimagedata alloc] I NIT]; Imagedata.name = dict[@ "name"]; [Imagearray Addobject:imagedata]; Nsattributedstring *as = [self parseimagedatafromnsdictionary:dict config:config]; [Result Appendattributedstring:as]; }}}} return result;
6. Placeholder characters and the ctrundelegate that set the placeholder characters, which are occupied by the character ' 0xFFFC ' in the code.
Static CGFloat ascentcallback (void *ref) {return [(NSNumber *) [(__bridge nsdictionary *) ref objectforkey:@ "Height"] fl Oatvalue];} Static CGFloat descentcallback (void *ref) {return 0;} Static CGFloat widthcallback (void *ref) {return [(NSNumber *) [(__bridge nsdictionary *) ref objectforkey:@ "width"] Floa TValue];} + (nsattributedstring *) Parseimagedatafromnsdictionary: (nsdictionary *) dict config: (ctframeparserconfig *) Config { Ctrundelegatecallbacks callbacks; Memset sets the value of the first n bytes of the Open memory space callbacks to a value of 0, which is equivalent to initializing the ctrundelegatecallbacks memory space memset (&callbacks, 0, sizeof (ctrunde Legatecallbacks)); Callbacks.version = KCTRunDelegateVersion1; Callbacks.getascent = Ascentcallback; Callbacks.getdescent = Descentcallback; Callbacks.getwidth = Widthcallback; Ctrundelegateref delegate = Ctrundelegatecreate (&callbacks, (__bridge void *) (dict)); Use 0xFFFC as the blank placeholder Unichar Objectreplacementchar = 0xFFFC; NSString *content = [NSString StringwithcharacTers:&objectreplacementchar Length:1]; Nsdictionary *attributes = [self attributeswithconfig:config]; nsmutableattributedstring *space = [[Nsmutableattributedstring alloc] initwithstring:content attributes:attributes]; Cfattributedstringsetattribute (Cfmutableattributedstringref) space, Cfrangemake (0, 1), Kctrundelegateattributename, delegate); Cfrelease (delegate); return space;}
7. After the code in 5, 62 points is executed, the code returns to the 4th and executes the following code:
Data.imagearray = Imagearray;
it actually overrides the Imagearray property method in Coretextdata, the purpose of which is to calculate the actual placeholder size of the whitespace character. For the following code, I have a general description:
1) Get all the ctline by calling the Ctframegetlines method.
2) Gets the starting coordinates of each row by calling the Ctframegetlineorigins method.
3) Get all the ctrun of each row by calling the Ctlinegetglyphruns method.
4) by ctrun the attributes information to find the key to ctrundelegateattributename information, if present, indicating that he is a placeholder character, otherwise the word directly filtered out.
5) The final calculation obtains the actual size of each placeholder character.
-(void) Setimagearray: (Nsarray *) Imagearray {_imagearray = Imagearray; [Self fillimageposition];} -(void) Fillimageposition {if (Self.imageArray.count = = 0) return; Nsarray *lines = (Nsarray *) ctframegetlines (self.ctframe); int linecount = Lines.count; The starting coordinate of each line cgpoint Lineorigins[linecount]; Ctframegetlineorigins (Self.ctframe, cfrangemake (0, 0), lineorigins); int imageindex = 0; Coretextimagedata *imagedata = self.imagearray[0]; for (int i = 0; i < LineCount; i++) {if (!imagedata) break; Ctlineref line = (__bridge ctlineref) (Lines[i]); Nsarray *runobjectarray = (Nsarray *) ctlinegetglyphruns (line); For (ID runobject in Runobjectarray) {ctrunref run = (__bridge ctrunref) (runobject); Nsdictionary *runattributes = (nsdictionary *) ctrungetattributes (run); Ctrundelegateref delegate = (__bridge ctrundelegateref) ([Runattributes Valueforkey: (ID) kctrundelegateattributename] ); If delegate is empty, indicates that it is not a picture if (!delegate) continue; Nsdictionary *metadict = Ctrundelegategetrefcon (delegate); if (![ Metadict Iskindofclass:[nsdictionary class]]) continue; /* Determine the picture run frame */CGRect runbounds; CGFloat ascent,descent; RunBounds.size.width = Ctrungettypographicbounds (run, cfrangemake (0, 0), &ascent, &descent, NULL); RunBounds.size.height = ascent + descent; Calculates the offset of the picture relative to the X-direction of the starting position of each row cgfloat Xoffset = Ctlinegetoffsetforstringindex (line, Ctrungetstringrange (run). Locat Ion, NULL); runbounds.origin.x = lineorigins[i].x + xoffset; RUNBOUNDS.ORIGIN.Y = LINEORIGINS[I].Y; RUNBOUNDS.ORIGIN.Y-= descent; Imagedata.imageposition = Runbounds; imageindex++; if (ImageIndex = = self.imageArray.count) {imageData = nil; Break } else { ImageData = Self.imagearray[imageindex]; } } }}
8. Transform the code in the Ctdisplayview to complete the drawing work.
1) First call the Ctframedraw method to complete the overall drawing, at this time the picture area is the actual size of the picture blank display.
2) traverse the Imagearray array in the Coretextdata, and use the Cgcontextdrawimage method to draw the picture in the corresponding blank area.
-(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); The whole is drawn first if (self.data) { Ctframedraw (self.data.ctFrame, context); } Draw the picture for (Coretextimagedata *imagedata in Self.data.imageArray) { UIImage *image = [UIImage imagenamed: Imagedata.name]; if (image) { cgcontextdrawimage (context, imagedata.imageposition, image. cgimage);}}}
IOS Coretext---Graphics and text blending code encapsulation