Skip to content

Commit

Permalink
Merge pull request facebookarchive#1570 from maicki/FixASTextNodeAttr…
Browse files Browse the repository at this point in the history
…ibutedStringInconsitency

[ASTextNode] Fix API ASTextNode API inconsistencies
  • Loading branch information
appleguy committed May 10, 2016
2 parents a1d2f8c + 90c3dbc commit 495f32f
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 55 deletions.
32 changes: 28 additions & 4 deletions AsyncDisplayKit/ASTextNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,19 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
@interface ASTextNode : ASControlNode

/**
@abstract The attributed string to show.
@abstract The styled text displayed by the node.
@discussion Defaults to nil, no text is shown.
For inline image attachments, add an attribute of key NSAttachmentAttributeName, with a value of an NSTextAttachment.
*/
@property (nullable, nonatomic, copy) NSAttributedString *attributedString;
@property (nullable, nonatomic, copy) NSAttributedString *attributedText;

#pragma mark - Truncation

/**
@abstract The attributedString to use when the text must be truncated.
@abstract The attributedText to use when the text must be truncated.
@discussion Defaults to a localized ellipsis character.
*/
@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedString;
@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedText;

/**
@summary The second attributed string appended for truncation.
Expand Down Expand Up @@ -270,4 +270,28 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {

@end

/**
* @abstract Text node deprecated properties
*/
@interface ASTextNode (Deprecated)

/**
The attributedString and attributedText properties are equivalent, but attributedText is now the standard API
name in order to match UILabel and ASEditableTextNode.
@see attributedText
*/
@property (nullable, nonatomic, copy) NSAttributedString *attributedString;


/**
The truncationAttributedString and truncationAttributedText properties are equivalent, but attributedText is now the
standard API name in order to match UILabel and ASEditableTextNode.
@see truncationAttributedText
*/
@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedString;

@end

NS_ASSUME_NONNULL_END
108 changes: 66 additions & 42 deletions AsyncDisplayKit/ASTextNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ @implementation ASTextNode {

NSArray *_exclusionPaths;

NSAttributedString *_composedTruncationString;
NSAttributedString *_composedTruncationText;

NSString *_highlightedLinkAttributeName;
id _highlightedLinkAttributeValue;
Expand Down Expand Up @@ -105,7 +105,7 @@ - (instancetype)init
self.needsDisplayOnBoundsChange = YES;

_truncationMode = NSLineBreakByWordWrapping;
_composedTruncationString = DefaultTruncationAttributedString();
_composedTruncationText = DefaultTruncationAttributedString();

// The common case is for a text node to be non-opaque and blended over some background.
self.opaque = NO;
Expand Down Expand Up @@ -158,8 +158,8 @@ - (void)dealloc

- (NSString *)description
{
NSString *plainString = [[_attributedString string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSString *truncationString = [_composedTruncationString string];
NSString *plainString = [[_attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSString *truncationString = [_composedTruncationText string];
if (plainString.length > 50)
plainString = [[plainString substringToIndex:50] stringByAppendingString:@"\u2026"];
return [NSString stringWithFormat:@"<%@: %p; text = \"%@\"; truncation string = \"%@\"; frame = %@; renderer = %p>", self.class, self, plainString, truncationString, self.nodeLoaded ? NSStringFromCGRect(self.layer.frame) : nil, _renderer];
Expand Down Expand Up @@ -237,8 +237,8 @@ - (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds
- (ASTextKitAttributes)_rendererAttributes
{
return {
.attributedString = _attributedString,
.truncationAttributedString = _composedTruncationString,
.attributedString = _attributedText,
.truncationAttributedString = _composedTruncationText,
.lineBreakMode = _truncationMode,
.maximumNumberOfLines = _maximumNumberOfLines,
.exclusionPaths = _exclusionPaths,
Expand Down Expand Up @@ -340,10 +340,10 @@ - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
[self setNeedsDisplay];

CGSize size = [[self _renderer] size];
if (self.attributedString.length > 0) {
if (_attributedText.length > 0) {
CGFloat screenScale = ASScreenScale();
self.ascender = round([[_attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
self.descender = round([[_attributedString attribute:NSFontAttributeName atIndex:_attributedString.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
self.ascender = round([[_attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
self.descender = round([[_attributedText attribute:NSFontAttributeName atIndex:_attributedText.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
if (_renderer.currentScaleFactor > 0 && _renderer.currentScaleFactor < 1.0) {
// while not perfect, this is a good estimate of what the ascender of the scaled font will be.
self.ascender *= _renderer.currentScaleFactor;
Expand All @@ -355,28 +355,28 @@ - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize

#pragma mark - Modifying User Text

- (void)setAttributedString:(NSAttributedString *)attributedString
- (void)setAttributedText:(NSAttributedString *)attributedText
{
if (attributedString == nil) {
attributedString = [[NSAttributedString alloc] initWithString:@"" attributes:nil];
if (attributedText == nil) {
attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil];
}

if (ASObjectIsEqual(attributedString, _attributedString)) {
if (ASObjectIsEqual(attributedText, _attributedText)) {
return;
}

_attributedString = ASCleanseAttributedStringOfCoreTextAttributes(attributedString);
_attributedText = ASCleanseAttributedStringOfCoreTextAttributes(attributedText);

if (_attributedString.length > 0) {
if (_attributedText.length > 0) {
CGFloat screenScale = ASScreenScale();
self.ascender = round([[_attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
self.descender = round([[_attributedString attribute:NSFontAttributeName atIndex:_attributedString.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
self.ascender = round([[_attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
self.descender = round([[_attributedText attribute:NSFontAttributeName atIndex:_attributedText.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
}

// Sync the truncation string with attributes from the updated _attributedString
// Without this, the size calculation of the text with truncation applied will
// not take into account the attributes of attributedString in the last line
[self _updateComposedTruncationString];
// not take into account the attributes of attributedText in the last line
[self _updateComposedTruncationText];

// We need an entirely new renderer
[self _invalidateRenderer];
Expand All @@ -386,10 +386,10 @@ - (void)setAttributedString:(NSAttributedString *)attributedString

[self setNeedsDisplay];

self.accessibilityLabel = _attributedString.string;
self.accessibilityLabel = _attributedText.string;

// We're an accessibility element by default if there is a string.
self.isAccessibilityElement = _attributedString.length != 0;
self.isAccessibilityElement = _attributedText.length != 0;
}

#pragma mark - Text Layout
Expand Down Expand Up @@ -470,7 +470,7 @@ - (id)_linkAttributeValueAtPoint:(CGPoint)point
{
ASTextKitRenderer *renderer = [self _renderer];
NSRange visibleRange = renderer.visibleRanges[0];
NSAttributedString *attributedString = _attributedString;
NSAttributedString *attributedString = _attributedText;
NSRange clampedRange = NSIntersectionRange(visibleRange, NSMakeRange(0, attributedString.length));

// Check in a 9-point region around the actual touch point so we make sure
Expand Down Expand Up @@ -1023,14 +1023,14 @@ - (UIEdgeInsets)shadowPaddingWithRenderer:(ASTextKitRenderer *)renderer
return defaultTruncationAttributedString;
}

- (void)setTruncationAttributedString:(NSAttributedString *)truncationAttributedString
- (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText
{
if (ASObjectIsEqual(_truncationAttributedString, truncationAttributedString)) {
if (ASObjectIsEqual(_truncationAttributedText, truncationAttributedText)) {
return;
}

_truncationAttributedString = [truncationAttributedString copy];
[self _invalidateTruncationString];
_truncationAttributedText = [truncationAttributedText copy];
[self _invalidateTruncationText];
}

- (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage
Expand All @@ -1040,7 +1040,7 @@ - (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncatio
}

_additionalTruncationMessage = [additionalTruncationMessage copy];
[self _invalidateTruncationString];
[self _invalidateTruncationText];
}

- (void)setTruncationMode:(NSLineBreakMode)truncationMode
Expand All @@ -1055,7 +1055,7 @@ - (void)setTruncationMode:(NSLineBreakMode)truncationMode
- (BOOL)isTruncated
{
NSRange visibleRange = [self _renderer].visibleRanges[0];
return visibleRange.length < _attributedString.length;
return visibleRange.length < _attributedText.length;
}

- (void)setPointSizeScaleFactors:(NSArray *)pointSizeScaleFactors
Expand All @@ -1082,14 +1082,14 @@ - (NSUInteger)lineCount

#pragma mark - Truncation Message

- (void)_updateComposedTruncationString
- (void)_updateComposedTruncationText
{
_composedTruncationString = [self _prepareTruncationStringForDrawing:[self _composedTruncationString]];
_composedTruncationText = [self _prepareTruncationStringForDrawing:[self _composedTruncationText]];
}

- (void)_invalidateTruncationString
- (void)_invalidateTruncationText
{
[self _updateComposedTruncationString];
[self _updateComposedTruncationText];
[self _invalidateRenderer];
[self setNeedsDisplay];
}
Expand All @@ -1111,7 +1111,7 @@ - (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRan
NSUInteger additionalTruncationMessageLength = _additionalTruncationMessage.length;
// We get the location of the truncation token, then add the length of the
// truncation attributed string +1 for the space between.
NSRange range = NSMakeRange(truncationTokenIndex + _truncationAttributedString.length + 1, additionalTruncationMessageLength);
NSRange range = NSMakeRange(truncationTokenIndex + _truncationAttributedText.length + 1, additionalTruncationMessageLength);
return range;
}

Expand All @@ -1120,24 +1120,24 @@ - (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRan
* additional truncation message and a truncation attributed string, they will
* be properly composed.
*/
- (NSAttributedString *)_composedTruncationString
- (NSAttributedString *)_composedTruncationText
{
//If we have neither return the default
if (!_additionalTruncationMessage && !_truncationAttributedString) {
return _composedTruncationString;
if (!_additionalTruncationMessage && !_truncationAttributedText) {
return _composedTruncationText;
}
// Short circuit if we only have one or the other.
if (!_additionalTruncationMessage) {
return _truncationAttributedString;
return _truncationAttributedText;
}
if (!_truncationAttributedString) {
if (!_truncationAttributedText) {
return _additionalTruncationMessage;
}

// If we've reached this point, both _additionalTruncationMessage and
// _truncationAttributedString are present. Compose them.

NSMutableAttributedString *newComposedTruncationString = [[NSMutableAttributedString alloc] initWithAttributedString:_truncationAttributedString];
NSMutableAttributedString *newComposedTruncationString = [[NSMutableAttributedString alloc] initWithAttributedString:_truncationAttributedText];
[newComposedTruncationString replaceCharactersInRange:NSMakeRange(newComposedTruncationString.length, 0) withString:@" "];
[newComposedTruncationString appendAttributedString:_additionalTruncationMessage];
return newComposedTruncationString;
Expand All @@ -1153,9 +1153,9 @@ - (NSAttributedString *)_prepareTruncationStringForDrawing:(NSAttributedString *
truncationString = ASCleanseAttributedStringOfCoreTextAttributes(truncationString);
NSMutableAttributedString *truncationMutableString = [truncationString mutableCopy];
// Grab the attributes from the full string
if (_attributedString.length > 0) {
NSAttributedString *originalString = _attributedString;
NSInteger originalStringLength = _attributedString.length;
if (_attributedText.length > 0) {
NSAttributedString *originalString = _truncationAttributedText;
NSInteger originalStringLength = _truncationAttributedText.length;
// Add any of the original string's attributes to the truncation string,
// but don't overwrite any of the truncation string's attributes
NSDictionary *originalStringAttributes = [originalString attributesAtIndex:originalStringLength-1 effectiveRange:NULL];
Expand All @@ -1170,3 +1170,27 @@ - (NSAttributedString *)_prepareTruncationStringForDrawing:(NSAttributedString *
}

@end

@implementation ASTextNode (Deprecated)

- (void)setAttributedString:(NSAttributedString *)attributedString
{
self.attributedText = attributedString;
}

- (NSAttributedString *)attributedString
{
return self.attributedText;
}

- (void)setTruncationAttributedString:(NSAttributedString *)truncationAttributedString
{
self.truncationAttributedText = truncationAttributedString;
}

- (NSAttributedString *)truncationAttributedString
{
return self.truncationAttributedText;
}

@end
18 changes: 9 additions & 9 deletions AsyncDisplayKitTests/ASTextNodeTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ - (BOOL)textNode:(ASTextNode *)textNode shouldHighlightLinkAttribute:(NSString *
@interface ASTextNodeTests : XCTestCase

@property (nonatomic, readwrite, strong) ASTextNode *textNode;
@property (nonatomic, readwrite, copy) NSAttributedString *attributedString;
@property (nonatomic, readwrite, copy) NSAttributedString *attributedText;

@end

Expand Down Expand Up @@ -80,8 +80,8 @@ - (void)setUp
[mas addAttribute:NSParagraphStyleAttributeName value:lastLinePara
range:NSMakeRange(mas.length - 1, 1)];

_attributedString = mas;
_textNode.attributedString = _attributedString;
_attributedText = mas;
_textNode.attributedText = _attributedText;
}

#pragma mark - ASTextNode
Expand All @@ -97,8 +97,8 @@ - (void)testAllocASTextNode
- (void)testSettingTruncationMessage
{
NSAttributedString *truncation = [[NSAttributedString alloc] initWithString:@"..." attributes:nil];
_textNode.truncationAttributedString = truncation;
XCTAssertTrue([_textNode.truncationAttributedString isEqualToAttributedString:truncation], @"Failed to set truncation message");
_textNode.truncationAttributedText = truncation;
XCTAssertTrue([_textNode.truncationAttributedText isEqualToAttributedString:truncation], @"Failed to set truncation message");
}

- (void)testSettingAdditionalTruncationMessage
Expand Down Expand Up @@ -142,11 +142,11 @@ - (void)testRecalculationOfSizeIsSameAsOriginallyCalculatedFloatingPointSize

- (void)testAccessibility
{
_textNode.attributedString = _attributedString;
_textNode.attributedText = _attributedText;
XCTAssertTrue(_textNode.isAccessibilityElement, @"Should be an accessibility element");
XCTAssertTrue(_textNode.accessibilityTraits == UIAccessibilityTraitStaticText, @"Should have static text accessibility trait, instead has %llu", _textNode.accessibilityTraits);

XCTAssertTrue([_textNode.accessibilityLabel isEqualToString:_attributedString.string], @"Accessibility label is incorrectly set to \n%@\n when it should be \n%@\n", _textNode.accessibilityLabel, _attributedString.string);
XCTAssertTrue([_textNode.accessibilityLabel isEqualToString:_attributedText.string], @"Accessibility label is incorrectly set to \n%@\n when it should be \n%@\n", _textNode.accessibilityLabel, _attributedText.string);
}

- (void)testLinkAttribute
Expand All @@ -156,7 +156,7 @@ - (void)testLinkAttribute
NSString *linkString = @"Link";
NSRange linkRange = NSMakeRange(0, linkString.length);
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:linkString attributes:@{ linkAttributeName : linkAttributeValue}];
_textNode.attributedString = attributedString;
_textNode.attributedText = attributedString;
_textNode.linkAttributeNames = @[linkAttributeName];

ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new];
Expand All @@ -178,7 +178,7 @@ - (void)testTapNotOnALinkAttribute
NSString *linkString = @"Link notalink";
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:linkString];
[attributedString addAttribute:linkAttributeName value:linkAttributeValue range:NSMakeRange(0, 4)];
_textNode.attributedString = attributedString;
_textNode.attributedText = attributedString;
_textNode.linkAttributeNames = @[linkAttributeName];

ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new];
Expand Down

0 comments on commit 495f32f

Please sign in to comment.