Use NSForegroundColorAttributeName for links in ASTextNodes by subclassing NSLayoutManager

This commit is contained in:
Samuel Hsiung
2016-01-27 13:37:16 -08:00
parent 1514d23d33
commit 90ac40020f
5 changed files with 115 additions and 8 deletions

View File

@@ -369,6 +369,9 @@
B13CA0F81C519EBA00E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = B13CA0F61C519E9400E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h */; };
B13CA1001C52004900E031AB /* ASCollectionNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = B13CA0FF1C52004900E031AB /* ASCollectionNode+Beta.h */; };
B13CA1011C52004900E031AB /* ASCollectionNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = B13CA0FF1C52004900E031AB /* ASCollectionNode+Beta.h */; };
B30BF6521C5964B0004FCD53 /* ASLayoutManager.h in Headers */ = {isa = PBXBuildFile; fileRef = B30BF6501C5964B0004FCD53 /* ASLayoutManager.h */; };
B30BF6531C5964B0004FCD53 /* ASLayoutManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */; };
B30BF6541C59D889004FCD53 /* ASLayoutManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */; };
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061F61B010EFD0018CF92 /* ASCollectionView.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A501A1139C100143C57 /* ASCollectionView.mm */; };
@@ -785,6 +788,8 @@
B0F880591BEAEC7500D17647 /* ASTableNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableNode.m; sourceTree = "<group>"; };
B13CA0F61C519E9400E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewLayoutFacilitatorProtocol.h; sourceTree = "<group>"; };
B13CA0FF1C52004900E031AB /* ASCollectionNode+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCollectionNode+Beta.h"; sourceTree = "<group>"; };
B30BF6501C5964B0004FCD53 /* ASLayoutManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutManager.h; path = TextKit/ASLayoutManager.h; sourceTree = "<group>"; };
B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutManager.m; path = TextKit/ASLayoutManager.m; sourceTree = "<group>"; };
B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B35061DD1B010EDF0018CF92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../AsyncDisplayKit-iOS/Info.plist"; sourceTree = "<group>"; };
CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = "<group>"; };
@@ -1173,6 +1178,8 @@
257754661BED245B00737CA5 /* TextKit */ = {
isa = PBXGroup;
children = (
B30BF6501C5964B0004FCD53 /* ASLayoutManager.h */,
B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */,
257754B71BEE458D00737CA5 /* ASTextKitHelpers.mm */,
257754BB1BEE458E00737CA5 /* ASTextKitCoreTextAdditions.h */,
257754B81BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m */,
@@ -1409,6 +1416,7 @@
055F1A3419ABD3E3004DAFF1 /* ASTableView.h in Headers */,
251B8EF71BBB3D690087C538 /* ASCollectionDataController.h in Headers */,
257754C11BEE458E00737CA5 /* ASTextKitHelpers.h in Headers */,
B30BF6521C5964B0004FCD53 /* ASLayoutManager.h in Headers */,
0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */,
058D0A51195D05CB00B7D73C /* ASTextNode.h in Headers */,
058D0A81195D05F900B7D73C /* ASThread.h in Headers */,
@@ -1771,6 +1779,7 @@
205F0E1E1B373A2C007741D0 /* ASCollectionViewLayoutController.mm in Sources */,
058D0A13195D050800B7D73C /* ASControlNode.m in Sources */,
464052211A3F83C40061C0BA /* ASDataController.mm in Sources */,
B30BF6531C5964B0004FCD53 /* ASLayoutManager.m in Sources */,
05A6D05B19D0EB64002DD95E /* ASDealloc2MainObject.m in Sources */,
ACF6ED211B17843500DA7C62 /* ASDimension.mm in Sources */,
058D0A28195D050800B7D73C /* ASDisplayNode+AsyncDisplay.mm in Sources */,
@@ -1881,6 +1890,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B30BF6541C59D889004FCD53 /* ASLayoutManager.m in Sources */,
92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */,
9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */,
9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */,

View File

@@ -0,0 +1,13 @@
/* Copyright (c) 2016-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 <UIKit/UIKit.h>
@interface ASLayoutManager : NSLayoutManager
@end

View File

@@ -0,0 +1,41 @@
/* Copyright (c) 2016-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 "ASLayoutManager.h"
@implementation ASLayoutManager
- (void)showCGGlyphs:(const CGGlyph *)glyphs
positions:(const CGPoint *)positions
count:(NSUInteger)glyphCount
font:(UIFont *)font
matrix:(CGAffineTransform)textMatrix
attributes:(NSDictionary *)attributes
inContext:(CGContextRef)graphicsContext
{
// NSLayoutManager has a hard coded internal color for hyperlinks which ignores
// NSForegroundColorAttributeName. To get around this, we force the fill color
// in the current context to match NSForegroundColorAttributeName.
UIColor *foregroundColor = attributes[NSForegroundColorAttributeName];
if (foregroundColor)
{
CGContextSetFillColorWithColor(graphicsContext, foregroundColor.CGColor);
}
[super showCGGlyphs:glyphs
positions:positions
count:glyphCount
font:font
matrix:textMatrix
attributes:attributes
inContext:graphicsContext];
}
@end

View File

@@ -12,6 +12,8 @@
#import "ASTextKitContext.h"
#import "ASLayoutManager.h"
@implementation ASTextKitContext
{
// All TextKit operations (even non-mutative ones) must be executed serially.
@@ -35,7 +37,7 @@
std::lock_guard<std::mutex> l(__static_mutex);
// Create the TextKit component stack with our default configuration.
_textStorage = (attributedString ? [[NSTextStorage alloc] initWithAttributedString:attributedString] : [[NSTextStorage alloc] init]);
_layoutManager = layoutManagerFactory ? layoutManagerFactory() : [[NSLayoutManager alloc] init];
_layoutManager = layoutManagerFactory ? layoutManagerFactory() : [[ASLayoutManager alloc] init];
_layoutManager.usesFontLeading = NO;
[_textStorage addLayoutManager:_layoutManager];
_textContainer = [[NSTextContainer alloc] initWithSize:constrainedSize];

View File

@@ -18,7 +18,9 @@
@end
static UITextView *UITextViewWithAttributes(const ASTextKitAttributes &attributes, const CGSize constrainedSize)
static UITextView *UITextViewWithAttributes(const ASTextKitAttributes &attributes,
const CGSize constrainedSize,
NSDictionary *linkTextAttributes)
{
UITextView *textView = [[UITextView alloc] initWithFrame:{ .size = constrainedSize }];
textView.backgroundColor = [UIColor clearColor];
@@ -28,12 +30,15 @@ static UITextView *UITextViewWithAttributes(const ASTextKitAttributes &attribute
textView.textContainerInset = UIEdgeInsetsZero;
textView.layoutManager.usesFontLeading = NO;
textView.attributedText = attributes.attributedString;
textView.linkTextAttributes = linkTextAttributes;
return textView;
}
static UIImage *UITextViewImageWithAttributes(const ASTextKitAttributes &attributes, const CGSize constrainedSize)
static UIImage *UITextViewImageWithAttributes(const ASTextKitAttributes &attributes,
const CGSize constrainedSize,
NSDictionary *linkTextAttributes)
{
UITextView *textView = UITextViewWithAttributes(attributes, constrainedSize);
UITextView *textView = UITextViewWithAttributes(attributes, constrainedSize, linkTextAttributes);
UIGraphicsBeginImageContextWithOptions(constrainedSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
@@ -68,10 +73,11 @@ static UIImage *ASTextKitImageWithAttributes(const ASTextKitAttributes &attribut
return snapshot;
}
static BOOL checkAttributes(const ASTextKitAttributes &attributes, const CGSize constrainedSize)
// linkTextAttributes are only applied to UITextView
static BOOL checkAttributes(const ASTextKitAttributes &attributes, const CGSize constrainedSize, NSDictionary *linkTextAttributes)
{
FBSnapshotTestController *controller = [[FBSnapshotTestController alloc] init];
UIImage *labelImage = UITextViewImageWithAttributes(attributes, constrainedSize);
UIImage *labelImage = UITextViewImageWithAttributes(attributes, constrainedSize, linkTextAttributes);
UIImage *textKitImage = ASTextKitImageWithAttributes(attributes, constrainedSize);
return [controller compareReferenceImage:labelImage toImage:textKitImage error:nil];
}
@@ -83,7 +89,7 @@ static BOOL checkAttributes(const ASTextKitAttributes &attributes, const CGSize
ASTextKitAttributes attributes {
.attributedString = [[NSAttributedString alloc] initWithString:@"hello" attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:12]}]
};
XCTAssert(checkAttributes(attributes, { 100, 100 }));
XCTAssert(checkAttributes(attributes, { 100, 100 }, nil));
}
- (void)testChangingAPropertyChangesHash
@@ -130,7 +136,42 @@ static BOOL checkAttributes(const ASTextKitAttributes &attributes, const CGSize
ASTextKitAttributes attributes {
.attributedString = attrStr
};
XCTAssert(checkAttributes(attributes, { 100, 100 }));
XCTAssert(checkAttributes(attributes, { 100, 100 }, nil));
}
- (void)testLinkInTextUsesForegroundColor
{
NSDictionary *linkTextAttributes = @{ NSForegroundColorAttributeName : [UIColor redColor],
// UITextView adds underline by default and we can't get rid of it
// so we have to choose a style and color and match it in the text kit version
// for this test
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle),
NSUnderlineColorAttributeName: [UIColor redColor],
};
NSDictionary *textAttributes = @{NSFontAttributeName : [UIFont systemFontOfSize:12],
};
NSString *prefixString = @"click ";
NSString *linkString = @"this link";
NSString *textString = [prefixString stringByAppendingString:linkString];
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:textString attributes:textAttributes];
NSURL *linkURL = [NSURL URLWithString:@"https://github.com/facebook/AsyncDisplayKit/issues/967"];
NSRange selectedRange = (NSRange){prefixString.length, linkString.length};
[attrStr addAttribute:NSLinkAttributeName value:linkURL range:selectedRange];
for (NSString *attributeName in linkTextAttributes.keyEnumerator) {
[attrStr addAttribute:attributeName
value:linkTextAttributes[NSUnderlineStyleAttributeName]
range:selectedRange];
}
ASTextKitAttributes textKitattributes {
.attributedString = attrStr
};
XCTAssert(checkAttributes(textKitattributes, { 100, 100 }, linkTextAttributes));
}
@end