|
@@ -50,6 +50,9 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
YYTextHighlight *_highlight; ///< highlight attribute in `_highlightRange`
|
|
YYTextHighlight *_highlight; ///< highlight attribute in `_highlightRange`
|
|
YYTextLayout *_highlightLayout; ///< when _state.showingHighlight=YES, this layout should be displayed
|
|
YYTextLayout *_highlightLayout; ///< when _state.showingHighlight=YES, this layout should be displayed
|
|
|
|
|
|
|
|
+ YYTextLayout *_shrinkInnerLayout;
|
|
|
|
+ YYTextLayout *_shrinkHighlightLayout;
|
|
|
|
+
|
|
NSTimer *_longPressTimer;
|
|
NSTimer *_longPressTimer;
|
|
CGPoint _touchBeganPoint;
|
|
CGPoint _touchBeganPoint;
|
|
|
|
|
|
@@ -81,6 +84,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
|
|
|
|
- (void)_updateLayout {
|
|
- (void)_updateLayout {
|
|
_innerLayout = [YYTextLayout layoutWithContainer:_innerContainer text:_innerText];
|
|
_innerLayout = [YYTextLayout layoutWithContainer:_innerContainer text:_innerText];
|
|
|
|
+ _shrinkInnerLayout = [YYLabel _shrinkLayoutWithLayout:_innerLayout];
|
|
}
|
|
}
|
|
|
|
|
|
- (void)_setLayoutNeedUpdate {
|
|
- (void)_setLayoutNeedUpdate {
|
|
@@ -97,6 +101,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
if (!_innerLayout) return;
|
|
if (!_innerLayout) return;
|
|
YYTextLayout *layout = _innerLayout;
|
|
YYTextLayout *layout = _innerLayout;
|
|
_innerLayout = nil;
|
|
_innerLayout = nil;
|
|
|
|
+ _shrinkInnerLayout = nil;
|
|
dispatch_async(YYLabelGetReleaseQueue(), ^{
|
|
dispatch_async(YYLabelGetReleaseQueue(), ^{
|
|
NSAttributedString *text = [layout text]; // capture to block and release in background
|
|
NSAttributedString *text = [layout text]; // capture to block and release in background
|
|
if (layout.attachments.count) {
|
|
if (layout.attachments.count) {
|
|
@@ -107,6 +112,31 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+- (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 {
|
|
- (void)_startLongPressTimer {
|
|
[_longPressTimer invalidate];
|
|
[_longPressTimer invalidate];
|
|
_longPressTimer = [NSTimer timerWithTimeInterval:kLongPressMinimumDuration
|
|
_longPressTimer = [NSTimer timerWithTimeInterval:kLongPressMinimumDuration
|
|
@@ -130,7 +160,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
YYTextPosition *start = [YYTextPosition positionWithOffset:_highlightRange.location];
|
|
YYTextPosition *start = [YYTextPosition positionWithOffset:_highlightRange.location];
|
|
YYTextPosition *end = [YYTextPosition positionWithOffset:_highlightRange.location + _highlightRange.length affinity:YYTextAffinityBackward];
|
|
YYTextPosition *end = [YYTextPosition positionWithOffset:_highlightRange.location + _highlightRange.length affinity:YYTextAffinityBackward];
|
|
YYTextRange *range = [YYTextRange rangeWithStart:start end:end];
|
|
YYTextRange *range = [YYTextRange rangeWithStart:start end:end];
|
|
- CGRect rect = [_innerLayout rectForRange:range];
|
|
|
|
|
|
+ CGRect rect = [self._innerLayout rectForRange:range];
|
|
rect = [self _convertRectFromLayout:rect];
|
|
rect = [self _convertRectFromLayout:rect];
|
|
longPressAction(self, _innerText, _highlightRange, rect);
|
|
longPressAction(self, _innerText, _highlightRange, rect);
|
|
[self _removeHighlightAnimated:YES];
|
|
[self _removeHighlightAnimated:YES];
|
|
@@ -140,9 +170,9 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
|
|
|
|
- (YYTextHighlight *)_getHighlightAtPoint:(CGPoint)point range:(NSRangePointer)range {
|
|
- (YYTextHighlight *)_getHighlightAtPoint:(CGPoint)point range:(NSRangePointer)range {
|
|
- if (!_innerLayout.containsHighlight) return nil;
|
|
|
|
|
|
+ if (!self._innerLayout.containsHighlight) return nil;
|
|
point = [self _convertPointToLayout:point];
|
|
point = [self _convertPointToLayout:point];
|
|
- YYTextRange *textRange = [_innerLayout textRangeAtPoint:point];
|
|
|
|
|
|
+ YYTextRange *textRange = [self._innerLayout textRangeAtPoint:point];
|
|
if (!textRange) return nil;
|
|
if (!textRange) return nil;
|
|
|
|
|
|
NSUInteger startIndex = textRange.start.offset;
|
|
NSUInteger startIndex = textRange.start.offset;
|
|
@@ -171,6 +201,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
[hiText yy_setAttribute:key value:value range:_highlightRange];
|
|
[hiText yy_setAttribute:key value:value range:_highlightRange];
|
|
}];
|
|
}];
|
|
_highlightLayout = [YYTextLayout layoutWithContainer:_innerContainer text:hiText];
|
|
_highlightLayout = [YYTextLayout layoutWithContainer:_innerContainer text:hiText];
|
|
|
|
+ _shrinkHighlightLayout = [YYLabel _shrinkLayoutWithLayout:_highlightLayout];
|
|
if (!_highlightLayout) _highlight = nil;
|
|
if (!_highlightLayout) _highlight = nil;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -193,6 +224,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
[self _hideHighlightAnimated:animated];
|
|
[self _hideHighlightAnimated:animated];
|
|
_highlight = nil;
|
|
_highlight = nil;
|
|
_highlightLayout = nil;
|
|
_highlightLayout = nil;
|
|
|
|
+ _shrinkHighlightLayout = nil;
|
|
}
|
|
}
|
|
|
|
|
|
- (void)_endTouch {
|
|
- (void)_endTouch {
|
|
@@ -202,11 +234,11 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
|
|
|
|
- (CGPoint)_convertPointToLayout:(CGPoint)point {
|
|
- (CGPoint)_convertPointToLayout:(CGPoint)point {
|
|
- CGSize boundingSize = _innerLayout.textBoundingSize;
|
|
|
|
- if (_innerLayout.container.isVerticalForm) {
|
|
|
|
- CGFloat w = _innerLayout.textBoundingSize.width;
|
|
|
|
|
|
+ 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;
|
|
if (w < self.bounds.size.width) w = self.bounds.size.width;
|
|
- point.x += _innerLayout.container.size.width - w;
|
|
|
|
|
|
+ point.x += self._innerLayout.container.size.width - w;
|
|
if (_textVerticalAlignment == YYTextVerticalAlignmentCenter) {
|
|
if (_textVerticalAlignment == YYTextVerticalAlignmentCenter) {
|
|
point.x += (self.bounds.size.width - boundingSize.width) * 0.5;
|
|
point.x += (self.bounds.size.width - boundingSize.width) * 0.5;
|
|
} else if (_textVerticalAlignment == YYTextVerticalAlignmentBottom) {
|
|
} else if (_textVerticalAlignment == YYTextVerticalAlignmentBottom) {
|
|
@@ -224,11 +256,11 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
|
|
|
|
- (CGPoint)_convertPointFromLayout:(CGPoint)point {
|
|
- (CGPoint)_convertPointFromLayout:(CGPoint)point {
|
|
- CGSize boundingSize = _innerLayout.textBoundingSize;
|
|
|
|
- if (_innerLayout.container.isVerticalForm) {
|
|
|
|
- CGFloat w = _innerLayout.textBoundingSize.width;
|
|
|
|
|
|
+ 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;
|
|
if (w < self.bounds.size.width) w = self.bounds.size.width;
|
|
- point.x -= _innerLayout.container.size.width - w;
|
|
|
|
|
|
+ point.x -= self._innerLayout.container.size.width - w;
|
|
if (boundingSize.width < self.bounds.size.width) {
|
|
if (boundingSize.width < self.bounds.size.width) {
|
|
if (_textVerticalAlignment == YYTextVerticalAlignmentCenter) {
|
|
if (_textVerticalAlignment == YYTextVerticalAlignmentCenter) {
|
|
point.x -= (self.bounds.size.width - boundingSize.width) * 0.5;
|
|
point.x -= (self.bounds.size.width - boundingSize.width) * 0.5;
|
|
@@ -267,7 +299,11 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
if (!_shadowColor || _shadowBlurRadius < 0) return nil;
|
|
if (!_shadowColor || _shadowBlurRadius < 0) return nil;
|
|
NSShadow *shadow = [NSShadow new];
|
|
NSShadow *shadow = [NSShadow new];
|
|
shadow.shadowColor = _shadowColor;
|
|
shadow.shadowColor = _shadowColor;
|
|
|
|
+#if !TARGET_INTERFACE_BUILDER
|
|
shadow.shadowOffset = _shadowOffset;
|
|
shadow.shadowOffset = _shadowOffset;
|
|
|
|
+#else
|
|
|
|
+ shadow.shadowOffset = CGSizeMake(_shadowOffset.x, _shadowOffset.y);
|
|
|
|
+#endif
|
|
shadow.shadowBlurRadius = _shadowBlurRadius;
|
|
shadow.shadowBlurRadius = _shadowBlurRadius;
|
|
return shadow;
|
|
return shadow;
|
|
}
|
|
}
|
|
@@ -301,7 +337,12 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
_lineBreakMode = _innerText.yy_lineBreakMode;
|
|
_lineBreakMode = _innerText.yy_lineBreakMode;
|
|
NSShadow *shadow = _innerText.yy_shadow;
|
|
NSShadow *shadow = _innerText.yy_shadow;
|
|
_shadowColor = shadow.shadowColor;
|
|
_shadowColor = shadow.shadowColor;
|
|
|
|
+#if !TARGET_INTERFACE_BUILDER
|
|
_shadowOffset = shadow.shadowOffset;
|
|
_shadowOffset = shadow.shadowOffset;
|
|
|
|
+#else
|
|
|
|
+ _shadowOffset = CGPointMake(shadow.shadowOffset.width, shadow.shadowOffset.height);
|
|
|
|
+#endif
|
|
|
|
+
|
|
_shadowBlurRadius = shadow.shadowBlurRadius;
|
|
_shadowBlurRadius = shadow.shadowBlurRadius;
|
|
_attributedText = _innerText;
|
|
_attributedText = _innerText;
|
|
[self _updateOuterLineBreakMode];
|
|
[self _updateOuterLineBreakMode];
|
|
@@ -450,6 +491,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
|
|
|
|
_highlight = [self _getHighlightAtPoint:point range:&_highlightRange];
|
|
_highlight = [self _getHighlightAtPoint:point range:&_highlightRange];
|
|
_highlightLayout = nil;
|
|
_highlightLayout = nil;
|
|
|
|
+ _shrinkHighlightLayout = nil;
|
|
|
|
|
|
if (_highlight) {
|
|
if (_highlight) {
|
|
_touchBeganPoint = point;
|
|
_touchBeganPoint = point;
|
|
@@ -514,7 +556,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
YYTextPosition *start = [YYTextPosition positionWithOffset:_highlightRange.location];
|
|
YYTextPosition *start = [YYTextPosition positionWithOffset:_highlightRange.location];
|
|
YYTextPosition *end = [YYTextPosition positionWithOffset:_highlightRange.location + _highlightRange.length affinity:YYTextAffinityBackward];
|
|
YYTextPosition *end = [YYTextPosition positionWithOffset:_highlightRange.location + _highlightRange.length affinity:YYTextAffinityBackward];
|
|
YYTextRange *range = [YYTextRange rangeWithStart:start end:end];
|
|
YYTextRange *range = [YYTextRange rangeWithStart:start end:end];
|
|
- CGRect rect = [_innerLayout rectForRange:range];
|
|
|
|
|
|
+ CGRect rect = [self._innerLayout rectForRange:range];
|
|
rect = [self _convertRectFromLayout:rect];
|
|
rect = [self _convertRectFromLayout:rect];
|
|
tapAction(self, _innerText, _highlightRange, rect);
|
|
tapAction(self, _innerText, _highlightRange, rect);
|
|
}
|
|
}
|
|
@@ -568,6 +610,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -584,6 +627,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -614,6 +658,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#if !TARGET_INTERFACE_BUILDER
|
|
- (void)setShadowOffset:(CGSize)shadowOffset {
|
|
- (void)setShadowOffset:(CGSize)shadowOffset {
|
|
if (CGSizeEqualToSize(_shadowOffset, shadowOffset)) return;
|
|
if (CGSizeEqualToSize(_shadowOffset, shadowOffset)) return;
|
|
_shadowOffset = shadowOffset;
|
|
_shadowOffset = shadowOffset;
|
|
@@ -625,6 +670,19 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
[self _setLayoutNeedUpdate];
|
|
[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 {
|
|
- (void)setShadowBlurRadius:(CGFloat)shadowBlurRadius {
|
|
if (_shadowBlurRadius == shadowBlurRadius) return;
|
|
if (_shadowBlurRadius == shadowBlurRadius) return;
|
|
@@ -648,6 +706,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -683,6 +742,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -695,6 +755,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -708,6 +769,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -721,6 +783,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -751,6 +814,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
[self _updateOuterTextProperties];
|
|
[self _updateOuterTextProperties];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -768,6 +832,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -781,6 +846,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -794,6 +860,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -807,6 +874,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -820,6 +888,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -834,12 +903,14 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
[self _setLayoutNeedUpdate];
|
|
[self _setLayoutNeedUpdate];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- (void)setTextLayout:(YYTextLayout *)textLayout {
|
|
- (void)setTextLayout:(YYTextLayout *)textLayout {
|
|
_innerLayout = textLayout;
|
|
_innerLayout = textLayout;
|
|
|
|
+ _shrinkInnerLayout = nil;
|
|
|
|
|
|
if (_ignoreCommonProperties) {
|
|
if (_ignoreCommonProperties) {
|
|
_innerText = (NSMutableAttributedString *)textLayout.text;
|
|
_innerText = (NSMutableAttributedString *)textLayout.text;
|
|
@@ -866,6 +937,7 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
_state.layoutNeedUpdate = NO;
|
|
_state.layoutNeedUpdate = NO;
|
|
[self _setLayoutNeedRedraw];
|
|
[self _setLayoutNeedRedraw];
|
|
[self _endTouch];
|
|
[self _endTouch];
|
|
|
|
+ [self invalidateIntrinsicContentSize];
|
|
}
|
|
}
|
|
|
|
|
|
- (YYTextLayout *)textLayout {
|
|
- (YYTextLayout *)textLayout {
|
|
@@ -878,6 +950,41 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
((YYTextAsyncLayer *)self.layer).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
|
|
#pragma mark - YYTextDebugTarget
|
|
|
|
|
|
- (void)setDebugOption:(YYTextDebugOption *)debugOption {
|
|
- (void)setDebugOption:(YYTextDebugOption *)debugOption {
|
|
@@ -902,7 +1009,8 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
NSMutableArray *attachmentLayers = _attachmentLayers;
|
|
NSMutableArray *attachmentLayers = _attachmentLayers;
|
|
BOOL layoutNeedUpdate = _state.layoutNeedUpdate;
|
|
BOOL layoutNeedUpdate = _state.layoutNeedUpdate;
|
|
BOOL fadeForAsync = _displaysAsynchronously && _fadeOnAsynchronouslyDisplay;
|
|
BOOL fadeForAsync = _displaysAsynchronously && _fadeOnAsynchronouslyDisplay;
|
|
- __block YYTextLayout *layout = (_state.showingHighlight && _highlightLayout) ? _highlightLayout : _innerLayout;
|
|
|
|
|
|
+ __block YYTextLayout *layout = (_state.showingHighlight && _highlightLayout) ? self._highlightLayout : self._innerLayout;
|
|
|
|
+ __block YYTextLayout *shrinkLayout = nil;
|
|
__block BOOL layoutUpdated = NO;
|
|
__block BOOL layoutUpdated = NO;
|
|
if (layoutNeedUpdate) {
|
|
if (layoutNeedUpdate) {
|
|
text = text.copy;
|
|
text = text.copy;
|
|
@@ -939,35 +1047,42 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
if (isCancelled()) return;
|
|
if (isCancelled()) return;
|
|
if (text.length == 0) return;
|
|
if (text.length == 0) return;
|
|
|
|
|
|
|
|
+ YYTextLayout *drawLayout = layout;
|
|
if (layoutNeedUpdate) {
|
|
if (layoutNeedUpdate) {
|
|
layout = [YYTextLayout layoutWithContainer:container text:text];
|
|
layout = [YYTextLayout layoutWithContainer:container text:text];
|
|
|
|
+ shrinkLayout = [YYLabel _shrinkLayoutWithLayout:layout];
|
|
if (isCancelled()) return;
|
|
if (isCancelled()) return;
|
|
layoutUpdated = YES;
|
|
layoutUpdated = YES;
|
|
|
|
+ drawLayout = shrinkLayout ? shrinkLayout : layout;
|
|
}
|
|
}
|
|
|
|
|
|
- CGSize boundingSize = layout.textBoundingSize;
|
|
|
|
|
|
+ CGSize boundingSize = drawLayout.textBoundingSize;
|
|
CGPoint point = CGPointZero;
|
|
CGPoint point = CGPointZero;
|
|
if (verticalAlignment == YYTextVerticalAlignmentCenter) {
|
|
if (verticalAlignment == YYTextVerticalAlignmentCenter) {
|
|
- if (layout.container.isVerticalForm) {
|
|
|
|
|
|
+ if (drawLayout.container.isVerticalForm) {
|
|
point.x = -(size.width - boundingSize.width) * 0.5;
|
|
point.x = -(size.width - boundingSize.width) * 0.5;
|
|
} else {
|
|
} else {
|
|
point.y = (size.height - boundingSize.height) * 0.5;
|
|
point.y = (size.height - boundingSize.height) * 0.5;
|
|
}
|
|
}
|
|
} else if (verticalAlignment == YYTextVerticalAlignmentBottom) {
|
|
} else if (verticalAlignment == YYTextVerticalAlignmentBottom) {
|
|
- if (layout.container.isVerticalForm) {
|
|
|
|
|
|
+ if (drawLayout.container.isVerticalForm) {
|
|
point.x = -(size.width - boundingSize.width);
|
|
point.x = -(size.width - boundingSize.width);
|
|
} else {
|
|
} else {
|
|
point.y = (size.height - boundingSize.height);
|
|
point.y = (size.height - boundingSize.height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
point = YYTextCGPointPixelRound(point);
|
|
point = YYTextCGPointPixelRound(point);
|
|
- [layout drawInContext:context size:size point:point view:nil layer:nil debug:debug cancel:isCancelled];
|
|
|
|
|
|
+ [drawLayout drawInContext:context size:size point:point view:nil layer:nil debug:debug cancel:isCancelled];
|
|
};
|
|
};
|
|
|
|
|
|
task.didDisplay = ^(CALayer *layer, BOOL finished) {
|
|
task.didDisplay = ^(CALayer *layer, BOOL finished) {
|
|
|
|
+ YYTextLayout *drawLayout = layout;
|
|
|
|
+ if (layoutUpdated && shrinkLayout) {
|
|
|
|
+ drawLayout = shrinkLayout;
|
|
|
|
+ }
|
|
if (!finished) {
|
|
if (!finished) {
|
|
// If the display task is cancelled, we should clear the attachments.
|
|
// If the display task is cancelled, we should clear the attachments.
|
|
- for (YYTextAttachment *a in layout.attachments) {
|
|
|
|
|
|
+ for (YYTextAttachment *a in drawLayout.attachments) {
|
|
if ([a.content isKindOfClass:[UIView class]]) {
|
|
if ([a.content isKindOfClass:[UIView class]]) {
|
|
if (((UIView *)a.content).superview == layer.delegate) {
|
|
if (((UIView *)a.content).superview == layer.delegate) {
|
|
[((UIView *)a.content) removeFromSuperview];
|
|
[((UIView *)a.content) removeFromSuperview];
|
|
@@ -986,28 +1101,29 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
if (!view) return;
|
|
if (!view) return;
|
|
if (view->_state.layoutNeedUpdate && layoutUpdated) {
|
|
if (view->_state.layoutNeedUpdate && layoutUpdated) {
|
|
view->_innerLayout = layout;
|
|
view->_innerLayout = layout;
|
|
|
|
+ view->_shrinkInnerLayout = shrinkLayout;
|
|
view->_state.layoutNeedUpdate = NO;
|
|
view->_state.layoutNeedUpdate = NO;
|
|
}
|
|
}
|
|
|
|
|
|
CGSize size = layer.bounds.size;
|
|
CGSize size = layer.bounds.size;
|
|
- CGSize boundingSize = layout.textBoundingSize;
|
|
|
|
|
|
+ CGSize boundingSize = drawLayout.textBoundingSize;
|
|
CGPoint point = CGPointZero;
|
|
CGPoint point = CGPointZero;
|
|
if (verticalAlignment == YYTextVerticalAlignmentCenter) {
|
|
if (verticalAlignment == YYTextVerticalAlignmentCenter) {
|
|
- if (layout.container.isVerticalForm) {
|
|
|
|
|
|
+ if (drawLayout.container.isVerticalForm) {
|
|
point.x = -(size.width - boundingSize.width) * 0.5;
|
|
point.x = -(size.width - boundingSize.width) * 0.5;
|
|
} else {
|
|
} else {
|
|
point.y = (size.height - boundingSize.height) * 0.5;
|
|
point.y = (size.height - boundingSize.height) * 0.5;
|
|
}
|
|
}
|
|
} else if (verticalAlignment == YYTextVerticalAlignmentBottom) {
|
|
} else if (verticalAlignment == YYTextVerticalAlignmentBottom) {
|
|
- if (layout.container.isVerticalForm) {
|
|
|
|
|
|
+ if (drawLayout.container.isVerticalForm) {
|
|
point.x = -(size.width - boundingSize.width);
|
|
point.x = -(size.width - boundingSize.width);
|
|
} else {
|
|
} else {
|
|
point.y = (size.height - boundingSize.height);
|
|
point.y = (size.height - boundingSize.height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
point = YYTextCGPointPixelRound(point);
|
|
point = YYTextCGPointPixelRound(point);
|
|
- [layout drawInContext:nil size:CGSizeZero point:point view:view layer:layer debug:nil cancel:NULL];
|
|
|
|
- for (YYTextAttachment *a in layout.attachments) {
|
|
|
|
|
|
+ [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];
|
|
if ([a.content isKindOfClass:[UIView class]]) [attachmentViews addObject:a.content];
|
|
else if ([a.content isKindOfClass:[CALayer class]]) [attachmentLayers addObject:a.content];
|
|
else if ([a.content isKindOfClass:[CALayer class]]) [attachmentLayers addObject:a.content];
|
|
}
|
|
}
|
|
@@ -1031,3 +1147,101 @@ static dispatch_queue_t YYLabelGetReleaseQueue() {
|
|
}
|
|
}
|
|
|
|
|
|
@end
|
|
@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 containsString:@"bold"]) {
|
|
|
|
+ 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
|