Add support for textContainerInset to ASTextNode (ala UITextView) (#2062)

* Add support for textContainerInset to ASTextNode (ala UITextView)

* Better comment, parens to increase readability. Thanks @schneider!

* Add textContainerInset snapshot test.
This commit is contained in:
Garrett Moon 2016-08-12 12:07:00 -07:00 committed by Adlai Holler
parent 39cb188b9e
commit 2c9e51e8f7
4 changed files with 95 additions and 4 deletions

View File

@ -31,6 +31,14 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nullable, nonatomic, copy) NSTextStorage * (^textStorageCreationBlock)(NSAttributedString *_Nullable attributedString);
/**
@abstract Text margins for text laid out in the text node.
@discussion defaults to UIEdgeInsetsZero.
This property can be useful for handling text which does not fit within the view by default. An example: like UILabel,
ASTextNode will clip the left and right of the string "judar" if it's rendered in an italicised font.
*/
@property (nonatomic, assign) UIEdgeInsets textContainerInset;
@end
NS_ASSUME_NONNULL_END

View File

@ -48,6 +48,8 @@ struct ASTextNodeDrawParameter {
UIColor *_cachedShadowUIColor;
CGFloat _shadowOpacity;
CGFloat _shadowRadius;
UIEdgeInsets _textContainerInset;
NSArray *_exclusionPaths;
@ -213,7 +215,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
ASDN::MutexLocker l(__instanceLock__);
if (_renderer == nil) {
CGSize constrainedSize = _constrainedSize.width != -INFINITY ? _constrainedSize : bounds.size;
CGSize constrainedSize;
if (_constrainedSize.width != -INFINITY) {
constrainedSize = _constrainedSize;
} else {
constrainedSize = bounds.size;
constrainedSize.width -= (_textContainerInset.left + _textContainerInset.right);
constrainedSize.height -= (_textContainerInset.top + _textContainerInset.bottom);
}
_renderer = [[ASTextKitRenderer alloc] initWithTextKitAttributes:[self _rendererAttributes]
constrainedSize:constrainedSize];
}
@ -279,6 +289,24 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
#pragma mark - Layout and Sizing
- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset
{
ASDN::MutexLocker l(__instanceLock__);
BOOL needsUpdate = !UIEdgeInsetsEqualToEdgeInsets(textContainerInset, _textContainerInset);
if (needsUpdate) {
_textContainerInset = textContainerInset;
[self invalidateCalculatedLayout];
[self setNeedsLayout];
}
}
- (UIEdgeInsets)textContainerInset
{
ASDN::MutexLocker l(__instanceLock__);
return _textContainerInset;
}
- (BOOL)_needInvalidateRendererForBoundsSize:(CGSize)boundsSize
{
ASDN::MutexLocker l(__instanceLock__);
@ -291,6 +319,10 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
// a new one. However, there are common cases where the constrained size doesn't need to be the same as calculated.
CGSize rendererConstrainedSize = _renderer.constrainedSize;
//inset bounds
boundsSize.width -= _textContainerInset.left + _textContainerInset.right;
boundsSize.height -= _textContainerInset.top + _textContainerInset.bottom;
if (CGSizeEqualToSize(boundsSize, rendererConstrainedSize)) {
return NO;
} else {
@ -321,9 +353,14 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
if (layout != nil) {
ASDN::MutexLocker l(__instanceLock__);
if (CGSizeEqualToSize(_constrainedSize, layout.size) == NO) {
_constrainedSize = layout.size;
_renderer.constrainedSize = layout.size;
CGSize layoutSize = layout.size;
//Apply textContainerInset
layoutSize.width -= (_textContainerInset.left + _textContainerInset.right);
layoutSize.height -= (_textContainerInset.top + _textContainerInset.bottom);
if (CGSizeEqualToSize(_constrainedSize, layoutSize) == NO) {
_constrainedSize = layoutSize;
_renderer.constrainedSize = layoutSize;
}
}
}
@ -335,6 +372,10 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
ASDN::MutexLocker l(__instanceLock__);
//remove textContainerInset
constrainedSize.width -= (_textContainerInset.left + _textContainerInset.right);
constrainedSize.height -= (_textContainerInset.top + _textContainerInset.bottom);
_constrainedSize = constrainedSize;
// Instead of invalidating the renderer, in case this is a new call with a different constrained size,
@ -353,6 +394,11 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
self.descender *= _renderer.currentScaleFactor;
}
}
//add textContainerInset
size.width += (_textContainerInset.left + _textContainerInset.right);
size.height += (_textContainerInset.top + _textContainerInset.bottom);
return size;
}
@ -466,6 +512,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
CGContextSaveGState(context);
CGContextTranslateCTM(context, _textContainerInset.left, _textContainerInset.top);
ASTextKitRenderer *renderer = [self _rendererWithBounds:drawParameterBounds];
UIEdgeInsets shadowPadding = [self shadowPaddingWithRenderer:renderer];
CGPoint boundsOrigin = drawParameterBounds.origin;

View File

@ -0,0 +1,35 @@
//
// ASTextNodeSnapshotTests.m
// AsyncDisplayKit
//
// Created by Garrett Moon on 8/12/16.
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
#import "ASSnapshotTestCase.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface ASTextNodeSnapshotTests : ASSnapshotTestCase
@end
@implementation ASTextNodeSnapshotTests
- (void)testTextContainerInset
{
// trivial test case to ensure ASSnapshotTestCase works
ASTextNode *textNode = [[ASTextNode alloc] init];
textNode.attributedText = [[NSAttributedString alloc] initWithString:@"judar"
attributes:@{NSFontAttributeName : [UIFont italicSystemFontOfSize:24]}];
[textNode measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))];
textNode.frame = CGRectMake(0, 0, textNode.calculatedSize.width, textNode.calculatedSize.height);
textNode.textContainerInset = UIEdgeInsetsMake(0, 2, 0, 2);
ASSnapshotVerifyNode(textNode, nil);
}
@end

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB