YYText介紹
YYText 功能強(qiáng)大的 iOS 富文本編輯與顯示框架监憎,是 YYKit 的組件之一乘客。在此感謝作者 ibireme 開源如此優(yōu)秀的組件狡刘。本文將以一個(gè)例子分析YYLabel的具體實(shí)現(xiàn)俺祠。
YYLabel源碼分析
本人在閱讀的過程中畫了一個(gè)簡(jiǎn)單的類圖如下:(圖畫的不對(duì)的地方還請(qǐng)指出,對(duì)UML還是不太熟悉 /(ㄒoㄒ)/~~ )
說明如下:
**YYAsyncLayerDelegate **
這是一個(gè)協(xié)議逼纸,該協(xié)議只有一個(gè)必須實(shí)現(xiàn)的方法newAsyncDisplayTask洋措。當(dāng)layer的內(nèi)容需要刷新的時(shí)候該方法被調(diào)用,并返回一個(gè)刷新任務(wù)杰刽。
YYAsyncLayerDisplayTask
這是一個(gè)抽象類菠发,定義顯示任務(wù)王滤,沒有具體具體的實(shí)現(xiàn),該類具有三個(gè)Block類型的屬性滓鸠,分別用來執(zhí)行顯示任務(wù)之前雁乡,執(zhí)行時(shí),執(zhí)行后的操作糜俗。
YYAsyncLayer
該類繼承自CALayer蔗怠,并添加了一個(gè)屬性displaysAsynchronously,用來表示是否異步渲染界面吩跋,實(shí)現(xiàn)中,覆蓋了CALayer的setNeedsDisplay和display方法渔工。
YYSentinel
該類是一個(gè)線程安全的原子遞增計(jì)數(shù)器锌钮,多用于多線程的情況下。
YYLabel
該類繼承自UIVIew,實(shí)現(xiàn)了YYAsyncLayerDelegate引矩,并在代理的方法中創(chuàng)建了task梁丘。
OK,下面以作者源碼中的一個(gè)例子來分析源代碼
使用代碼:
NSMutableAttributedString *text = [NSMutableAttributedString new];
{
NSMutableAttributedString *one = [[NSMutableAttributedString alloc] initWithString:@"Shadow"];
one.font = [UIFont boldSystemFontOfSize:30];
one.color = [UIColor whiteColor];
//(1-a)
YYTextShadow *shadow = [YYTextShadow new];
shadow.color = [UIColor colorWithWhite:0.000 alpha:0.490];
shadow.offset = CGSizeMake(0, 1);
shadow.radius = 5;
one.textShadow = shadow; //設(shè)置陰影
[text appendAttributedString:one];
}
YYLabel *label = [YYLabel new];
//(1-b)
label.attributedText = text;
label.width = self.view.width;
label.height = self.view.height - (kiOS7Later ? 64 : 44);
label.top = (kiOS7Later ? 64 : 0);
label.textAlignment = NSTextAlignmentCenter;
label.textVerticalAlignment = YYTextVerticalAlignmentCenter;
label.numberOfLines = 0;
label.backgroundColor = [UIColor colorWithWhite:0.933 alpha:1.000];
[self.view addSubview:label];
首先我們看 (1-a) 這個(gè)創(chuàng)建label的時(shí)候調(diào)用了YYLabel的- (void)_initLabel方法:
- (void)_initLabel {
//(2-a)
((YYAsyncLayer *)self.layer).displaysAsynchronously = NO;
self.layer.contentsScale = [UIScreen mainScreen].scale;
self.contentMode = UIViewContentModeRedraw;
_attachmentViews = [NSMutableArray new];
_attachmentLayers = [NSMutableArray new];
_debugOption = [YYTextDebugOption sharedDebugOption];
[YYTextDebugOption addDebugTarget:self];
_font = [self _defaultFont];
_textColor = [UIColor blackColor];
_textVerticalAlignment = YYTextVerticalAlignmentCenter;
_numberOfLines = 1;
_lineBreakMode = NSLineBreakByTruncatingTail;
_innerText = [NSMutableAttributedString new];
_innerContainer = [YYTextContainer new];
_innerContainer.truncationType = YYTextTruncationTypeEnd;
_innerContainer.maximumNumberOfRows = _numberOfLines;
_clearContentsBeforeAsynchronouslyDisplay = YES;
_fadeOnAsynchronouslyDisplay = YES;
_fadeOnHighlight = YES;
self.isAccessibilityElement = YES;
}
這里我們看(2-a)旺韭,將layer強(qiáng)轉(zhuǎn)成了YYAsyncLayer氛谜,設(shè)置了displaysAsynchronously。
再看代碼片段1中的(1-b)調(diào)用了YYLabel的setAttributedText:(NSAttributedString *)attributedText
- (void)setAttributedText:(NSAttributedString *)attributedText {
if (attributedText.length > 0) {
_innerText = attributedText.mutableCopy;
switch (_lineBreakMode) {
case NSLineBreakByWordWrapping:
case NSLineBreakByCharWrapping:
case NSLineBreakByClipping: {
_innerText.lineBreakMode = _lineBreakMode;
} break;
case NSLineBreakByTruncatingHead:
case NSLineBreakByTruncatingTail:
case NSLineBreakByTruncatingMiddle: {
_innerText.lineBreakMode = NSLineBreakByWordWrapping;
} break;
default: break;
}
} else {
_innerText = [NSMutableAttributedString new];
}
[_textParser parseText:_innerText selectedRange:NULL];
if (!_ignoreCommonProperties) {
if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
[self _clearContents];
}
[self _updateOuterTextProperties];
//(3-a)
[self _setLayoutNeedUpdate];
[self _endTouch];
[self invalidateIntrinsicContentSize];
}
}
(3-a)調(diào)用了 _setLayoutNeedUpdate
- (void)_setLayoutNeedUpdate {
_state.layoutNeedUpdate = YES;
[self _clearInnerLayout];
[self _setLayoutNeedRedraw];
}
- (void)_setLayoutNeedRedraw {
//(4-a)刷新
[self.layer setNeedsDisplay];
}
在(4-a)中調(diào)用了layer的setNeedsDisplay区端,因?yàn)樵赺initLabel中值漫,已經(jīng)將layer轉(zhuǎn)成了YYAsyncLayer,這里就會(huì)調(diào)用到Y(jié)YAsyncLayer的setNeedsDisplay
- (void)setNeedsDisplay {
[self _cancelAsyncDisplay];
//(5-a)
[super setNeedsDisplay];
}
- (void)display {
super.contents = super.contents;
//調(diào)用刷新
[self _displayAsync:_displaysAsynchronously];
}
在(5-a)中织盼,調(diào)用了CALayer的setNeedsDisplay杨何,會(huì)自動(dòng)調(diào)用display方法,而YYAsyncLayer覆蓋了CALayer的display沥邻,所以走到了YYAsyncLayer的display危虱。然后調(diào)用了_displayAsync
- (void)_displayAsync:(BOOL)async {
//(6-a)
__strong id<YYAsyncLayerDelegate> delegate = self.delegate;
YYAsyncLayerDisplayTask *task = [delegate newAsyncDisplayTask];
if (!task.display) {
if (task.willDisplay) task.willDisplay(self);
self.contents = nil;
if (task.didDisplay) task.didDisplay(self, YES);
return;
}
//(6-b)
if (async) {
if (task.willDisplay) task.willDisplay(self);
YYSentinel *sentinel = _sentinel;
int32_t value = sentinel.value;
//?
BOOL (^isCancelled)() = ^BOOL() {
return value != sentinel.value;
};
CGSize size = self.bounds.size;
BOOL opaque = self.opaque;
CGFloat scale = self.contentsScale;
CGColorRef backgroundColor = (opaque && self.backgroundColor) ? CGColorRetain(self.backgroundColor) : NULL;
if (size.width < 1 || size.height < 1) {
CGImageRef image = (__bridge_retained CGImageRef)(self.contents);
self.contents = nil;
if (image) {
dispatch_async(YYAsyncLayerGetReleaseQueue(), ^{
CFRelease(image);
});
}
if (task.didDisplay) task.didDisplay(self, YES);
CGColorRelease(backgroundColor);
return;
}
//異步了
dispatch_async(YYAsyncLayerGetDisplayQueue(), ^{
if (isCancelled()) {
CGColorRelease(backgroundColor);
return;
}
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
if (opaque) {
CGContextSaveGState(context); {
if (!backgroundColor || CGColorGetAlpha(backgroundColor) < 1) {
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale));
CGContextFillPath(context);
}
if (backgroundColor) {
CGContextSetFillColorWithColor(context, backgroundColor);
CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale));
CGContextFillPath(context);
}
} CGContextRestoreGState(context);
CGColorRelease(backgroundColor);
}
// display是異步的
task.display(context, size, isCancelled);
if (isCancelled()) {
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
if (task.didDisplay) task.didDisplay(self, NO);
});
return;
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (isCancelled()) {
dispatch_async(dispatch_get_main_queue(), ^{
if (task.didDisplay) task.didDisplay(self, NO);
});
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
if (isCancelled()) {
if (task.didDisplay) task.didDisplay(self, NO);
} else {
self.contents = (__bridge id)(image.CGImage);
if (task.didDisplay) task.didDisplay(self, YES);
}
});
});
} else {
[_sentinel increase];
if (task.willDisplay) task.willDisplay(self);
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, self.contentsScale);
CGContextRef context = UIGraphicsGetCurrentContext();
if (self.opaque) {
CGSize size = self.bounds.size;
size.width *= self.contentsScale;
size.height *= self.contentsScale;
CGContextSaveGState(context); {
if (!self.backgroundColor || CGColorGetAlpha(self.backgroundColor) < 1) {
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
CGContextFillPath(context);
}
if (self.backgroundColor) {
CGContextSetFillColorWithColor(context, self.backgroundColor);
CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
CGContextFillPath(context);
}
} CGContextRestoreGState(context);
}
task.display(context, self.bounds.size, ^{return NO;});
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.contents = (__bridge id)(image.CGImage);
if (task.didDisplay) task.didDisplay(self, YES);
}
}
(6-a)中拿到了delegate,之后強(qiáng)轉(zhuǎn)為YYAsyncLayerDelegate唐全,YYLabel中實(shí)現(xiàn)了YYAsyncLayerDelegate和newAsyncDisplayTask埃跷,拿到task之后進(jìn)行顯示操作。這里大家可能有一個(gè)疑問邮利,YYLabel中并沒有見到對(duì)self.layer.delegate=self的操作懊直ⅰ?這里拿到的delegate怎么就掉到了YYLabel的實(shí)現(xiàn)了呢近弟?經(jīng)過一番查閱缅糟,發(fā)現(xiàn)在UIVIew的nitWithFrame:(CGRect)theFrame 方法中對(duì)代理進(jìn)行了賦值。參見:Chameleon UIKit源碼祷愉。
從上面代碼中可以看出:task.display是在異步隊(duì)列中執(zhí)行的窗宦。而willDisplay和didDisplay是在主線程中執(zhí)行的赦颇。
OK現(xiàn)在我們已經(jīng)將YYLabel的具體組織形式看清楚了,剩下的只是代碼的渲染和異步過程的控制赴涵。這部分需要大家細(xì)致的看每一行代碼媒怯,具體問題大家可以留言討論。
交流群
移動(dòng)開發(fā)交流群:264706196