博客> 关于iOS 框架的整体架构认识和使用
关于iOS 框架的整体架构认识和使用
2019-06-16 03:47 评论:0 阅读:437 Wensidun

本人从开发到现在有几年时间了,但是一直以来都没有认真整理过iOS学习过的相关内容。今年下半年才开始整理相关框架的使用,今天发一篇关于CoreText框架的文章。

  1. CoreText框架的概括 它是用于文本布局和处理字体的底层技术,可以与CoreGraphics框架协同工作。同时可以直接用于多线程编程。 CoreText有着基于c语言的并与平台无关的API,在iOS和OS X系统中的CoreText框架的API没有较大差异。

2.CoreText布局引擎的架构  core_text_arch_2x.png 在运行时会形成如上图的层次。顶层是framesetter对象,通过属性字符串作为输入,framesetter会生成一个或多个文本frame,每个CTFrame代表一个段落。每个CTFrame对象包含着段落行对象(CTLine),每个行对象代表着一行文本。CTFrame对象可以表示一行文本或多行文本对象集。在生成CTFrame对象的同时,CTLine对象直接被绘制到Graphics Context中。每个CTLine对象包含一个字符管理对象(CTRun)的数组,每个CTRun对象代表着具有相同属性的字符集。CTTypeSetter对象通过字符串(character strings)、字体(Font)和属性(attributes)生成字符管理(CTRun)。这意味着一行文本需要一个或者多个CTRun对象。CTRun对象会直接被绘制到Graphics Context对象中。

3.CTFont对象 通过CTFontDescriptor生成CTFont对象。  query_font_system_2x.png CTFont在将字符绘制到Context中时在字符布局之间提供帮助,并创建当前字体对象(Font)。它包含着大量信息,例如字符大小,字符与象形符号的匹配(character to glyph),编码(encoding), 字体混合数据(font metric data),象形符号数据(glyph data)等等。

以下就通用的文本布局操作和通用的字体操作举例。

1.段落布局(sample paragraph) // Initialize a graphics context in iOS. CGContextRef context = UIGraphicsGetCurrentContext();

// Flip the context coordinates, in iOS only. CGContextTranslateCTM(context, 0, self.bounds.size.height); CGContextScaleCTM(context, 1.0, -1.0);

// Initializing a graphic context in OS X is different: // CGContextRef context = // (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];

// Set the text matrix. CGContextSetTextMatrix(context, CGAffineTransformIdentity);

// Create a path which bounds the area where you will be drawing text. // The path need not be rectangular. CGMutablePathRef path = CGPathCreateMutable();

// In this simple example, initialize a rectangular path. CGRect bounds = CGRectMake(10.0, 10.0, 200.0, 200.0); CGPathAddRect(path, NULL, bounds );

// Initialize a string. CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine.");

// Create a mutable attributed string with a max length of 0. // The max length is a hint as to how much internal storage to reserve. // 0 means no hint. CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);

// Copy the textString into the newly created attrString CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), textString);

// Create a color that will be added as an attribute to the attrString. CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); CGFloat components[] = { 1.0, 0.0, 0.0, 0.8 }; CGColorRef red = CGColorCreate(rgbColorSpace, components); CGColorSpaceRelease(rgbColorSpace);

// Set the color of the first 12 chars to red. CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 12), kCTForegroundColorAttributeName, red);

// Create the framesetter with the attributed string. CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString); CFRelease(attrString);

// Create a frame. CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);

// Draw the specified frame in the given context. CTFrameDraw(frame, context);

// Release the objects we used. CFRelease(frame); CFRelease(path); CFRelease(framesetter);

2.简单的文本标签

CFStringRef string; CTFontRef font; CGContextRef context; // Initialize the string, font, and context

CFStringRef keys[] = { kCTFontAttributeName }; CFTypeRef values[] = { font };

CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void)&keys, (const void)&values, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); CFRelease(string); CFRelease(attributes);

CTLineRef line = CTLineCreateWithAttributedString(attrString);

// Set text position and draw the line into the graphics context CGContextSetTextPosition(context, 10.0, 10.0); CTLineDraw(line, context); CFRelease(line);

3.列表布局 1.首先把view划分为多列

  • (CFArrayRef)createColumnsWithColumnCount:(int)columnCount { int column;

    CGRect columnRects = (CGRect)calloc(columnCount, sizeof(*columnRects)); // Set the first column to cover the entire view. columnRects[0] = self.bounds;

    // Divide the columns equally across the frame's width. CGFloat columnWidth = CGRectGetWidth(self.bounds) / columnCount; for (column = 0; column < columnCount - 1; column++) { CGRectDivide(columnRects[column], &columnRects[column], &columnRects[column + 1], columnWidth, CGRectMinXEdge); }

    // Inset all columns by a few pixels of margin. for (column = 0; column < columnCount; column++) { columnRects[column] = CGRectInset(columnRects[column], 8.0, 15.0); }

    // Create an array of layout paths, one for each column. CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, columnCount, &kCFTypeArrayCallBacks);

    for (column = 0; column < columnCount; column++) { CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, columnRects[column]); CFArrayInsertValueAtIndex(array, column, path); CFRelease(path); } free(columnRects); return array; } 2.执行列表布局 // Override drawRect: to draw the attributed string into columns. // (In OS X, the drawRect: method of NSView takes an NSRect parameter, // but that parameter is not used in this listing.)

  • (void)drawRect:(CGRect)rect { // Initialize a graphics context in iOS. CGContextRef context = UIGraphicsGetCurrentContext();

    // Flip the context coordinates in iOS only. CGContextTranslateCTM(context, 0, self.bounds.size.height); CGContextScaleCTM(context, 1.0, -1.0);

    // Initializing a graphic context in OS X is different: // CGContextRef context = // (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];

    // Set the text matrix. CGContextSetTextMatrix(context, CGAffineTransformIdentity);

    // Create the framesetter with the attributed string. CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString( (CFAttributedStringRef)self.attributedString);

    // Call createColumnsWithColumnCount function to create an array of // three paths (columns). CFArrayRef columnPaths = [self createColumnsWithColumnCount:3];

    CFIndex pathCount = CFArrayGetCount(columnPaths); CFIndex startIndex = 0; int column;

    // Create a frame for each column (path). for (column = 0; column < pathCount; column++) { // Get the path for this column. CGPathRef path = (CGPathRef)CFArrayGetValueAtIndex(columnPaths, column);

    // Create a frame for this column and draw it.
    CTFrameRef frame = CTFramesetterCreateFrame(
                         framesetter, CFRangeMake(startIndex, 0), path, NULL);
    CTFrameDraw(frame, context);
    
    // Start the next frame at the first character not visible in this frame.
    CFRange frameRange = CTFrameGetVisibleStringRange(frame);
    startIndex += frameRange.length;
    CFRelease(frame);

    } CFRelease(columnPaths); CFRelease(framesetter);

}

4.手动的行间距 double width; CGContextRef context; CGPoint textPosition; CFAttributedStringRef attrString; // Initialize those variables.

// Create a typesetter using the attributed string. CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString(attrString);

// Find a break for line from the beginning of the string to the given width. CFIndex start = 0; CFIndex count = CTTypesetterSuggestLineBreak(typesetter, start, width);

// Use the returned character count (to the break) to create the line. CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(start, count));

// Get the offset needed to center the line. float flush = 0.5; // centered double penOffset = CTLineGetPenOffsetForFlush(line, flush, width);

// Move the given text drawing position by the calculated offset and draw the line. CGContextSetTextPosition(context, textPosition.x + penOffset, textPosition.y); CTLineDraw(line, context);

// Move the index beyond the line break. start += count;

  1. 应用段落风格 1.创建段落风格文本 NSAttributedString applyParaStyle( CFStringRef fontName , CGFloat pointSize, NSString plainText, CGFloat lineSpaceInc){

    // Create the font so we can determine its height. CTFontRef font = CTFontCreateWithName(fontName, pointSize, NULL);

    // Set the lineSpacing. CGFloat lineSpacing = (CTFontGetLeading(font) + lineSpaceInc) * 2;

    // Create the paragraph style settings. CTParagraphStyleSetting setting;

    setting.spec = kCTParagraphStyleSpecifierLineSpacing; setting.valueSize = sizeof(CGFloat); setting.value = &lineSpacing;

    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(&setting, 1);

    // Add the paragraph style to the dictionary. NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys: (bridge id)font, (id)kCTFontNameAttribute, (bridge id)paragraphStyle, (id)kCTParagraphStyleAttributeName, nil]; CFRelease(font); CFRelease(paragraphStyle);

    // Apply the paragraph style to the string to created the attributed string. NSAttributedString attrString = [[NSAttributedString alloc] initWithString:(NSString)plainText attributes:attributes];

    return attrString; }

    1. 把对应文本绘制到context上
      • (void)drawRect:(CGRect)rect { // Initialize a graphics context in iOS. CGContextRef context = UIGraphicsGetCurrentContext();

    // Flip the context coordinates in iOS only. CGContextTranslateCTM(context, 0, self.bounds.size.height); CGContextScaleCTM(context, 1.0, -1.0);

    // Set the text matrix. CGContextSetTextMatrix(context, CGAffineTransformIdentity);

    CFStringRef fontName = CFSTR("Didot Italic"); CGFloat pointSize = 24.0;

    CFStringRef string = CFSTR("Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine.");

    // Apply the paragraph style. NSAttributedString* attrString = applyParaStyle(fontName, pointSize, string, 50.0);

    // Put the attributed string with applied paragraph style into a framesetter. CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);

    // Create a path to fill the View. CGPathRef path = CGPathCreateWithRect(rect, NULL);

    // Create a frame in which to draw. CTFrameRef frame = CTFramesetterCreateFrame( framesetter, CFRangeMake(0, 0), path, NULL);

    // Draw the frame. CTFrameDraw(frame, context); CFRelease(frame); CGPathRelease(path); CFRelease(framesetter); }

  2. 在非矩形区域内展示文本

    // Create a path in the shape of a donut. static void AddSquashedDonutPath(CGMutablePathRef path, const CGAffineTransform *m, CGRect rect) { CGFloat width = CGRectGetWidth(rect); CGFloat height = CGRectGetHeight(rect);

    CGFloat radiusH = width / 3.0; CGFloat radiusV = height / 3.0;

    CGPathMoveToPoint( path, m, rect.origin.x, rect.origin.y + height - radiusV); CGPathAddQuadCurveToPoint( path, m, rect.origin.x, rect.origin.y + height, rect.origin.x + radiusH, rect.origin.y + height); CGPathAddLineToPoint( path, m, rect.origin.x + width - radiusH, rect.origin.y + height); CGPathAddQuadCurveToPoint( path, m, rect.origin.x + width, rect.origin.y + height, rect.origin.x + width, rect.origin.y + height - radiusV); CGPathAddLineToPoint( path, m, rect.origin.x + width, rect.origin.y + radiusV); CGPathAddQuadCurveToPoint( path, m, rect.origin.x + width, rect.origin.y, rect.origin.x + width - radiusH, rect.origin.y); CGPathAddLineToPoint( path, m, rect.origin.x + radiusH, rect.origin.y); CGPathAddQuadCurveToPoint( path, m, rect.origin.x, rect.origin.y, rect.origin.x, rect.origin.y + radiusV); CGPathCloseSubpath( path);

    CGPathAddEllipseInRect( path, m, CGRectMake( rect.origin.x + width / 2.0 - width / 5.0, rect.origin.y + height / 2.0 - height / 5.0, width / 5.0 2.0, height / 5.0 2.0)); }

// Generate the path outside of the drawRect call so the path is calculated only once.

  • (NSArray *)paths { CGMutablePathRef path = CGPathCreateMutable(); CGRect bounds = self.bounds; bounds = CGRectInset(bounds, 10.0, 10.0); AddSquashedDonutPath(path, NULL, bounds);

    NSMutableArray *result = [NSMutableArray arrayWithObject:CFBridgingRelease(path)]; return result; }

  • (void)drawRect:(CGRect)rect { [super drawRect:rect];

    // Initialize a graphics context in iOS. CGContextRef context = UIGraphicsGetCurrentContext();

    // Flip the context coordinates in iOS only. CGContextTranslateCTM(context, 0, self.bounds.size.height); CGContextScaleCTM(context, 1.0, -1.0);

    // Set the text matrix. CGContextSetTextMatrix(context, CGAffineTransformIdentity);

    // Initialize an attributed string. CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine.");

    // Create a mutable attributed string. CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);

    // Copy the textString into the newly created attrString. CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), textString);

    // Create a color that will be added as an attribute to the attrString. CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); CGFloat components[] = { 1.0, 0.0, 0.0, 0.8 }; CGColorRef red = CGColorCreate(rgbColorSpace, components); CGColorSpaceRelease(rgbColorSpace);

    // Set the color of the first 13 chars to red. CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 13), kCTForegroundColorAttributeName, red);

    // Create the framesetter with the attributed string. CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);

    // Create the array of paths in which to draw the text. NSArray *paths = [self paths];

    CFIndex startIndex = 0;

    // For each path in the array of paths... for (id object in paths) { CGPathRef path = (__bridge CGPathRef)object;

    // Set the background of the path to yellow.
    CGContextSetFillColorWithColor(context, [YELLOW_COLOR CGColor]);
    
    CGContextAddPath(context, path);
    CGContextFillPath(context);
    
    CGContextDrawPath(context, kCGPathStroke);
    
    // Create a frame for this path and draw the text.
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
                                     CFRangeMake(startIndex, 0), path, NULL);
    CTFrameDraw(frame, context);
    
    // Start the next frame at the first character not visible in this frame.
    CFRange frameRange = CTFrameGetVisibleStringRange(frame);
    startIndex += frameRange.length;
    CFRelease(frame);

    }

CFRelease(attrString); CFRelease(framesetter); }

收藏
3
sina weixin mail 回到顶部