Skip to content

Commit

Permalink
Add support for tintColor on ASImageNode and ASButtonNode
Browse files Browse the repository at this point in the history
  • Loading branch information
rahul-malik committed Jul 30, 2019
1 parent 7aba287 commit f45a502
Show file tree
Hide file tree
Showing 17 changed files with 195 additions and 18 deletions.
14 changes: 9 additions & 5 deletions AsyncDisplayKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@
8021EC1D1D2B00B100799119 /* UIImage+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.mm */; };
81E95C141D62639600336598 /* ASTextNodeSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 81E95C131D62639600336598 /* ASTextNodeSnapshotTests.mm */; };
81FF150722EB5F410039311A /* ASButtonSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 81FF150622EB5F410039311A /* ASButtonSnapshotTests.mm */; };
83A7D95B1D44547700BF333E /* ASWeakMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83A7D9591D44542100BF333E /* ASWeakMap.mm */; };
83A7D95C1D44548100BF333E /* ASWeakMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A7D9581D44542100BF333E /* ASWeakMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
83A7D95E1D446A6E00BF333E /* ASWeakMapTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83A7D95D1D446A6E00BF333E /* ASWeakMapTests.mm */; };
Expand Down Expand Up @@ -747,6 +748,7 @@
81E95C131D62639600336598 /* ASTextNodeSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTextNodeSnapshotTests.mm; sourceTree = "<group>"; };
81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRunLoopQueue.h; path = ../ASRunLoopQueue.h; sourceTree = "<group>"; };
81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRunLoopQueue.mm; path = ../ASRunLoopQueue.mm; sourceTree = "<group>"; };
81FF150622EB5F410039311A /* ASButtonSnapshotTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonSnapshotTests.mm; sourceTree = "<group>"; };
83A7D9581D44542100BF333E /* ASWeakMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASWeakMap.h; sourceTree = "<group>"; };
83A7D9591D44542100BF333E /* ASWeakMap.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASWeakMap.mm; sourceTree = "<group>"; };
83A7D95D1D446A6E00BF333E /* ASWeakMapTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASWeakMapTests.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1297,23 +1299,22 @@
058D09C5195D04C000B7D73C /* Tests */ = {
isa = PBXGroup;
children = (
9692B4FE219E12370060C2C3 /* ASCollectionViewThrashTests.mm */,
F325E48F217460B000AC93A4 /* ASTextNode2Tests.mm */,
F325E48B21745F9E00AC93A4 /* ASButtonNodeTests.mm */,
F3F698D1211CAD4600800CB1 /* ASDisplayViewAccessibilityTests.mm */,
DBC452DD1C5C6A6A00B16017 /* ArrayDiffingTests.mm */,
AC026B571BD3F61800BBC17E /* ASAbsoluteLayoutSpecSnapshotTests.mm */,
696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */,
29CDC2E11AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.mm */,
242995D21B29743C00090100 /* ASBasicImageDownloaderTests.mm */,
296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.mm */,
CC3B208F1C3F892D00798563 /* ASBridgedPropertiesTests.mm */,
F325E48B21745F9E00AC93A4 /* ASButtonNodeTests.mm */,
81FF150622EB5F410039311A /* ASButtonSnapshotTests.mm */,
CC051F1E1D7A286A006434CB /* ASCALayerTests.mm */,
ACF6ED531B178DC700DA7C62 /* ASCenterLayoutSpecSnapshotTests.mm */,
CCDD148A1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.mm */,
CC35CEC520DD87280006448D /* ASCollectionsTests.mm */,
2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.mm */,
9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.mm */,
9692B4FE219E12370060C2C3 /* ASCollectionViewThrashTests.mm */,
CCEDDDD8200C518800FFCD0A /* ASConfigurationTests.mm */,
2911485B1A77147A005D0878 /* ASControlNodeTests.mm */,
1A6C000F1FAB4ED400D05926 /* ASCornerLayoutSpecSnapshotTests.mm */,
Expand All @@ -1328,6 +1329,7 @@
058D0A2F195D057000B7D73C /* ASDisplayNodeTests.mm */,
058D0A30195D057000B7D73C /* ASDisplayNodeTestsHelper.h */,
058D0A31195D057000B7D73C /* ASDisplayNodeTestsHelper.mm */,
F3F698D1211CAD4600800CB1 /* ASDisplayViewAccessibilityTests.mm */,
697B31591CFE4B410049936F /* ASEditableTextNodeTests.mm */,
471D04B0224CB98600649215 /* ASImageNodeBackingSizeTests.mm */,
056D21541ABCEF50001107EF /* ASImageNodeSnapshotTests.mm */,
Expand Down Expand Up @@ -1366,6 +1368,7 @@
254C6B531BF8FF2A003EC431 /* ASTextKitTests.mm */,
254C6B511BF8FE6D003EC431 /* ASTextKitTruncationTests.mm */,
C057D9BC20B5453D00FC9112 /* ASTextNode2SnapshotTests.mm */,
F325E48F217460B000AC93A4 /* ASTextNode2Tests.mm */,
CC8B05D71D73979700F54286 /* ASTextNodePerformanceTests.mm */,
81E95C131D62639600336598 /* ASTextNodeSnapshotTests.mm */,
058D0A36195D057000B7D73C /* ASTextNodeTests.mm */,
Expand All @@ -1374,13 +1377,13 @@
9644CFDF2193777C00213478 /* ASThrashUtility.m */,
CCE4F9BC1F0ECE5200062E4E /* ASTLayoutFixture.h */,
CCE4F9BD1F0ECE5200062E4E /* ASTLayoutFixture.mm */,
D933F040224AD17F00FF495E /* ASTransactionTests.mm */,
CC0AEEA31D66316E005D1C78 /* ASUICollectionViewTests.mm */,
AEEC47E31C21D3D200EC1693 /* ASVideoNodeTests.mm */,
CCA221D21D6FA7EF00AF6A0F /* ASViewControllerTests.mm */,
83A7D95D1D446A6E00BF333E /* ASWeakMapTests.mm */,
CC3B208D1C3F7D0A00798563 /* ASWeakSetTests.mm */,
695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */,
D933F040224AD17F00FF495E /* ASTransactionTests.mm */,
057D02C01AC0A66700C7AC3C /* AsyncDisplayKitTestHost */,
CC583ABF1EF9BAB400134156 /* Common */,
058D09C6195D04C000B7D73C /* Supporting Files */,
Expand Down Expand Up @@ -2293,6 +2296,7 @@
CC3B208E1C3F7D0A00798563 /* ASWeakSetTests.mm in Sources */,
F711994E1D20C21100568860 /* ASDisplayNodeExtrasTests.mm in Sources */,
BB5FC3CE1F9BA689007F191E /* ASNavigationControllerTests.mm in Sources */,
81FF150722EB5F410039311A /* ASButtonSnapshotTests.mm in Sources */,
ACF6ED5D1B178DC700DA7C62 /* ASDimensionTests.mm in Sources */,
BB5FC3D11F9C9389007F191E /* ASTabBarControllerTests.mm in Sources */,
695BE2551DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm in Sources */,
Expand Down
72 changes: 63 additions & 9 deletions Source/ASButtonNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ - (instancetype)init
_contentEdgeInsets = UIEdgeInsetsZero;
_imageAlignment = ASButtonNodeImageAlignmentBeginning;
self.accessibilityTraits = self.defaultAccessibilityTraits;

[self updateYogaLayoutIfNeeded];
}
return self;
Expand All @@ -55,7 +55,7 @@ - (ASTextNode *)titleNode
#if TARGET_OS_IOS
// tvOS needs access to the underlying view
// of the button node to add a touch handler.
[_titleNode setLayerBacked:YES];
// [_titleNode setLayerBacked:YES];
#endif
_titleNode.style.flexShrink = 1.0;
}
Expand All @@ -69,7 +69,7 @@ - (ASImageNode *)imageNode
ASLockScopeSelf();
if (!_imageNode) {
_imageNode = [[ASImageNode alloc] init];
[_imageNode setLayerBacked:YES];
// [_imageNode setLayerBacked:YES];
}
return _imageNode;
}
Expand Down Expand Up @@ -131,6 +131,55 @@ - (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously
[self.titleNode setDisplaysAsynchronously:displaysAsynchronously];
}


- (NSAttributedString *)__updateTitle:(NSAttributedString *)title withForegroundColor:(UIColor *)newColor
{
if (title) {
NSMutableAttributedString *mutString = [[NSMutableAttributedString alloc] initWithAttributedString:title];
NSRange limit = NSMakeRange(0, _normalAttributedTitle.length);
NSRange effectiveRange;
UIColor *attributeValue = (UIColor *)[mutString attribute:NSForegroundColorAttributeName atIndex:limit.location effectiveRange:&effectiveRange];
if (attributeValue == nil) {
[mutString setAttributes:@{ NSForegroundColorAttributeName : newColor } range:limit];
}

return [mutString copy];
}
return title;
}

- (void)updateTitleForegroundColor:(UIColor *)newColor
{
if (_normalAttributedTitle) {
[self setAttributedTitle:[self __updateTitle:_normalAttributedTitle withForegroundColor:newColor] forState:UIControlStateNormal];
}

if (_highlightedAttributedTitle) {
[self setAttributedTitle:[self __updateTitle:_highlightedAttributedTitle withForegroundColor:newColor] forState:UIControlStateHighlighted];
}

if (_selectedAttributedTitle) {
[self setAttributedTitle:[self __updateTitle:_selectedAttributedTitle withForegroundColor:newColor] forState:UIControlStateSelected];
}
if (_selectedHighlightedAttributedTitle) {
[self setAttributedTitle:[self __updateTitle:_selectedHighlightedAttributedTitle withForegroundColor:newColor] forState:UIControlStateSelected | UIControlStateHighlighted];
}

if (_disabledAttributedTitle) {
[self setAttributedTitle:[self __updateTitle:_disabledAttributedTitle withForegroundColor:newColor] forState:UIControlStateDisabled];
}
}

- (void)setTintColor:(UIColor *)tintColor
{
if (![self.tintColor isEqual:tintColor]) {
[super setTintColor:tintColor];
// Forward tint color to underlying image and title nodes to mirror UIButton
self.imageNode.tintColor = tintColor;
[self updateTitleForegroundColor:tintColor];
}
}

- (void)updateImage
{
[self lock];
Expand Down Expand Up @@ -301,12 +350,17 @@ - (void)setImageAlignment:(ASButtonNodeImageAlignment)imageAlignment
#if TARGET_OS_IOS
- (void)setTitle:(NSString *)title withFont:(UIFont *)font withColor:(UIColor *)color forState:(UIControlState)state
{
NSDictionary *attributes = @{
NSFontAttributeName: font ? : [UIFont systemFontOfSize:[UIFont buttonFontSize]],
NSForegroundColorAttributeName : color ? : [UIColor blackColor]
};

NSAttributedString *string = [[NSAttributedString alloc] initWithString:title attributes:attributes];
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
attributes[NSFontAttributeName] = font ? : [UIFont systemFontOfSize:[UIFont buttonFontSize]];
if (color != nil) {
// From apple's documentation: If color is not specified, NSForegroundColorAttributeName will fallback to black
// Only set if the color is nonnull
attributes[NSForegroundColorAttributeName] = color;
} else if (self.tintColor != nil) {
attributes[NSForegroundColorAttributeName] = self.tintColor;
}

NSAttributedString *string = [[NSAttributedString alloc] initWithString:title attributes:[attributes copy]];
[self setAttributedTitle:string forState:state];
}
#endif
Expand Down
17 changes: 16 additions & 1 deletion Source/ASImageNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ @interface ASImageNodeDrawParameters : NSObject {
CGRect _bounds;
CGFloat _contentsScale;
UIColor *_backgroundColor;
UIColor *_tintColor;
UIViewContentMode _contentMode;
BOOL _cropEnabled;
BOOL _forceUpscaling;
Expand Down Expand Up @@ -69,6 +70,7 @@ @interface ASImageNodeContentsKey : NSObject
@property CGRect imageDrawRect;
@property BOOL isOpaque;
@property (nonatomic, copy) UIColor *backgroundColor;
@property (nonatomic, copy) UIColor *tintColor;
@property (nonatomic) ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext;
@property (nonatomic) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext;
@property (nonatomic) asimagenode_modification_block_t imageModificationBlock;
Expand All @@ -94,6 +96,7 @@ - (BOOL)isEqual:(id)object
&& CGRectEqualToRect(_imageDrawRect, other.imageDrawRect)
&& _isOpaque == other.isOpaque
&& [_backgroundColor isEqual:other.backgroundColor]
&& [_tintColor isEqual:other.tintColor]
&& _willDisplayNodeContentWithRenderingContext == other.willDisplayNodeContentWithRenderingContext
&& _didDisplayNodeContentWithRenderingContext == other.didDisplayNodeContentWithRenderingContext
&& _imageModificationBlock == other.imageModificationBlock;
Expand All @@ -112,6 +115,7 @@ - (NSUInteger)hash
CGRect imageDrawRect;
NSInteger isOpaque;
NSUInteger backgroundColorHash;
NSUInteger tintColorHash;
void *willDisplayNodeContentWithRenderingContext;
void *didDisplayNodeContentWithRenderingContext;
void *imageModificationBlock;
Expand All @@ -122,6 +126,7 @@ - (NSUInteger)hash
_imageDrawRect,
_isOpaque,
_backgroundColor.hash,
_tintColor.hash,
(void *)_willDisplayNodeContentWithRenderingContext,
(void *)_didDisplayNodeContentWithRenderingContext,
(void *)_imageModificationBlock
Expand Down Expand Up @@ -296,6 +301,7 @@ - (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer
drawParameters->_opaque = self.opaque;
drawParameters->_contentsScale = _contentsScaleForDisplay;
drawParameters->_backgroundColor = self.backgroundColor;
drawParameters->_tintColor = self.tintColor;
drawParameters->_contentMode = self.contentMode;
drawParameters->_cropEnabled = _imageNodeFlags.cropEnabled;
drawParameters->_forceUpscaling = _imageNodeFlags.forceUpscaling;
Expand Down Expand Up @@ -330,6 +336,7 @@ + (UIImage *)displayWithParameters:(id<NSObject>)parameter isCancelled:(NS_NOESC
BOOL cropEnabled = drawParameter->_cropEnabled;
BOOL isOpaque = drawParameter->_opaque;
UIColor *backgroundColor = drawParameter->_backgroundColor;
UIColor *tintColor = drawParameter->_tintColor;
UIViewContentMode contentMode = drawParameter->_contentMode;
CGFloat contentsScale = drawParameter->_contentsScale;
CGRect cropDisplayBounds = drawParameter->_cropDisplayBounds;
Expand Down Expand Up @@ -401,6 +408,7 @@ + (UIImage *)displayWithParameters:(id<NSObject>)parameter isCancelled:(NS_NOESC
contentsKey.imageDrawRect = imageDrawRect;
contentsKey.isOpaque = isOpaque;
contentsKey.backgroundColor = backgroundColor;
contentsKey.tintColor = tintColor;
contentsKey.willDisplayNodeContentWithRenderingContext = willDisplayNodeContentWithRenderingContext;
contentsKey.didDisplayNodeContentWithRenderingContext = didDisplayNodeContentWithRenderingContext;
contentsKey.imageModificationBlock = imageModificationBlock;
Expand Down Expand Up @@ -508,7 +516,14 @@ + (UIImage *)createContentsForkey:(ASImageNodeContentsKey *)key drawParameters:(
key.didDisplayNodeContentWithRenderingContext(context, drawParameters);
}
});

UIImageRenderingMode renderingMode = [key.image renderingMode];
if ((renderingMode == UIImageRenderingModeAlwaysTemplate
|| renderingMode == UIImageRenderingModeAutomatic)
&& key.tintColor) {
asimagenode_modification_block_t tintModificationBlock = ASImageNodeTintColorModificationBlock(key.tintColor);
result = tintModificationBlock(result);
}

if (key.imageModificationBlock) {
result = key.imageModificationBlock(result);
}
Expand Down
13 changes: 12 additions & 1 deletion Source/ASTextNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ @interface ASTextNodeDrawParameter : NSObject {
@package
ASTextKitAttributes _rendererAttributes;
UIColor *_backgroundColor;
UIColor *_tintColor;
UIEdgeInsets _textContainerInsets;
CGFloat _contentScale;
BOOL _opaque;
Expand All @@ -147,6 +148,7 @@ @implementation ASTextNodeDrawParameter

- (instancetype)initWithRendererAttributes:(ASTextKitAttributes)rendererAttributes
backgroundColor:(/*nullable*/ UIColor *)backgroundColor
tintColor:(/*nullable*/ UIColor *)tintColor
textContainerInsets:(UIEdgeInsets)textContainerInsets
contentScale:(CGFloat)contentScale
opaque:(BOOL)opaque
Expand All @@ -156,6 +158,7 @@ - (instancetype)initWithRendererAttributes:(ASTextKitAttributes)rendererAttribut
if (self != nil) {
_rendererAttributes = rendererAttributes;
_backgroundColor = backgroundColor;
_tintColor = tintColor;
_textContainerInsets = textContainerInsets;
_contentScale = contentScale;
_opaque = opaque;
Expand Down Expand Up @@ -540,6 +543,7 @@ - (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer

return [[ASTextNodeDrawParameter alloc] initWithRendererAttributes:[self _locked_rendererAttributes]
backgroundColor:self.backgroundColor
tintColor:self.tintColor
textContainerInsets:_textContainerInset
contentScale:_contentsScaleForDisplay
opaque:self.isOpaque
Expand All @@ -555,6 +559,7 @@ + (UIImage *)displayWithParameters:(id<NSObject>)parameters isCancelled:(NS_NOES
}

UIColor *backgroundColor = drawParameter->_backgroundColor;
UIColor *tintColor = drawParameter->_tintColor;
UIEdgeInsets textContainerInsets = drawParameter ? drawParameter->_textContainerInsets : UIEdgeInsetsZero;
ASTextKitRenderer *renderer = [drawParameter rendererForBounds:drawParameter->_bounds];

Expand All @@ -570,7 +575,13 @@ + (UIImage *)displayWithParameters:(id<NSObject>)parameters isCancelled:(NS_NOES
[backgroundColor setFill];
UIRectFillUsingBlendMode(CGContextGetClipBoundingBox(context), kCGBlendModeCopy);
}


// Set stroke path if tint is specified
if (tintColor) {
[tintColor setStroke]; // doesn't work? overwrite attributedString here?

}

// Draw text
[renderer drawInContext:context bounds:drawParameter->_bounds];
CGContextRestoreGState(context);
Expand Down
58 changes: 58 additions & 0 deletions Tests/ASButtonSnapshotTests.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// ASButtonSnapshotTests.m
// AsyncDisplayKitTests
//
// Created by Rahul Malik on 7/26/19.
// Copyright © 2019 Pinterest. All rights reserved.
//

#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import "ASSnapshotTestCase.h"

@interface ASButtonSnapshotTests : ASSnapshotTestCase

@end


@implementation ASButtonSnapshotTests

- (UIImage *)testImage
{
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"logo-square"
ofType:@"png"
inDirectory:@"TestResources"];
return [UIImage imageWithContentsOfFile:path];
}


- (void)testTintColor
{git
ASButtonNode *node = [[ASButtonNode alloc] init];
node.tintColor = UIColor.redColor;
[node setImage:[self testImage] forState:UIControlStateNormal];
[node setTitle:@"Press Me"
withFont:[UIFont systemFontOfSize:48]
withColor:nil
forState:UIControlStateNormal];
node.imageNode.style.width = ASDimensionMake(200);
node.imageNode.style.height = ASDimensionMake(200);
ASDisplayNodeSizeToFitSize(node, CGSizeMake(1000, 1000));
ASSnapshotVerifyNode(node, nil);
}

- (void)testTintColorWithForegroundColorSet
{
ASButtonNode *node = [[ASButtonNode alloc] init];
node.tintColor = UIColor.redColor;
[node setImage:[self testImage] forState:UIControlStateNormal];
[node setTitle:@"Press Me"
withFont:[UIFont systemFontOfSize:48]
withColor:[UIColor blueColor]
forState:UIControlStateNormal];
node.imageNode.style.width = ASDimensionMake(200);
node.imageNode.style.height = ASDimensionMake(200);
ASDisplayNodeSizeToFitSize(node, CGSizeMake(1000, 1000));
ASSnapshotVerifyNode(node, nil);
}

@end
Loading

0 comments on commit f45a502

Please sign in to comment.