IOS CoreText-code encapsulation of text and text Mixing

Source: Internet
Author: User

IOS CoreText-code encapsulation of text and text Mixing

In the previous section, I explained in detail how to encapsulate the pure C language code of Core Text with the object-oriented idea. In this section, I will encapsulate the effect of "text and text mixing. However, the Code in this section is based on the previous section. Therefore, if you have not browsed the content in the previous section, click here. First look at the final:

 

Now, let's continue to expand the code in the previous section.

1. Added image information, so we need to modify the structure of the data source (plist ).

1) type information is added for each item. "txt" indicates plain text; "img" indicates an image; and the image information includes name, width, and height. Name is the image address, which is stored in the sandbox here. In actual development, remote images can be loaded.

2) the width and height of the image must be provided, because the Core Text layout calculates the placeholder size of each element. If the width and height information of the image are not provided, the client will calculate the width and height after loading the remote image, which is inefficient. If the network is poor, if the image cannot be loaded all the time, the Core Text layout is obviously messy. If the server data provides width and height information, even if the image is not loaded, you can also leave blank areas of the same size in place without affecting the overall layout.

 

2. Define the CoreTextImageData model to store the image name and location information.

 

@ Interface CoreTextImageData: NSObject @ property (nonatomic, copy) NSString * name; // This coordinate is the coordinate system of CoreText, rather than the coordinate system of UIKit @ property (nonatomic, assign) CGRect imagePosition; @ end

 

 

3. The CoreTextData class should contain CoreTextImageData model information. Here we use an array of imageArray, because it may contain multiple images. Therefore, the CoreTextData class is transformed. 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 to include 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 image-supported code to the loadTemplateFile method. In this way, save the img information in plist to the CoreTextImageData model.
However, the problem is that Core Text does not support image display! However, we can use a special blank character in place of the text to be displayed, and set the CTRunDelegate information of the font to the width and height of the image to be displayed, in this way, the generated CTFrame instance is reserved for the image position during painting. Because the CTDisplayView drawing code is in drawRect, we can easily draw the image to be drawn using the CGContextDrawImage method of Quartz 2D. The process described here is implemented in the called parseImageDataFromNSDictionary.

 

 

+ (NSAttributedString *) loadTemplateFile :( NSString *) path config :( CTFrameParserConfig *) config imageArray :( NSMutableArray *) imageArray {NSMutableAttributedString * result = [NSMutableAttributedString alloc] init]; // obtain data in JSON format // NSArray * array = [NSJSONSerialization JSONObjectWithData: data options: Unknown 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 parseAttributedContentFromNSDictionary: dict config: config]; [result appendAttributedString: as];} else if ([type is%tostring: @ "img"]) {CoreTextImageData * imageData = [[CoreTextImageData alloc] init]; imageData. name = dict [@ "name"]; [imageArray addObject: imageData]; NSAttributedString * as = [self parseImageDataFromNSDictionary: dict config: config]; [result appendAttributedString: as] ;}}} return result ;}

 

6. The placeholder character and CTRunDelegate for setting the placeholder character. The Code uses the character '0xfffc 'to hold the placeholder.

 

Static CGFloat ascentCallback (void * ref) {return [(NSNumber *) [(_ bridge NSDictionary *) ref objectForKey: @ "height"] floatValue];} static CGFloat descentCallback (void * ref) {return 0;} static CGFloat widthCallback (void * ref) {return [(NSNumber *) [(_ bridge NSDictionary *) ref objectForKey: @ "width"] floatValue];} + (NSAttributedString *) parseImageDataFromNSDictionary :( NSDictionary *) dict config :( CTFrameParserConfig *) config {CTRunDelegateCallbacks callbacks; // memset sets the value of the first n Bytes of callbacks in the opened memory space to 0, equivalent to initializing memset (& callbacks, 0, sizeof (CTRunDelegateCallbacks) for ctrundelegs memory space )); callbacks. version = kCTRunDelegateVersion1; callbacks. getAscent = ascentCallback; callbacks. getDescent = descentCallback; callbacks. getWidth = widthCallback; CTRunDelegateRef delegate = callback (& callbacks, (_ bridge void *) (dict); // use 0 xFFFC as the placeholder unichar objectReplacementChar = 0 xFFFC; NSString * content = [NSString stringWithCharacters: & objectReplacementChar length: 1]; NSDictionary * attributes = [self attributesWithConfig: config]; optional * space = [NSMutableAttributedString alloc] initWithString: content attributes: attributes]; CFAttributedStringSetAttribute (CFMutableAttributedStringRef) space, CFRangeMake (0, 1), kCTRunDelegateAttributeName, delegate); CFRelease (delegate); return space ;}

7. After the code at is executed, the code will return to and execute the following code:

 

 

data.imageArray = imageArray;

In fact, it overwrites the imageArray attribute method in CoreTextData. The purpose of the code below is to calculate the actual placeholder size of blank characters. I will give a rough description of the following code:

 

1) Call the CTFrameGetLines method to obtain all the ctlines.

2) Call the CTFrameGetLineOrigins method to obtain the starting coordinate of each row.

3) Call the CTLineGetGlyphRuns method to obtain all CTRun of each row.

4) Find the information with the key CTRunDelegateAttributeName through the ctrbutes information of CTRun. If it exists, it indicates that it is a placeholder character; otherwise, it will be filtered out directly.

5) Calculate 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 row CGPoint lineOrigins [lineCount]; CTFrameGetLineOrigins (self. ctFrame, CFRangeMake (0, 0), lineOrigins); int imageIndex = 0; CoreTextImageDa Ta * 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 null, it indicates that it is not an image if (! Delegate) continue; NSDictionary * metaDict = CTRunDelegateGetRefCon (delegate); if (! [MetaDict isKindOfClass: [NSDictionary class]) continue;/* determine the frame of the image run */CGRect runBounds; CGFloat ascent, descent; runBounds. size. width = CTRunGetTypographicBounds (run, CFRangeMake (0, 0), & ascent, & descent, NULL); runBounds. size. height = ascent + descent; // calculates the offset CGFloat xOffset = CTLineGetOffsetForStringIndex (line, CTRunGetStringRange (run) of the image relative to the start position of each row ). location, 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. modify the code in CTDisplayView to complete the painting.

 

1) Call the CTFrameDraw method to complete the overall painting. At this time, the image area is a blank display of the actual size of the image.

2) traverse the imageArray array in CoreTextData and use the CGContextDrawImage method to draw an image in the corresponding blank area.

 

-(Void) drawRect :( CGRect) rect {[super drawRect: rect]; CGContextRef context = equals (); CGContextSetTextMatrix (context, plaintext); CGContextTranslateCTM (context, 0, self. bounds. size. height); CGContextScaleCTM (context, 1.0,-1.0); // you can specify if (self. data) {CTFrameDraw (self. data. ctFrame, context);} // draw the image for (CoreTextImageData * imageData in self. data. imageArray) {UIImage * image = [UIImage imageNamed: imageData. name]; if (image) {CGContextDrawImage (context, imageData. imagePosition, image. CGImage );}}}

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.