YYText源碼閱讀(一):YYLabel

YYText介紹

YYText 功能強(qiáng)大的 iOS 富文本編輯與顯示框架,是 YYKit 的組件之一。在此感謝作者 ibireme 開源如此優(yōu)秀的組件。本文將以一個(gè)例子分析YYLabel的具體實(shí)現(xiàn)。

YYLabel源碼分析

本人在閱讀的過程中畫了一個(gè)簡單的類圖如下:(圖畫的不對的地方還請指出,對UML還是不太熟悉 /(ㄒoㄒ)/~~ )

YYLabel類圖

說明如下:
**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è)例子來分析源代碼

Demo

使用代碼:

    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,這里就會調(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,會自動調(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中并沒有見到對self.layer.delegate=self的操作?。窟@里拿到的delegate怎么就掉到了YYLabel的實(shí)現(xiàn)了呢?經(jīng)過一番查閱,發(fā)現(xiàn)在UIVIew的nitWithFrame:(CGRect)theFrame 方法中對代理進(jìn)行了賦值。參見:Chameleon UIKit源碼。
從上面代碼中可以看出:task.display是在異步隊(duì)列中執(zhí)行的。而willDisplay和didDisplay是在主線程中執(zhí)行的。
OK現(xiàn)在我們已經(jīng)將YYLabel的具體組織形式看清楚了,剩下的只是代碼的渲染和異步過程的控制。這部分需要大家細(xì)致的看每一行代碼,具體問題大家可以留言討論。

交流群

移動開發(fā)交流群:264706196

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載:http://m.itdecent.cn/p/32fcadd12108 每個(gè)UIView有一個(gè)伙伴稱為l...
    F麥子閱讀 6,595評論 0 13
  • 每個(gè)UIView有一個(gè)伙伴稱為layer,一個(gè)CALayer。UIView實(shí)際上并沒有把自己畫到屏幕上;它繪制本身...
    shenzhenboy閱讀 3,265評論 0 17
  • YYText 簡單介紹 YYText 是YYKit中的一個(gè)富文本顯示,編輯組件,擁有YYLabel,YYText...
    喬克_叔叔閱讀 6,172評論 8 40
  • iOS的異步渲染 最近看了YYAsyncLayer在這里總結(jié)一下。YYAsyncLayer是整個(gè)YYKit異步渲染...
    uncle_charlie閱讀 3,244評論 0 5
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,656評論 30 472

友情鏈接更多精彩內(nèi)容