From 83b9d5c682b21d88133f24da0f94dd56bd79e687 Mon Sep 17 00:00:00 2001
From: 单军华
Date: Thu, 19 Jul 2018 13:38:55 +0800
Subject: [PATCH] change

---
 screendisplay/Pods/YYText/YYText/YYLabel.m | 1306 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1,306 insertions(+), 0 deletions(-)

diff --git a/screendisplay/Pods/YYText/YYText/YYLabel.m b/screendisplay/Pods/YYText/YYText/YYLabel.m
new file mode 100755
index 0000000..f5656e9
--- /dev/null
+++ b/screendisplay/Pods/YYText/YYText/YYLabel.m
@@ -0,0 +1,1306 @@
+//
+//  YYLabel.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/2/25.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYLabel.h"
+#import "YYTextAsyncLayer.h"
+#import "YYTextWeakProxy.h"
+#import "YYTextUtilities.h"
+#import "NSAttributedString+YYText.h"
+#import <libkern/OSAtomic.h>
+
+
+static dispatch_queue_t YYLabelGetReleaseQueue() {
+    return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
+}
+
+
+#define kLongPressMinimumDuration 0.5 // Time in seconds the fingers must be held down for long press gesture.
+#define kLongPressAllowableMovement 9.0 // Maximum movement in points allowed before the long press fails.
+#define kHighlightFadeDuration 0.15 // Time in seconds for highlight fadeout animation.
+#define kAsyncFadeDuration 0.08 // Time in seconds for async display fadeout animation.
+
+
+@interface YYLabel() <YYTextDebugTarget, YYTextAsyncLayerDelegate> {
+    NSMutableAttributedString *_innerText; ///< nonnull
+    YYTextLayout *_innerLayout;
+    YYTextContainer *_innerContainer; ///< nonnull
+    
+    NSMutableArray *_attachmentViews;
+    NSMutableArray *_attachmentLayers;
+    
+    NSRange _highlightRange; ///< current highlight range
+    YYTextHighlight *_highlight; ///< highlight attribute in `_highlightRange`
+    YYTextLayout *_highlightLayout; ///< when _state.showingHighlight=YES, this layout should be displayed
+    
+    YYTextLayout *_shrinkInnerLayout;
+    YYTextLayout *_shrinkHighlightLayout;
+    
+    NSTimer *_longPressTimer;
+    CGPoint _touchBeganPoint;
+    
+    struct {
+        unsigned int layoutNeedUpdate : 1;
+        unsigned int showingHighlight : 1;
+        
+        unsigned int trackingTouch : 1;
+        unsigned int swallowTouch : 1;
+        unsigned int touchMoved : 1;
+        
+        unsigned int hasTapAction : 1;
+        unsigned int hasLongPressAction : 1;
+        
+        unsigned int contentsNeedFade : 1;
+    } _state;
+}
+@end
+
+
+@implementation YYLabel
+
+#pragma mark - Private
+
+- (void)_updateIfNeeded {
+    if (_state.layoutNeedUpdate) {
+        _state.layoutNeedUpdate = NO;
+        [self _updateLayout];
+        [self.layer setNeedsDisplay];
+    }
+}
+
+- (void)_updateLayout {
+    _innerLayout = [YYTextLayout layoutWithContainer:_innerContainer text:_innerText];
+    _shrinkInnerLayout = [YYLabel _shrinkLayoutWithLayout:_innerLayout];
+}
+
+- (void)_setLayoutNeedUpdate {
+    _state.layoutNeedUpdate = YES;
+    [self _clearInnerLayout];
+    [self _setLayoutNeedRedraw];
+}
+
+- (void)_setLayoutNeedRedraw {
+    [self.layer setNeedsDisplay];
+}
+
+- (void)_clearInnerLayout {
+    if (!_innerLayout) return;
+    YYTextLayout *layout = _innerLayout;
+    _innerLayout = nil;
+    _shrinkInnerLayout = nil;
+    dispatch_async(YYLabelGetReleaseQueue(), ^{
+        NSAttributedString *text = [layout text]; // capture to block and release in background
+        if (layout.attachments.count) {
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [text length]; // capture to block and release in main thread (maybe there's UIView/CALayer attachments).
+            });
+        }
+    });
+}
+
+- (YYTextLayout *)_innerLayout {
+    return _shrinkInnerLayout ? _shrinkInnerLayout : _innerLayout;
+}
+
+- (YYTextLayout *)_highlightLayout {
+    return _shrinkHighlightLayout ? _shrinkHighlightLayout : _highlightLayout;
+}
+
++ (YYTextLayout *)_shrinkLayoutWithLayout:(YYTextLayout *)layout {
+    if (layout.text.length && layout.lines.count == 0) {
+        YYTextContainer *container = layout.container.copy;
+        container.maximumNumberOfRows = 1;
+        CGSize containerSize = container.size;
+        if (!container.verticalForm) {
+            containerSize.height = YYTextContainerMaxSize.height;
+        } else {
+            containerSize.width = YYTextContainerMaxSize.width;
+        }
+        container.size = containerSize;
+        return [YYTextLayout layoutWithContainer:container text:layout.text];
+    } else {
+        return nil;
+    }
+}
+
+- (void)_startLongPressTimer {
+    [_longPressTimer invalidate];
+    _longPressTimer = [NSTimer timerWithTimeInterval:kLongPressMinimumDuration
+                                              target:[YYTextWeakProxy proxyWithTarget:self]
+                                            selector:@selector(_trackDidLongPress)
+                                            userInfo:nil
+                                             repeats:NO];
+    [[NSRunLoop currentRunLoop] addTimer:_longPressTimer forMode:NSRunLoopCommonModes];
+}
+
+- (void)_endLongPressTimer {
+    [_longPressTimer invalidate];
+    _longPressTimer = nil;
+}
+
+- (void)_trackDidLongPress {
+    [self _endLongPressTimer];
+    if (_state.hasLongPressAction && _textLongPressAction) {
+        NSRange range = NSMakeRange(NSNotFound, 0);
+        CGRect rect = CGRectNull;
+        CGPoint point = [self _convertPointToLayout:_touchBeganPoint];
+        YYTextRange *textRange = [self._innerLayout textRangeAtPoint:point];
+        CGRect textRect = [self._innerLayout rectForRange:textRange];
+        textRect = [self _convertRectFromLayout:textRect];
+        if (textRange) {
+            range = textRange.asRange;
+            rect = textRect;
+        }
+        _textLongPressAction(self, _innerText, range, rect);
+    }
+    if (_highlight) {
+        YYTextAction longPressAction = _highlight.longPressAction ? _highlight.longPressAction : _highlightLongPressAction;
+        if (longPressAction) {
+            YYTextPosition *start = [YYTextPosition positionWithOffset:_highlightRange.location];
+            YYTextPosition *end = [YYTextPosition positionWithOffset:_highlightRange.location + _highlightRange.length affinity:YYTextAffinityBackward];
+            YYTextRange *range = [YYTextRange rangeWithStart:start end:end];
+            CGRect rect = [self._innerLayout rectForRange:range];
+            rect = [self _convertRectFromLayout:rect];
+            longPressAction(self, _innerText, _highlightRange, rect);
+            [self _removeHighlightAnimated:YES];
+            _state.trackingTouch = NO;
+        }
+    }
+}
+
+- (YYTextHighlight *)_getHighlightAtPoint:(CGPoint)point range:(NSRangePointer)range {
+    if (!self._innerLayout.containsHighlight) return nil;
+    point = [self _convertPointToLayout:point];
+    YYTextRange *textRange = [self._innerLayout textRangeAtPoint:point];
+    if (!textRange) return nil;
+    
+    NSUInteger startIndex = textRange.start.offset;
+    if (startIndex == _innerText.length) {
+        if (startIndex > 0) {
+            startIndex--;
+        }
+    }
+    NSRange highlightRange = {0};
+    YYTextHighlight *highlight = [_innerText attribute:YYTextHighlightAttributeName
+                                               atIndex:startIndex
+                                 longestEffectiveRange:&highlightRange
+                                               inRange:NSMakeRange(0, _innerText.length)];
+    
+    if (!highlight) return nil;
+    if (range) *range = highlightRange;
+    return highlight;
+}
+
+- (void)_showHighlightAnimated:(BOOL)animated {
+    if (!_highlight) return;
+    if (!_highlightLayout) {
+        NSMutableAttributedString *hiText = _innerText.mutableCopy;
+        NSDictionary *newAttrs = _highlight.attributes;
+        [newAttrs enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
+            [hiText yy_setAttribute:key value:value range:_highlightRange];
+        }];
+        _highlightLayout = [YYTextLayout layoutWithContainer:_innerContainer text:hiText];
+        _shrinkHighlightLayout = [YYLabel _shrinkLayoutWithLayout:_highlightLayout];
+        if (!_highlightLayout) _highlight = nil;
+    }
+    
+    if (_highlightLayout && !_state.showingHighlight) {
+        _state.showingHighlight = YES;
+        _state.contentsNeedFade = animated;
+        [self _setLayoutNeedRedraw];
+    }
+}
+
+- (void)_hideHighlightAnimated:(BOOL)animated {
+    if (_state.showingHighlight) {
+        _state.showingHighlight = NO;
+        _state.contentsNeedFade = animated;
+        [self _setLayoutNeedRedraw];
+    }
+}
+
+- (void)_removeHighlightAnimated:(BOOL)animated {
+    [self _hideHighlightAnimated:animated];
+    _highlight = nil;
+    _highlightLayout = nil;
+    _shrinkHighlightLayout = nil;
+}
+
+- (void)_endTouch {
+    [self _endLongPressTimer];
+    [self _removeHighlightAnimated:YES];
+    _state.trackingTouch = NO;
+}
+
+- (CGPoint)_convertPointToLayout:(CGPoint)point {
+    CGSize boundingSize = self._innerLayout.textBoundingSize;
+    if (self._innerLayout.container.isVerticalForm) {
+        CGFloat w = self._innerLayout.textBoundingSize.width;
+        if (w < self.bounds.size.width) w = self.bounds.size.width;
+        point.x += self._innerLayout.container.size.width - w;
+        if (_textVerticalAlignment == YYTextVerticalAlignmentCenter) {
+            point.x += (self.bounds.size.width - boundingSize.width) * 0.5;
+        } else if (_textVerticalAlignment == YYTextVerticalAlignmentBottom) {
+            point.x += (self.bounds.size.width - boundingSize.width);
+        }
+        return point;
+    } else {
+        if (_textVerticalAlignment == YYTextVerticalAlignmentCenter) {
+            point.y -= (self.bounds.size.height - boundingSize.height) * 0.5;
+        } else if (_textVerticalAlignment == YYTextVerticalAlignmentBottom) {
+            point.y -= (self.bounds.size.height - boundingSize.height);
+        }
+        return point;
+    }
+}
+
+- (CGPoint)_convertPointFromLayout:(CGPoint)point {
+    CGSize boundingSize = self._innerLayout.textBoundingSize;
+    if (self._innerLayout.container.isVerticalForm) {
+        CGFloat w = self._innerLayout.textBoundingSize.width;
+        if (w < self.bounds.size.width) w = self.bounds.size.width;
+        point.x -= self._innerLayout.container.size.width - w;
+        if (boundingSize.width < self.bounds.size.width) {
+            if (_textVerticalAlignment == YYTextVerticalAlignmentCenter) {
+                point.x -= (self.bounds.size.width - boundingSize.width) * 0.5;
+            } else if (_textVerticalAlignment == YYTextVerticalAlignmentBottom) {
+                point.x -= (self.bounds.size.width - boundingSize.width);
+            }
+        }
+        return point;
+    } else {
+        if (boundingSize.height < self.bounds.size.height) {
+            if (_textVerticalAlignment == YYTextVerticalAlignmentCenter) {
+                point.y += (self.bounds.size.height - boundingSize.height) * 0.5;
+            } else if (_textVerticalAlignment == YYTextVerticalAlignmentBottom) {
+                point.y += (self.bounds.size.height - boundingSize.height);
+            }
+        }
+        return point;
+    }
+}
+
+- (CGRect)_convertRectToLayout:(CGRect)rect {
+    rect.origin = [self _convertPointToLayout:rect.origin];
+    return rect;
+}
+
+- (CGRect)_convertRectFromLayout:(CGRect)rect {
+    rect.origin = [self _convertPointFromLayout:rect.origin];
+    return rect;
+}
+
+- (UIFont *)_defaultFont {
+    return [UIFont systemFontOfSize:17];
+}
+
+- (NSShadow *)_shadowFromProperties {
+    if (!_shadowColor || _shadowBlurRadius < 0) return nil;
+    NSShadow *shadow = [NSShadow new];
+    shadow.shadowColor = _shadowColor;
+#if !TARGET_INTERFACE_BUILDER
+    shadow.shadowOffset = _shadowOffset;
+#else
+    shadow.shadowOffset = CGSizeMake(_shadowOffset.x, _shadowOffset.y);
+#endif
+    shadow.shadowBlurRadius = _shadowBlurRadius;
+    return shadow;
+}
+
+- (void)_updateOuterLineBreakMode {
+    if (_innerContainer.truncationType) {
+        switch (_innerContainer.truncationType) {
+            case YYTextTruncationTypeStart: {
+                _lineBreakMode = NSLineBreakByTruncatingHead;
+            } break;
+            case YYTextTruncationTypeEnd: {
+                _lineBreakMode = NSLineBreakByTruncatingTail;
+            } break;
+            case YYTextTruncationTypeMiddle: {
+                _lineBreakMode = NSLineBreakByTruncatingMiddle;
+            } break;
+            default:break;
+        }
+    } else {
+        _lineBreakMode = _innerText.yy_lineBreakMode;
+    }
+}
+
+- (void)_updateOuterTextProperties {
+    _text = [_innerText yy_plainTextForRange:NSMakeRange(0, _innerText.length)];
+    _font = _innerText.yy_font;
+    if (!_font) _font = [self _defaultFont];
+    _textColor = _innerText.yy_color;
+    if (!_textColor) _textColor = [UIColor blackColor];
+    _textAlignment = _innerText.yy_alignment;
+    _lineBreakMode = _innerText.yy_lineBreakMode;
+    NSShadow *shadow = _innerText.yy_shadow;
+    _shadowColor = shadow.shadowColor;
+#if !TARGET_INTERFACE_BUILDER
+    _shadowOffset = shadow.shadowOffset;
+#else
+    _shadowOffset = CGPointMake(shadow.shadowOffset.width, shadow.shadowOffset.height);
+#endif
+    
+    _shadowBlurRadius = shadow.shadowBlurRadius;
+    _attributedText = _innerText;
+    [self _updateOuterLineBreakMode];
+}
+
+- (void)_updateOuterContainerProperties {
+    _truncationToken = _innerContainer.truncationToken;
+    _numberOfLines = _innerContainer.maximumNumberOfRows;
+    _textContainerPath = _innerContainer.path;
+    _exclusionPaths = _innerContainer.exclusionPaths;
+    _textContainerInset = _innerContainer.insets;
+    _verticalForm = _innerContainer.verticalForm;
+    _linePositionModifier = _innerContainer.linePositionModifier;
+    [self _updateOuterLineBreakMode];
+}
+
+- (void)_clearContents {
+    CGImageRef image = (__bridge_retained CGImageRef)(self.layer.contents);
+    self.layer.contents = nil;
+    if (image) {
+        dispatch_async(YYLabelGetReleaseQueue(), ^{
+            CFRelease(image);
+        });
+    }
+}
+
+- (void)_initLabel {
+    ((YYTextAsyncLayer *)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;
+    _textAlignment = NSTextAlignmentNatural;
+    _lineBreakMode = NSLineBreakByTruncatingTail;
+    _innerText = [NSMutableAttributedString new];
+    _innerContainer = [YYTextContainer new];
+    _innerContainer.truncationType = YYTextTruncationTypeEnd;
+    _innerContainer.maximumNumberOfRows = _numberOfLines;
+    _clearContentsBeforeAsynchronouslyDisplay = YES;
+    _fadeOnAsynchronouslyDisplay = YES;
+    _fadeOnHighlight = YES;
+    
+    self.isAccessibilityElement = YES;
+}
+
+#pragma mark - Override
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:CGRectZero];
+    if (!self) return nil;
+    self.backgroundColor = [UIColor clearColor];
+    self.opaque = NO;
+    [self _initLabel];
+    self.frame = frame;
+    return self;
+}
+
+- (void)dealloc {
+    [YYTextDebugOption removeDebugTarget:self];
+    [_longPressTimer invalidate];
+}
+
++ (Class)layerClass {
+    return [YYTextAsyncLayer class];
+}
+
+- (void)setFrame:(CGRect)frame {
+    CGSize oldSize = self.bounds.size;
+    [super setFrame:frame];
+    CGSize newSize = self.bounds.size;
+    if (!CGSizeEqualToSize(oldSize, newSize)) {
+        _innerContainer.size = self.bounds.size;
+        if (!_ignoreCommonProperties) {
+            _state.layoutNeedUpdate = YES;
+        }
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedRedraw];
+    }
+}
+
+- (void)setBounds:(CGRect)bounds {
+    CGSize oldSize = self.bounds.size;
+    [super setBounds:bounds];
+    CGSize newSize = self.bounds.size;
+    if (!CGSizeEqualToSize(oldSize, newSize)) {
+        _innerContainer.size = self.bounds.size;
+        if (!_ignoreCommonProperties) {
+            _state.layoutNeedUpdate = YES;
+        }
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedRedraw];
+    }
+}
+
+- (CGSize)sizeThatFits:(CGSize)size {
+    if (_ignoreCommonProperties) {
+        return _innerLayout.textBoundingSize;
+    }
+    
+    if (!_verticalForm && size.width <= 0) size.width = YYTextContainerMaxSize.width;
+    if (_verticalForm && size.height <= 0) size.height = YYTextContainerMaxSize.height;
+    
+    if ((!_verticalForm && size.width == self.bounds.size.width) ||
+        (_verticalForm && size.height == self.bounds.size.height)) {
+        [self _updateIfNeeded];
+        YYTextLayout *layout = self._innerLayout;
+        BOOL contains = NO;
+        if (layout.container.maximumNumberOfRows == 0) {
+            if (layout.truncatedLine == nil) {
+                contains = YES;
+            }
+        } else {
+            if (layout.rowCount <= layout.container.maximumNumberOfRows) {
+                contains = YES;
+            }
+        }
+        if (contains) {
+            return layout.textBoundingSize;
+        }
+    }
+    
+    if (!_verticalForm) {
+        size.height = YYTextContainerMaxSize.height;
+    } else {
+        size.width = YYTextContainerMaxSize.width;
+    }
+    
+    YYTextContainer *container = [_innerContainer copy];
+    container.size = size;
+    
+    YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:_innerText];
+    return layout.textBoundingSize;
+}
+
+- (NSString *)accessibilityLabel {
+    return [_innerLayout.text yy_plainTextForRange:_innerLayout.text.yy_rangeOfAll];
+}
+
+#pragma mark - NSCoding
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    [super encodeWithCoder:aCoder];
+    [aCoder encodeObject:_attributedText forKey:@"attributedText"];
+    [aCoder encodeObject:_innerContainer forKey:@"innerContainer"];
+}
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+    self = [super initWithCoder:aDecoder];
+    [self _initLabel];
+    YYTextContainer *innerContainer = [aDecoder decodeObjectForKey:@"innerContainer"];
+    if (innerContainer) {
+        _innerContainer = innerContainer;
+    } else {
+        _innerContainer.size = self.bounds.size;
+    }
+    [self _updateOuterContainerProperties];
+    self.attributedText = [aDecoder decodeObjectForKey:@"attributedText"];
+    [self _setLayoutNeedUpdate];
+    return self;
+}
+
+#pragma mark - Touches
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+    [self _updateIfNeeded];
+    UITouch *touch = touches.anyObject;
+    CGPoint point = [touch locationInView:self];
+    
+    _highlight = [self _getHighlightAtPoint:point range:&_highlightRange];
+    _highlightLayout = nil;
+    _shrinkHighlightLayout = nil;
+    _state.hasTapAction = _textTapAction != nil;
+    _state.hasLongPressAction = _textLongPressAction != nil;
+    
+    if (_highlight || _textTapAction || _textLongPressAction) {
+        _touchBeganPoint = point;
+        _state.trackingTouch = YES;
+        _state.swallowTouch = YES;
+        _state.touchMoved = NO;
+        [self _startLongPressTimer];
+        if (_highlight) [self _showHighlightAnimated:NO];
+    } else {
+        _state.trackingTouch = NO;
+        _state.swallowTouch = NO;
+        _state.touchMoved = NO;
+    }
+    if (!_state.swallowTouch) {
+        [super touchesBegan:touches withEvent:event];
+    }
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+    [self _updateIfNeeded];
+    
+    UITouch *touch = touches.anyObject;
+    CGPoint point = [touch locationInView:self];
+    
+    if (_state.trackingTouch) {
+        if (!_state.touchMoved) {
+            CGFloat moveH = point.x - _touchBeganPoint.x;
+            CGFloat moveV = point.y - _touchBeganPoint.y;
+            if (fabs(moveH) > fabs(moveV)) {
+                if (fabs(moveH) > kLongPressAllowableMovement) _state.touchMoved = YES;
+            } else {
+                if (fabs(moveV) > kLongPressAllowableMovement) _state.touchMoved = YES;
+            }
+            if (_state.touchMoved) {
+                [self _endLongPressTimer];
+            }
+        }
+        if (_state.touchMoved && _highlight) {
+            YYTextHighlight *highlight = [self _getHighlightAtPoint:point range:NULL];
+            if (highlight == _highlight) {
+                [self _showHighlightAnimated:_fadeOnHighlight];
+            } else {
+                [self _hideHighlightAnimated:_fadeOnHighlight];
+            }
+        }
+    }
+    
+    if (!_state.swallowTouch) {
+        [super touchesMoved:touches withEvent:event];
+    }
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+    UITouch *touch = touches.anyObject;
+    CGPoint point = [touch locationInView:self];
+    
+    if (_state.trackingTouch) {
+        [self _endLongPressTimer];
+        if (!_state.touchMoved && _textTapAction) {
+            NSRange range = NSMakeRange(NSNotFound, 0);
+            CGRect rect = CGRectNull;
+            CGPoint point = [self _convertPointToLayout:_touchBeganPoint];
+            YYTextRange *textRange = [self._innerLayout textRangeAtPoint:point];
+            CGRect textRect = [self._innerLayout rectForRange:textRange];
+            textRect = [self _convertRectFromLayout:textRect];
+            if (textRange) {
+                range = textRange.asRange;
+                rect = textRect;
+            }
+            _textTapAction(self, _innerText, range, rect);
+        }
+        
+        if (_highlight) {
+            if (!_state.touchMoved || [self _getHighlightAtPoint:point range:NULL] == _highlight) {
+                YYTextAction tapAction = _highlight.tapAction ? _highlight.tapAction : _highlightTapAction;
+                if (tapAction) {
+                    YYTextPosition *start = [YYTextPosition positionWithOffset:_highlightRange.location];
+                    YYTextPosition *end = [YYTextPosition positionWithOffset:_highlightRange.location + _highlightRange.length affinity:YYTextAffinityBackward];
+                    YYTextRange *range = [YYTextRange rangeWithStart:start end:end];
+                    CGRect rect = [self._innerLayout rectForRange:range];
+                    rect = [self _convertRectFromLayout:rect];
+                    tapAction(self, _innerText, _highlightRange, rect);
+                }
+            }
+            [self _removeHighlightAnimated:_fadeOnHighlight];
+        }
+    }
+    
+    if (!_state.swallowTouch) {
+        [super touchesEnded:touches withEvent:event];
+    }
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
+    [self _endTouch];
+    if (!_state.swallowTouch) [super touchesCancelled:touches withEvent:event];
+}
+
+#pragma mark - Properties
+
+- (void)setText:(NSString *)text {
+    if (_text == text || [_text isEqualToString:text]) return;
+    _text = text.copy;
+    BOOL needAddAttributes = _innerText.length == 0 && text.length > 0;
+    [_innerText replaceCharactersInRange:NSMakeRange(0, _innerText.length) withString:text ? text : @""];
+    [_innerText yy_removeDiscontinuousAttributesInRange:NSMakeRange(0, _innerText.length)];
+    if (needAddAttributes) {
+        _innerText.yy_font = _font;
+        _innerText.yy_color = _textColor;
+        _innerText.yy_shadow = [self _shadowFromProperties];
+        _innerText.yy_alignment = _textAlignment;
+        switch (_lineBreakMode) {
+            case NSLineBreakByWordWrapping:
+            case NSLineBreakByCharWrapping:
+            case NSLineBreakByClipping: {
+                _innerText.yy_lineBreakMode = _lineBreakMode;
+            } break;
+            case NSLineBreakByTruncatingHead:
+            case NSLineBreakByTruncatingTail:
+            case NSLineBreakByTruncatingMiddle: {
+                _innerText.yy_lineBreakMode = NSLineBreakByWordWrapping;
+            } break;
+            default: break;
+        }
+    }
+    if ([_textParser parseText:_innerText selectedRange:NULL]) {
+        [self _updateOuterTextProperties];
+    }
+    if (!_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setFont:(UIFont *)font {
+    if (!font) {
+        font = [self _defaultFont];
+    }
+    if (_font == font || [_font isEqual:font]) return;
+    _font = font;
+    _innerText.yy_font = _font;
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setTextColor:(UIColor *)textColor {
+    if (!textColor) {
+        textColor = [UIColor blackColor];
+    }
+    if (_textColor == textColor || [_textColor isEqual:textColor]) return;
+    _textColor = textColor;
+    _innerText.yy_color = textColor;
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+    }
+}
+
+- (void)setShadowColor:(UIColor *)shadowColor {
+    if (_shadowColor == shadowColor || [_shadowColor isEqual:shadowColor]) return;
+    _shadowColor = shadowColor;
+    _innerText.yy_shadow = [self _shadowFromProperties];
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+    }
+}
+
+#if !TARGET_INTERFACE_BUILDER
+- (void)setShadowOffset:(CGSize)shadowOffset {
+    if (CGSizeEqualToSize(_shadowOffset, shadowOffset)) return;
+    _shadowOffset = shadowOffset;
+    _innerText.yy_shadow = [self _shadowFromProperties];
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+    }
+}
+#else
+- (void)setShadowOffset:(CGPoint)shadowOffset {
+    if (CGPointEqualToPoint(_shadowOffset, shadowOffset)) return;
+    _shadowOffset = shadowOffset;
+    _innerText.yy_shadow = [self _shadowFromProperties];
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+    }
+}
+#endif
+
+- (void)setShadowBlurRadius:(CGFloat)shadowBlurRadius {
+    if (_shadowBlurRadius == shadowBlurRadius) return;
+    _shadowBlurRadius = shadowBlurRadius;
+    _innerText.yy_shadow = [self _shadowFromProperties];
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+    }
+}
+
+- (void)setTextAlignment:(NSTextAlignment)textAlignment {
+    if (_textAlignment == textAlignment) return;
+    _textAlignment = textAlignment;
+    _innerText.yy_alignment = textAlignment;
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode {
+    if (_lineBreakMode == lineBreakMode) return;
+    _lineBreakMode = lineBreakMode;
+    _innerText.yy_lineBreakMode = lineBreakMode;
+    // allow multi-line break
+    switch (lineBreakMode) {
+        case NSLineBreakByWordWrapping:
+        case NSLineBreakByCharWrapping:
+        case NSLineBreakByClipping: {
+            _innerContainer.truncationType = YYTextTruncationTypeNone;
+            _innerText.yy_lineBreakMode = lineBreakMode;
+        } break;
+        case NSLineBreakByTruncatingHead:{
+            _innerContainer.truncationType = YYTextTruncationTypeStart;
+            _innerText.yy_lineBreakMode = NSLineBreakByWordWrapping;
+        } break;
+        case NSLineBreakByTruncatingTail:{
+            _innerContainer.truncationType = YYTextTruncationTypeEnd;
+            _innerText.yy_lineBreakMode = NSLineBreakByWordWrapping;
+        } break;
+        case NSLineBreakByTruncatingMiddle: {
+            _innerContainer.truncationType = YYTextTruncationTypeMiddle;
+            _innerText.yy_lineBreakMode = NSLineBreakByWordWrapping;
+        } break;
+        default: break;
+    }
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setTextVerticalAlignment:(YYTextVerticalAlignment)textVerticalAlignment {
+    if (_textVerticalAlignment == textVerticalAlignment) return;
+    _textVerticalAlignment = textVerticalAlignment;
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setTruncationToken:(NSAttributedString *)truncationToken {
+    if (_truncationToken == truncationToken || [_truncationToken isEqual:truncationToken]) return;
+    _truncationToken = truncationToken.copy;
+    _innerContainer.truncationToken = truncationToken;
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setNumberOfLines:(NSUInteger)numberOfLines {
+    if (_numberOfLines == numberOfLines) return;
+    _numberOfLines = numberOfLines;
+    _innerContainer.maximumNumberOfRows = numberOfLines;
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setAttributedText:(NSAttributedString *)attributedText {
+    if (attributedText.length > 0) {
+        _innerText = attributedText.mutableCopy;
+        switch (_lineBreakMode) {
+            case NSLineBreakByWordWrapping:
+            case NSLineBreakByCharWrapping:
+            case NSLineBreakByClipping: {
+                _innerText.yy_lineBreakMode = _lineBreakMode;
+            } break;
+            case NSLineBreakByTruncatingHead:
+            case NSLineBreakByTruncatingTail:
+            case NSLineBreakByTruncatingMiddle: {
+                _innerText.yy_lineBreakMode = NSLineBreakByWordWrapping;
+            } break;
+            default: break;
+        }
+    } else {
+        _innerText = [NSMutableAttributedString new];
+    }
+    [_textParser parseText:_innerText selectedRange:NULL];
+    if (!_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _updateOuterTextProperties];
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setTextContainerPath:(UIBezierPath *)textContainerPath {
+    if (_textContainerPath == textContainerPath || [_textContainerPath isEqual:textContainerPath]) return;
+    _textContainerPath = textContainerPath.copy;
+    _innerContainer.path = textContainerPath;
+    if (!_textContainerPath) {
+        _innerContainer.size = self.bounds.size;
+        _innerContainer.insets = _textContainerInset;
+    }
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setExclusionPaths:(NSArray *)exclusionPaths {
+    if (_exclusionPaths == exclusionPaths || [_exclusionPaths isEqual:exclusionPaths]) return;
+    _exclusionPaths = exclusionPaths.copy;
+    _innerContainer.exclusionPaths = exclusionPaths;
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset {
+    if (UIEdgeInsetsEqualToEdgeInsets(_textContainerInset, textContainerInset)) return;
+    _textContainerInset = textContainerInset;
+    _innerContainer.insets = textContainerInset;
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setVerticalForm:(BOOL)verticalForm {
+    if (_verticalForm == verticalForm) return;
+    _verticalForm = verticalForm;
+    _innerContainer.verticalForm = verticalForm;
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setLinePositionModifier:(id<YYTextLinePositionModifier>)linePositionModifier {
+    if (_linePositionModifier == linePositionModifier) return;
+    _linePositionModifier = linePositionModifier;
+    _innerContainer.linePositionModifier = linePositionModifier;
+    if (_innerText.length && !_ignoreCommonProperties) {
+        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+            [self _clearContents];
+        }
+        [self _setLayoutNeedUpdate];
+        [self _endTouch];
+        [self invalidateIntrinsicContentSize];
+    }
+}
+
+- (void)setTextParser:(id<YYTextParser>)textParser {
+    if (_textParser == textParser || [_textParser isEqual:textParser]) return;
+    _textParser = textParser;
+    if ([_textParser parseText:_innerText selectedRange:NULL]) {
+        [self _updateOuterTextProperties];
+        if (!_ignoreCommonProperties) {
+            if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+                [self _clearContents];
+            }
+            [self _setLayoutNeedUpdate];
+            [self _endTouch];
+            [self invalidateIntrinsicContentSize];
+        }
+    }
+}
+
+- (void)setTextLayout:(YYTextLayout *)textLayout {
+    _innerLayout = textLayout;
+    _shrinkInnerLayout = nil;
+    
+    if (_ignoreCommonProperties) {
+        _innerText = (NSMutableAttributedString *)textLayout.text;
+        _innerContainer = textLayout.container.copy;
+    } else {
+        _innerText = textLayout.text.mutableCopy;
+        if (!_innerText) {
+            _innerText = [NSMutableAttributedString new];
+        }
+        [self _updateOuterTextProperties];
+        
+        _innerContainer = textLayout.container.copy;
+        if (!_innerContainer) {
+            _innerContainer = [YYTextContainer new];
+            _innerContainer.size = self.bounds.size;
+            _innerContainer.insets = self.textContainerInset;
+        }
+        [self _updateOuterContainerProperties];
+    }
+    
+    if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
+        [self _clearContents];
+    }
+    _state.layoutNeedUpdate = NO;
+    [self _setLayoutNeedRedraw];
+    [self _endTouch];
+    [self invalidateIntrinsicContentSize];
+}
+
+- (YYTextLayout *)textLayout {
+    [self _updateIfNeeded];
+    return _innerLayout;
+}
+
+- (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously {
+    _displaysAsynchronously = displaysAsynchronously;
+    ((YYTextAsyncLayer *)self.layer).displaysAsynchronously = displaysAsynchronously;
+}
+
+#pragma mark - AutoLayout
+
+- (void)setPreferredMaxLayoutWidth:(CGFloat)preferredMaxLayoutWidth {
+    if (_preferredMaxLayoutWidth == preferredMaxLayoutWidth) return;
+    _preferredMaxLayoutWidth = preferredMaxLayoutWidth;
+    [self invalidateIntrinsicContentSize];
+}
+
+- (CGSize)intrinsicContentSize {
+    if (_preferredMaxLayoutWidth == 0) {
+        YYTextContainer *container = [_innerContainer copy];
+        container.size = YYTextContainerMaxSize;
+        
+        YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:_innerText];
+        return layout.textBoundingSize;
+    }
+    
+    CGSize containerSize = _innerContainer.size;
+    if (!_verticalForm) {
+        containerSize.height = YYTextContainerMaxSize.height;
+        containerSize.width = _preferredMaxLayoutWidth;
+        if (containerSize.width == 0) containerSize.width = self.bounds.size.width;
+    } else {
+        containerSize.width = YYTextContainerMaxSize.width;
+        containerSize.height = _preferredMaxLayoutWidth;
+        if (containerSize.height == 0) containerSize.height = self.bounds.size.height;
+    }
+    
+    YYTextContainer *container = [_innerContainer copy];
+    container.size = containerSize;
+    
+    YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:_innerText];
+    return layout.textBoundingSize;
+}
+
+#pragma mark - YYTextDebugTarget
+
+- (void)setDebugOption:(YYTextDebugOption *)debugOption {
+    BOOL needDraw = _debugOption.needDrawDebug;
+    _debugOption = debugOption.copy;
+    if (_debugOption.needDrawDebug != needDraw) {
+        [self _setLayoutNeedRedraw];
+    }
+}
+
+#pragma mark - YYTextAsyncLayerDelegate
+
+- (YYTextAsyncLayerDisplayTask *)newAsyncDisplayTask {
+    
+    // capture current context
+    BOOL contentsNeedFade = _state.contentsNeedFade;
+    NSAttributedString *text = _innerText;
+    YYTextContainer *container = _innerContainer;
+    YYTextVerticalAlignment verticalAlignment = _textVerticalAlignment;
+    YYTextDebugOption *debug = _debugOption;
+    NSMutableArray *attachmentViews = _attachmentViews;
+    NSMutableArray *attachmentLayers = _attachmentLayers;
+    BOOL layoutNeedUpdate = _state.layoutNeedUpdate;
+    BOOL fadeForAsync = _displaysAsynchronously && _fadeOnAsynchronouslyDisplay;
+    __block YYTextLayout *layout = (_state.showingHighlight && _highlightLayout) ? self._highlightLayout : self._innerLayout;
+    __block YYTextLayout *shrinkLayout = nil;
+    __block BOOL layoutUpdated = NO;
+    if (layoutNeedUpdate) {
+        text = text.copy;
+        container = container.copy;
+    }
+    
+    // create display task
+    YYTextAsyncLayerDisplayTask *task = [YYTextAsyncLayerDisplayTask new];
+    
+    task.willDisplay = ^(CALayer *layer) {
+        [layer removeAnimationForKey:@"contents"];
+        
+        // If the attachment is not in new layout, or we don't know the new layout currently,
+        // the attachment should be removed.
+        for (UIView *view in attachmentViews) {
+            if (layoutNeedUpdate || ![layout.attachmentContentsSet containsObject:view]) {
+                if (view.superview == self) {
+                    [view removeFromSuperview];
+                }
+            }
+        }
+        for (CALayer *layer in attachmentLayers) {
+            if (layoutNeedUpdate || ![layout.attachmentContentsSet containsObject:layer]) {
+                if (layer.superlayer == self.layer) {
+                    [layer removeFromSuperlayer];
+                }
+            }
+        }
+        [attachmentViews removeAllObjects];
+        [attachmentLayers removeAllObjects];
+    };
+
+    task.display = ^(CGContextRef context, CGSize size, BOOL (^isCancelled)(void)) {
+        if (isCancelled()) return;
+        if (text.length == 0) return;
+        
+        YYTextLayout *drawLayout = layout;
+        if (layoutNeedUpdate) {
+            layout = [YYTextLayout layoutWithContainer:container text:text];
+            shrinkLayout = [YYLabel _shrinkLayoutWithLayout:layout];
+            if (isCancelled()) return;
+            layoutUpdated = YES;
+            drawLayout = shrinkLayout ? shrinkLayout : layout;
+        }
+        
+        CGSize boundingSize = drawLayout.textBoundingSize;
+        CGPoint point = CGPointZero;
+        if (verticalAlignment == YYTextVerticalAlignmentCenter) {
+            if (drawLayout.container.isVerticalForm) {
+                point.x = -(size.width - boundingSize.width) * 0.5;
+            } else {
+                point.y = (size.height - boundingSize.height) * 0.5;
+            }
+        } else if (verticalAlignment == YYTextVerticalAlignmentBottom) {
+            if (drawLayout.container.isVerticalForm) {
+                point.x = -(size.width - boundingSize.width);
+            } else {
+                point.y = (size.height - boundingSize.height);
+            }
+        }
+        point = YYTextCGPointPixelRound(point);
+        [drawLayout drawInContext:context size:size point:point view:nil layer:nil debug:debug cancel:isCancelled];
+    };
+
+    task.didDisplay = ^(CALayer *layer, BOOL finished) {
+        YYTextLayout *drawLayout = layout;
+        if (layoutUpdated && shrinkLayout) {
+            drawLayout = shrinkLayout;
+        }
+        if (!finished) {
+            // If the display task is cancelled, we should clear the attachments.
+            for (YYTextAttachment *a in drawLayout.attachments) {
+                if ([a.content isKindOfClass:[UIView class]]) {
+                    if (((UIView *)a.content).superview == layer.delegate) {
+                        [((UIView *)a.content) removeFromSuperview];
+                    }
+                } else if ([a.content isKindOfClass:[CALayer class]]) {
+                    if (((CALayer *)a.content).superlayer == layer) {
+                        [((CALayer *)a.content) removeFromSuperlayer];
+                    }
+                }
+            }
+            return;
+        }
+        [layer removeAnimationForKey:@"contents"];
+        
+        __strong YYLabel *view = (YYLabel *)layer.delegate;
+        if (!view) return;
+        if (view->_state.layoutNeedUpdate && layoutUpdated) {
+            view->_innerLayout = layout;
+            view->_shrinkInnerLayout = shrinkLayout;
+            view->_state.layoutNeedUpdate = NO;
+        }
+        
+        CGSize size = layer.bounds.size;
+        CGSize boundingSize = drawLayout.textBoundingSize;
+        CGPoint point = CGPointZero;
+        if (verticalAlignment == YYTextVerticalAlignmentCenter) {
+            if (drawLayout.container.isVerticalForm) {
+                point.x = -(size.width - boundingSize.width) * 0.5;
+            } else {
+                point.y = (size.height - boundingSize.height) * 0.5;
+            }
+        } else if (verticalAlignment == YYTextVerticalAlignmentBottom) {
+            if (drawLayout.container.isVerticalForm) {
+                point.x = -(size.width - boundingSize.width);
+            } else {
+                point.y = (size.height - boundingSize.height);
+            }
+        }
+        point = YYTextCGPointPixelRound(point);
+        [drawLayout drawInContext:nil size:size point:point view:view layer:layer debug:nil cancel:NULL];
+        for (YYTextAttachment *a in drawLayout.attachments) {
+            if ([a.content isKindOfClass:[UIView class]]) [attachmentViews addObject:a.content];
+            else if ([a.content isKindOfClass:[CALayer class]]) [attachmentLayers addObject:a.content];
+        }
+        
+        if (contentsNeedFade) {
+            CATransition *transition = [CATransition animation];
+            transition.duration = kHighlightFadeDuration;
+            transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
+            transition.type = kCATransitionFade;
+            [layer addAnimation:transition forKey:@"contents"];
+        } else if (fadeForAsync) {
+            CATransition *transition = [CATransition animation];
+            transition.duration = kAsyncFadeDuration;
+            transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
+            transition.type = kCATransitionFade;
+            [layer addAnimation:transition forKey:@"contents"];
+        }
+    };
+    
+    return task;
+}
+
+@end
+
+
+
+@interface YYLabel(IBInspectableProperties)
+@end
+
+@implementation YYLabel (IBInspectableProperties)
+
+- (BOOL)fontIsBold_:(UIFont *)font {
+    if (![font respondsToSelector:@selector(fontDescriptor)]) return NO;
+    return (font.fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) > 0;
+}
+
+- (UIFont *)boldFont_:(UIFont *)font {
+    if (![font respondsToSelector:@selector(fontDescriptor)]) return font;
+    return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold] size:font.pointSize];
+}
+
+- (UIFont *)normalFont_:(UIFont *)font {
+    if (![font respondsToSelector:@selector(fontDescriptor)]) return font;
+    return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:0] size:font.pointSize];
+}
+
+- (void)setFontName_:(NSString *)fontName {
+    if (!fontName) return;
+    UIFont *font = self.font;
+    if ((fontName.length == 0 || [fontName.lowercaseString isEqualToString:@"system"]) && ![self fontIsBold_:font]) {
+        font = [UIFont systemFontOfSize:font.pointSize];
+    } else if ([fontName.lowercaseString isEqualToString:@"system bold"]) {
+        font = [UIFont boldSystemFontOfSize:font.pointSize];
+    } else {
+        if ([self fontIsBold_:font] && ([fontName.lowercaseString rangeOfString:@"bold"].location == NSNotFound)) {
+            font = [UIFont fontWithName:fontName size:font.pointSize];
+            font = [self boldFont_:font];
+        } else {
+            font = [UIFont fontWithName:fontName size:font.pointSize];
+        }
+    }
+    if (font) self.font = font;
+}
+
+- (void)setFontSize_:(CGFloat)fontSize {
+    if (fontSize <= 0) return;
+    UIFont *font = self.font;
+    font = [font fontWithSize:fontSize];
+    if (font) self.font = font;
+}
+
+- (void)setFontIsBold_:(BOOL)fontBold {
+    UIFont *font = self.font;
+    if ([self fontIsBold_:font] == fontBold) return;
+    if (fontBold) {
+        font = [self boldFont_:font];
+    } else {
+        font = [self normalFont_:font];
+    }
+    if (font) self.font = font;
+}
+
+- (void)setInsetTop_:(CGFloat)textInsetTop {
+    UIEdgeInsets insets = self.textContainerInset;
+    insets.top = textInsetTop;
+    self.textContainerInset = insets;
+}
+
+- (void)setInsetBottom_:(CGFloat)textInsetBottom {
+    UIEdgeInsets insets = self.textContainerInset;
+    insets.bottom = textInsetBottom;
+    self.textContainerInset = insets;
+}
+
+- (void)setInsetLeft_:(CGFloat)textInsetLeft {
+    UIEdgeInsets insets = self.textContainerInset;
+    insets.left = textInsetLeft;
+    self.textContainerInset = insets;
+    
+}
+
+- (void)setInsetRight_:(CGFloat)textInsetRight {
+    UIEdgeInsets insets = self.textContainerInset;
+    insets.right = textInsetRight;
+    self.textContainerInset = insets;
+}
+
+- (void)setDebugEnabled_:(BOOL)enabled {
+    if (!enabled) {
+        self.debugOption = nil;
+    } else {
+        YYTextDebugOption *debugOption = [YYTextDebugOption new];
+        debugOption.baselineColor = [UIColor redColor];
+        debugOption.CTFrameBorderColor = [UIColor redColor];
+        debugOption.CTLineFillColor = [UIColor colorWithRed:0.000 green:0.463 blue:1.000 alpha:0.180];
+        debugOption.CGGlyphBorderColor = [UIColor colorWithRed:1.000 green:0.524 blue:0.000 alpha:0.200];
+        self.debugOption = debugOption;
+    }
+}
+
+@end

--
Gitblit v1.8.0