什么是異步繪制?
在子線程中繪制需要顯示的內(nèi)容、不占用主線程資源以防繪制的過程中阻塞主線程。
對UIView來說即繪制其layer.contents的內(nèi)容。
為什么要使用異步繪制?
如果在主線程中直接繪制有可能會阻塞主線程、導致fps降低(尤其是當在cell中使用時)、影響app的使用體驗。
如何進行異步繪制?
話不多說、直接上代碼:https://github.com/Avery-AN/TableView (QAAttributedLayer中使用了異步繪制)

QAAttributedLayer中異步繪制的主要實現(xiàn)方案
QAAttributedLabel為何而生?
主要是實現(xiàn)類似于微博app首頁cell中的富文本的效果、并支持單擊交互。


主要實現(xiàn)的功能
支持高亮文案點擊時的圓角背景
支持url高亮顯示、支持url點擊效果的高亮顯示、支持自定義url的字體
支持url自定義短鏈接文案
支持@xxx高亮顯示、支持@xxx點擊效果的高亮顯示、支持自定義@xxx的字體
支持#xxx#高亮顯示、支持#xxx#點擊效果的高亮顯示、支持自定義#xxx#的字體
支持自定義的Emoji顯示
支持固定文本的高亮顯示
支持搜索文本的高亮顯示
支持同步、異步繪制
使用CoreText繪制、可自定義各種富文本樣式(行間距、字間距、段間距等)
支持對超長文案的截斷處理 (ps:將超長文案的末尾處理成:"...全文"、并且支持自定義此截斷文案)
支持高亮文案的交互
使用時需要創(chuàng)建QARichTextLabel 或者 TrapezoidalLabel這兩個類的對象。
QARichTextLabel主要是實現(xiàn)微博首頁那種的富文本展示、而TrapezoidalLabel這個類主要是實現(xiàn)一些異形的label (見上面的效果圖2)。
具體的實現(xiàn)
QAAttributedLabel這個類是QARichTextLabel 或者 TrapezoidalLabel這兩個類的父類,這個類中定義了各種富文本的屬性,在使用時直接賦值即可。
@interface QAAttributedLabel : UIView
@property (nonatomic, copy, nullable) NSString *text;
@property (nonatomic, copy, nullable) NSMutableAttributedString *attributedString; // 若text也同時存在則優(yōu)先顯示attributedString
@property (nonatomic, strong, nullable, readonly) NSMutableAttributedString *srcAttributedString;
@property (nonatomic, copy, nullable) UIFont *font;
@property (nonatomic, copy, null_resettable) UIColor *textColor;
@property (nonatomic, assign) BOOL noRichTexts; // 不處理富文本(默認為NO)
@property (nonatomic, assign, readonly) NSInteger length; // 顯示文案的長度
@property (nonatomic, assign) NSTextAlignment textAlignment; // 文本的對齊方式
@property (nonatomic, assign) NSLineBreakMode lineBreakMode; // 換行模式
@property (nonatomic, assign) NSUInteger numberOfLines; // 需要顯示文本的行數(shù)
@property (nonatomic, assign) CGFloat paragraphSpace; // 段間距
@property (nonatomic, assign) CGFloat lineSpace; // 行間距
@property (nonatomic, assign) int wordSpace; // 字間距
@property (nonatomic, assign) BOOL display_async; // 是否異步繪制 (默認為NO)
@property (nonatomic, assign) BOOL linkHighlight; // 網(wǎng)頁鏈接是否需要高亮顯示 (默認為NO)
@property (nonatomic, assign) BOOL showShortLink; // 是否展示短鏈接 ("https://www.avery.com" -> "網(wǎng)頁短鏈接"; 默認為NO)
@property (nonatomic, assign) BOOL atHighlight; // "@xxx"是否需要高亮顯示 (默認為NO)
@property (nonatomic, assign) BOOL topicHighlight; // "#話題#"是否需要高亮顯示 (默認為NO)
@property (nonatomic, assign) BOOL showMoreText; // 當文本過多時、是否顯示seeMoreText的內(nèi)容 (默認為NO)
@property (nonatomic, assign, readonly) BOOL isTouching; // 是否正在被點擊 (touchesBegan時為YES、touches事件結束后為NO)
@property (nonatomic, copy, nullable) NSString *shortLink; // 展示短鏈接時顯示的文案 (PS:"網(wǎng)頁鏈接"、"網(wǎng)址"等)
@property (nonatomic, copy, nullable) NSArray *highLightTexts; // text文本中需要高亮顯示的部分
@property (nonatomic, copy, nullable) UIFont *highlightFont; // 高亮文案的字體
@property (nonatomic, copy, nullable) UIColor *highlightTextColor; // 高亮顯示時的顏色 (其它幾種高亮情況的默認顏色)
@property (nonatomic, copy, nullable) UIColor *highlightLinkTextColor; // 高亮顯示時的顏色 (link)
@property (nonatomic, copy, nullable) UIColor *highlightAtTextColor; // 高亮顯示時的顏色 (at)
@property (nonatomic, copy, nullable) UIColor *highlightTopicTextColor; // 高亮顯示時的顏色 (topic)
@property (nonatomic, copy, nullable) UIColor *highlightTapedTextColor; // 點擊高亮文案時的字體顏色 (其它幾種情況的默認顏色)
@property (nonatomic, copy, nullable) UIColor *highlightLinkTapedTextColor; // 點擊高亮文案時的字體顏色 (link)
@property (nonatomic, copy, nullable) UIColor *highlightAtTapedTextColor; // 點擊高亮文案時的字體顏色 (at)
@property (nonatomic, copy, nullable) UIColor *highlightTopicTapedTextColor; // 點擊高亮文案時的字體顏色 (topic)
@property (nonatomic, copy, nullable) UIColor *highlightTextBackgroundColor; // 高亮文案的背景顏色
@property (nonatomic, copy, nullable) UIColor *highlightTapedBackgroundColor; // 點擊高亮文案時的背景色
/**
顯示seeMoreText的前提條件:
(1) showMoreText = YES;
(2) 展示當前顯示文案所需的lines大于所設置的numberOfLines;
*/
@property (nonatomic, copy, nullable) NSString *seeMoreText; // PS:"...查看全文" 或者 "...全文"
@property (nonatomic, copy, nullable) UIFont *moreTextFont; // seeMoreText的字體
@property (nonatomic, copy, nullable) UIColor *moreTextColor; // seeMoreText的字體顏色
@property (nonatomic, copy, nullable) UIColor *moreTextBackgroundColor; // seeMoreText的背景顏色
@property (nonatomic, copy, nullable) UIColor *moreTapedBackgroundColor; // 點擊seeMoreText時的背景顏色
@property (nonatomic, copy, nullable) UIColor *moreTapedTextColor; // 點擊seeMoreText時的字體顏色
@property (nonatomic, strong, nullable) QATextLayout *textLayout;
@property (nonatomic, copy) void (^ _Nullable QAAttributedLabelTapAction)(NSString * _Nullable content, QAAttributedLabel_TapedStyle style);
@property (nonatomic, copy, nullable) GetTextContentSizeBlock getTextContentSizeBlock;
/**
label已渲染完畢后、若再次更改其屬性此值為被設為YES、并且后續(xù)還會調(diào)用layer的updateContent方法
*/
@property (nonatomic, assign) BOOL needUpdate;
/**
獲取文案所占用的size
*/
- (void)getTextContentSizeWithLayer:(id _Nonnull)layer
content:(id _Nonnull)content
maxWidth:(CGFloat)width
completionBlock:(GetTextContentSizeBlock _Nullable)block;
/**
設置layer的contents
@param image layer的contents需要顯示的圖片
@param attributedString 與image相對應的富文本字符串
PS: attributedString的作用主要是為了計算highlightRanges & highlightFonts & highlightFrameDic等、以備點擊高亮字體時使用
*/
- (void)setContentsImage:(UIImage * _Nonnull)image
attributedString:(NSMutableAttributedString * _Nonnull)attributedString;
/**
搜索文案
*/
- (void)searchTexts:(NSArray * _Nonnull)texts resetSearchResultInfo:(NSDictionary * _Nullable (^_Nullable)(void))searchResultInfo;
// 點擊事件是否擊中了高亮文本
- (BOOL)isHitHighlightTextWithPoint:(CGPoint)point
highlightRect:(CGRect)highlightRect
highlightRange:(NSString * _Nonnull)rangeString;
- (CGSize)getContentSize;
- (CGImageRef _Nullable )getLayerContents;
@end
QAAttributedLayer這個類主要是負責顯示內(nèi)容的同步/異步繪制。
- (void)fillContents_async:(QAAttributedLabel *)attributedLabel { // 異步繪制
dispatch_async(QAAttributedLayerDrawQueue(), ^{
self.contents = cgImage;
});
}
- (void)fillContents_sync:(QAAttributedLabel *)attributedLabel { // 同步繪制
}
繪制過程主要分為2步、第一步是將顯示的文案處理成NSMutableAttributedString、第二步是對NSMutableAttributedString進行繪制。
下面將以異步繪制來說下繪制的過程:
首先根據(jù)label設置的各種屬性值來獲取NSMutableAttributedString并給賦值上各種富文本屬性;
在獲取NSMutableAttributedString的過程中會保存需要高亮顯示的文案與位置 & 自定義的Emoji & 是否要對文案的結尾進行處理(處理成"...全文")等相關信息;
這里需要說下:保存相關高亮文案的信息以及位置的主要目的是用于label的交互。
繪制的過程是在QATextDraw類中進行處理的、主要包括NSMutableAttributedString以及自定義的emoji的繪制;
QABackgroundDraw主要是負責繪制圓角背景;
下面說一下代碼中幾個需要注意的點
(1) 為什么使用QAAttributedLayerDrawQueue()而不使用dispatch_get_global_queue()?
dispatch_async(QAAttributedLayerDrawQueue(), ^{
// 提交異步處理
});
因為系統(tǒng)本身和Project中的其他地方也會在dispatch_get_global_queue中請求新線程、一個隊列中可以開辟的線程數(shù)是有限的,所以當你在dispatch_get_global_queue中大量的開辟線程時有可能會導致堵塞、從而導致線程不能正常執(zhí)行。
(2) 為什么用CATransaction而不是切回到主線程?
dispatch_async(QAAttributedLayerDrawQueue(), ^{
self.contents = (__bridge id)(cgImage);
[CATransaction commit];
});
// 而沒有這樣做:
dispatch_async(QAAttributedLayerDrawQueue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{
self.contents = (__bridge id)(cgImage);
});
});
具體請看另一篇文章:iOS中的CATransaction是干什么的
【 請勿直接轉(zhuǎn)載 - 節(jié)約能源從你我做起 】