1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306 |
- //
- // 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
|