YYKit源码探究(四十一) —— NSParagraphStyle分类(一)

版本记录

版本号 时间
V1.0 2018.03.30

前言

iOS圈内有几个人大家基本都知道,比如说王巍、唐巧,还有YYKit框架的作者现任职于滴滴的郭曜源 - ibireme等。这里有一篇唐巧对他的专访,还有他的 GitHub - Yaoyuan博客,这里贴出来框架YYKit 框架。接下来几篇我们就一起来看一下这个框架。感兴趣的可以看上面写的几篇。
1. YYKit源码探究(一) —— 基本概览
2. YYKit源码探究(二) —— NSString分类之Hash(一)
3. YYKit源码探究(三) —— NSString分类之Encode and decode(二)
4. YYKit源码探究(四) —— NSString分类之Drawing(三)
5. YYKit源码探究(五) —— NSString分类之Regular Expression(四)
6. YYKit源码探究(六) —— NSString分类之NSNumber Compatible(五)
7. YYKit源码探究(七) —— NSString分类之Utilities(六)
8. YYKit源码探究(八) —— NSNumber分类(一)
9. YYKit源码探究(九) —— UIFont分类之架构分析和Font Traits(一)
10. YYKit源码探究(十) —— UIFont分类之Create font(二)
11. YYKit源码探究(十一) —— UIFont分类之Load and unload font(三)
12. YYKit源码探究(十二) —— UIFont分类之Dump font data(四)
13. YYKit源码探究(十三) —— UIImage分类之框架结构和Create image部分(一)
14. YYKit源码探究(十四) —— UIImage分类之Image Info(二)
15. YYKit源码探究(十五) —— UIImage分类之Modify Image(三)
16. YYKit源码探究(十六) —— UIImage分类之Image Effect(四)
17. YYKit源码探究(十七) —— UIImageView分类之架构和image部分(一)
18. YYKit源码探究(十八) —— UIImageView分类之highlight image部分(二)
19. YYKit源码探究(十九) —— UIScreen分类(一)
20. YYKit源码探究(二十) —— UIScrollView分类(一)
21. YYKit源码探究(二十一) —— UITableView分类(一)
22. YYKit源码探究(二十二) —— UITextField分类(一)
23. YYKit源码探究(二十三) —— UIView分类(一)
24. YYKit源码探究(二十四) —— UIPasteboard分类(一)
25. YYKit源码探究(二十五) —— UIGestureRecognizer分类(一)
26. YYKit源码探究(二十六) —— UIDevice分类框架及Device Information(一)
27. YYKit源码探究(二十七) —— UIDevice分类之Network Information(二)
28. YYKit源码探究(二十八) —— UIDevice分类之Disk Space(三)
29. YYKit源码探究(二十九) —— UIDevice分类之Memory Information(四)
30. YYKit源码探究(三十) —— UIDevice分类之CPU Information(五)
31. YYKit源码探究(三十一) —— UIControl分类(一)
32. YYKit源码探究(三十二) —— UIColor分类之Create a UIColor Object(一)
33. YYKit源码探究(三十三) —— UIColor分类之Get color's description(二)
34. YYKit源码探究(三十四) —— UIColor分类之Retrieving Color Information(三)
35. YYKit源码探究(三十五) —— UIButton分类之image(一)
36. YYKit源码探究(三十六) —— UIButton分类之background image(二)
37. YYKit源码探究(三十七) —— UIBezierPath分类(一)
38. YYKit源码探究(三十八) —— UIBarButtonItem分类(一)
39. YYKit源码探究(三十九) —— UIApplication分类(一)
40. YYKit源码探究(四十) —— NSTimer分类(一)

回顾

上一篇主要介绍了NSTimer分类,这一篇主要看一下NSParagraphStyle分类。


API

下面我们一起来看一下API文档。

/**
 Provides extensions for `NSParagraphStyle` to work with CoreText.
 */
@interface NSParagraphStyle (YYText)

/**
 Creates a new NSParagraphStyle object from the CoreText Style.
 
 @param CTStyle CoreText Paragraph Style.
 
 @return a new NSParagraphStyle
 */
+ (nullable NSParagraphStyle *)styleWithCTStyle:(CTParagraphStyleRef)CTStyle;

/**
 Creates and returns a CoreText Paragraph Style. (need call CFRelease() after used)
 */
- (nullable CTParagraphStyleRef)CTStyle CF_RETURNS_RETAINED;

@end

下面我们一起来看一下这个API

1. + (nullable NSParagraphStyle *)styleWithCTStyle:(CTParagraphStyleRef)CTStyle;

该方法的作用就是根据CTParagraphStyleRef创建新的NSParagraphStyle对象。

方法实现

+ (NSParagraphStyle *)styleWithCTStyle:(CTParagraphStyleRef)CTStyle {
    if (CTStyle == NULL) return nil;
    
    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    CGFloat lineSpacing;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineSpacing, sizeof(CGFloat), &lineSpacing)) {
        style.lineSpacing = lineSpacing;
    }
#pragma clang diagnostic pop
    
    CGFloat paragraphSpacing;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat), &paragraphSpacing)) {
        style.paragraphSpacing = paragraphSpacing;
    }
    
    CTTextAlignment alignment;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment)) {
        style.alignment = NSTextAlignmentFromCTTextAlignment(alignment);
    }
    
    CGFloat firstLineHeadIndent;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &firstLineHeadIndent)) {
        style.firstLineHeadIndent = firstLineHeadIndent;
    }
    
    CGFloat headIndent;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(CGFloat), &headIndent)) {
        style.headIndent = headIndent;
    }
    
    CGFloat tailIndent;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(CGFloat), &tailIndent)) {
        style.tailIndent = tailIndent;
    }
    
    CTLineBreakMode lineBreakMode;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineBreakMode, sizeof(CTLineBreakMode), &lineBreakMode)) {
        style.lineBreakMode = (NSLineBreakMode)lineBreakMode;
    }
    
    CGFloat minimumLineHeight;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(CGFloat), &minimumLineHeight)) {
        style.minimumLineHeight = minimumLineHeight;
    }
    
    CGFloat maximumLineHeight;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(CGFloat), &maximumLineHeight)) {
        style.maximumLineHeight = maximumLineHeight;
    }
    
    CTWritingDirection baseWritingDirection;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(CTWritingDirection), &baseWritingDirection)) {
        style.baseWritingDirection = (NSWritingDirection)baseWritingDirection;
    }
    
    CGFloat lineHeightMultiple;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(CGFloat), &lineHeightMultiple)) {
        style.lineHeightMultiple = lineHeightMultiple;
    }
    
    CGFloat paragraphSpacingBefore;
    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(CGFloat), &paragraphSpacingBefore)) {
        style.paragraphSpacingBefore = paragraphSpacingBefore;
    }
    
    if ([style respondsToSelector:@selector(tabStops)]) {
        CFArrayRef tabStops;
        if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierTabStops, sizeof(CFArrayRef), &tabStops)) {
            if ([style respondsToSelector:@selector(setTabStops:)]) {
                NSMutableArray *tabs = [NSMutableArray new];
                [((__bridge NSArray *)(tabStops))enumerateObjectsUsingBlock : ^(id obj, NSUInteger idx, BOOL *stop) {
                    CTTextTabRef ctTab = (__bridge CFTypeRef)obj;
                    
                    NSTextTab *tab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentFromCTTextAlignment(CTTextTabGetAlignment(ctTab)) location:CTTextTabGetLocation(ctTab) options:(__bridge id)CTTextTabGetOptions(ctTab)];
                    [tabs addObject:tab];
                }];
                if (tabs.count) {
                    style.tabStops = tabs;
                }
            }
        }
        
        CGFloat defaultTabInterval;
        if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierDefaultTabInterval, sizeof(CGFloat), &defaultTabInterval)) {
            if ([style respondsToSelector:@selector(setDefaultTabInterval:)]) {
                style.defaultTabInterval = defaultTabInterval;
            }
        }
    }
    
    return style;
}

2. - (nullable CTParagraphStyleRef)CTStyle

该方法的作用创建并返回CoreText Paragraph Style

方法实现

- (CTParagraphStyleRef)CTStyle CF_RETURNS_RETAINED {
    CTParagraphStyleSetting set[kCTParagraphStyleSpecifierCount] = { 0 };
    int count = 0;
    
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    CGFloat lineSpacing = self.lineSpacing;
    set[count].spec = kCTParagraphStyleSpecifierLineSpacing;
    set[count].valueSize = sizeof(CGFloat);
    set[count].value = &lineSpacing;
    count++;
#pragma clang diagnostic pop
    
    CGFloat paragraphSpacing = self.paragraphSpacing;
    set[count].spec = kCTParagraphStyleSpecifierParagraphSpacing;
    set[count].valueSize = sizeof(CGFloat);
    set[count].value = &paragraphSpacing;
    count++;
    
    CTTextAlignment alignment = NSTextAlignmentToCTTextAlignment(self.alignment);
    set[count].spec = kCTParagraphStyleSpecifierAlignment;
    set[count].valueSize = sizeof(CTTextAlignment);
    set[count].value = &alignment;
    count++;
    
    CGFloat firstLineHeadIndent = self.firstLineHeadIndent;
    set[count].spec = kCTParagraphStyleSpecifierFirstLineHeadIndent;
    set[count].valueSize = sizeof(CGFloat);
    set[count].value = &firstLineHeadIndent;
    count++;
    
    CGFloat headIndent = self.headIndent;
    set[count].spec = kCTParagraphStyleSpecifierHeadIndent;
    set[count].valueSize = sizeof(CGFloat);
    set[count].value = &headIndent;
    count++;
    
    CGFloat tailIndent = self.tailIndent;
    set[count].spec = kCTParagraphStyleSpecifierTailIndent;
    set[count].valueSize = sizeof(CGFloat);
    set[count].value = &tailIndent;
    count++;
    
    CTLineBreakMode paraLineBreak = (CTLineBreakMode)self.lineBreakMode;
    set[count].spec = kCTParagraphStyleSpecifierLineBreakMode;
    set[count].valueSize = sizeof(CTLineBreakMode);
    set[count].value = &paraLineBreak;
    count++;
    
    CGFloat minimumLineHeight = self.minimumLineHeight;
    set[count].spec = kCTParagraphStyleSpecifierMinimumLineHeight;
    set[count].valueSize = sizeof(CGFloat);
    set[count].value = &minimumLineHeight;
    count++;
    
    CGFloat maximumLineHeight = self.maximumLineHeight;
    set[count].spec = kCTParagraphStyleSpecifierMaximumLineHeight;
    set[count].valueSize = sizeof(CGFloat);
    set[count].value = &maximumLineHeight;
    count++;
    
    CTWritingDirection paraWritingDirection = (CTWritingDirection)self.baseWritingDirection;
    set[count].spec = kCTParagraphStyleSpecifierBaseWritingDirection;
    set[count].valueSize = sizeof(CTWritingDirection);
    set[count].value = &paraWritingDirection;
    count++;
    
    CGFloat lineHeightMultiple = self.lineHeightMultiple;
    set[count].spec = kCTParagraphStyleSpecifierLineHeightMultiple;
    set[count].valueSize = sizeof(CGFloat);
    set[count].value = &lineHeightMultiple;
    count++;
    
    CGFloat paragraphSpacingBefore = self.paragraphSpacingBefore;
    set[count].spec = kCTParagraphStyleSpecifierParagraphSpacingBefore;
    set[count].valueSize = sizeof(CGFloat);
    set[count].value = &paragraphSpacingBefore;
    count++;
    
    if([self respondsToSelector:@selector(tabStops)]) {
        NSMutableArray *tabs = [NSMutableArray array];
        if ([self respondsToSelector:@selector(tabStops)]) {
            NSInteger numTabs = self.tabStops.count;
            if (numTabs) {
                [self.tabStops enumerateObjectsUsingBlock: ^(NSTextTab *tab, NSUInteger idx, BOOL *stop) {
                    CTTextTabRef ctTab = CTTextTabCreate(NSTextAlignmentToCTTextAlignment(tab.alignment), tab.location, (__bridge CFTypeRef)tab.options);
                    [tabs addObject:(__bridge id)ctTab];
                    CFRelease(ctTab);
                }];
                
                CFArrayRef tabStops = (__bridge CFArrayRef)(tabs);
                set[count].spec = kCTParagraphStyleSpecifierTabStops;
                set[count].valueSize = sizeof(CFArrayRef);
                set[count].value = &tabStops;
                count++;
            }
        }
        
        if ([self respondsToSelector:@selector(defaultTabInterval)]) {
            CGFloat defaultTabInterval = self.defaultTabInterval;
            set[count].spec = kCTParagraphStyleSpecifierDefaultTabInterval;
            set[count].valueSize = sizeof(CGFloat);
            set[count].value = &defaultTabInterval;
            count++;
        }
    }
    
    CTParagraphStyleRef style = CTParagraphStyleCreate(set, count);
    return style;
}

后记

本篇讲述了NSParagraphStyle的分类,感兴趣的可以点赞或者关注~~~

推荐阅读更多精彩内容