From 3793dc024e39590f815073153ace615c275a1e3d Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Tue, 19 Apr 2016 10:50:22 -0700 Subject: [PATCH] Improve setting special properties for certain classes directly to the UIView - Remove duplicated code in ASCollectionNode and ASTableNode - Fix setting the pending state to the view if applying the pending state to the view --- AsyncDisplayKit/ASCollectionNode.mm | 24 ----------------- AsyncDisplayKit/ASDisplayNode.mm | 11 ++++++-- AsyncDisplayKit/ASTableNode.m | 27 +------------------ .../Private/ASDisplayNode+UIViewBridge.mm | 11 ++++++-- .../Private/ASDisplayNodeInternal.h | 2 ++ AsyncDisplayKit/Private/_ASPendingState.h | 2 +- AsyncDisplayKit/Private/_ASPendingState.mm | 14 +++++++--- 7 files changed, 32 insertions(+), 59 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionNode.mm b/AsyncDisplayKit/ASCollectionNode.mm index 605f0defaa..d33ef7bb0d 100644 --- a/AsyncDisplayKit/ASCollectionNode.mm +++ b/AsyncDisplayKit/ASCollectionNode.mm @@ -16,11 +16,6 @@ @interface _ASCollectionPendingState : NSObject @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; - -// If the background color is applied via the pending state it's applied to the layer of the UICollectionView. -// Unfortunately UICollectionView does not consider using the layer backgroundColor property as it's background color, -// so it needs to be applied to the view after the ASCollectionNode did load and the view is available -@property (strong, nonatomic) UIColor *backgroundColor; @end @implementation _ASCollectionPendingState @@ -167,25 +162,6 @@ } } -- (void)setBackgroundColor:(UIColor *)backgroundColor -{ - if ([self pendingState]) { - _pendingState.backgroundColor = backgroundColor; - } else { - ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); - self.view.backgroundColor = backgroundColor; - } -} - -- (UIColor *)backgroundColor -{ - if ([self pendingState]) { - return _pendingState.backgroundColor; - } else { - return self.view.backgroundColor; - } -} - - (ASCollectionView *)view { return (ASCollectionView *)[super view]; diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 56f7f85458..7ad4ee3b6c 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -83,6 +83,13 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) return ASSubclassOverridesSelector([ASDisplayNode class], subclass, selector); } +// For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - we have to be sure to set certain properties +// like setFrame: and setBackgroundColor: directly to the UIView and not apply it to the layer only. +BOOL ASDisplayNodeNeedsSpecialPropertiesSettingHandlingForFlags(ASDisplayNodeFlags flags) +{ + return flags.synchronous && !flags.layerBacked; +} + _ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node) { ASDN::MutexLocker l(node->_propertyLock); @@ -954,8 +961,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) if (self.layerBacked) { [_pendingViewState applyToLayer:self.layer]; } else { - BOOL setFrameDirectly = (_flags.synchronous && !_flags.layerBacked); - [_pendingViewState applyToView:self.view setFrameDirectly:setFrameDirectly]; + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesSettingHandlingForFlags(_flags); + [_pendingViewState applyToView:self.view withSpecialPropertiesHandling:specialPropertiesHandling]; } [_pendingViewState clearChanges]; diff --git a/AsyncDisplayKit/ASTableNode.m b/AsyncDisplayKit/ASTableNode.m index 9906acb5c4..22b6c3926f 100644 --- a/AsyncDisplayKit/ASTableNode.m +++ b/AsyncDisplayKit/ASTableNode.m @@ -14,18 +14,13 @@ @interface _ASTablePendingState : NSObject @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; - -// If the background color is applied via the pending state it's applied to the layer of the UITableView. -// Unfortunately UITableView does not consider using the layer backgroundColor property as it's background color, -// so it needs to be applied to the view after the ASTableNode did load and the view is available -@property (strong, nonatomic) UIColor *backgroundColor; @end @implementation _ASTablePendingState @end @interface ASTableNode () -@property (nonatomic) _ASTablePendingState *pendingState; +@property (nonatomic, strong) _ASTablePendingState *pendingState; @end @interface ASTableView () @@ -79,7 +74,6 @@ self.pendingState = nil; view.asyncDelegate = pendingState.delegate; view.asyncDataSource = pendingState.dataSource; - view.backgroundColor = pendingState.backgroundColor; } } @@ -139,25 +133,6 @@ } } -- (void)setBackgroundColor:(UIColor *)backgroundColor -{ - if ([self pendingState]) { - _pendingState.backgroundColor = backgroundColor; - } else { - ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); - self.view.backgroundColor = backgroundColor; - } -} - -- (UIColor *)backgroundColor -{ - if ([self pendingState]) { - return _pendingState.backgroundColor; - } else { - return self.view.backgroundColor; - } -} - - (ASTableView *)view { return (ASTableView *)[super view]; diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index eaf0cc9f59..c777857485 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -19,6 +19,7 @@ #import "ASPendingStateController.h" #import "ASThread.h" #import "ASTextNode.h" +#import "ASTableNode.h" /** * The following macros are conveniences to help in the common tasks related to the bridging that ASDisplayNode does to UIView and CALayer. @@ -239,11 +240,11 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - make sure UIView gets setFrame: struct ASDisplayNodeFlags flags = _flags; - BOOL setFrameDirectly = flags.synchronous && !flags.layerBacked; + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesSettingHandlingForFlags(flags); BOOL nodeLoaded = __loaded(self); BOOL isMainThread = ASDisplayNodeThreadIsMain(); - if (!setFrameDirectly) { + if (!specialPropertiesHandling) { BOOL canReadProperties = isMainThread || !nodeLoaded; if (canReadProperties) { // We don't have to set frame directly, and we can read current properties. @@ -583,6 +584,12 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo if (shouldApply) { CGColorRef oldBackgroundCGColor = _layer.backgroundColor; _layer.backgroundColor = newBackgroundCGColor; + + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesSettingHandlingForFlags(_flags); + if (specialPropertiesHandling) { + _view.backgroundColor = newBackgroundColor; + } + if (!CGColorEqualToColor(oldBackgroundCGColor, newBackgroundCGColor)) { [self setNeedsDisplay]; } diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index ae3f3c477a..ae7616add2 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -25,8 +25,10 @@ @class _ASDisplayLayer; @class _ASPendingState; @class ASSentinel; +struct ASDisplayNodeFlags; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); +BOOL ASDisplayNodeNeedsSpecialPropertiesSettingHandlingForFlags(ASDisplayNodeFlags flags); /// Get the pending view state for the node, creating one if needed. _ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node); diff --git a/AsyncDisplayKit/Private/_ASPendingState.h b/AsyncDisplayKit/Private/_ASPendingState.h index 8f6702465f..40986b18af 100644 --- a/AsyncDisplayKit/Private/_ASPendingState.h +++ b/AsyncDisplayKit/Private/_ASPendingState.h @@ -24,7 +24,7 @@ // Supports all of the properties included in the ASDisplayNodeViewProperties protocol -- (void)applyToView:(UIView *)view setFrameDirectly:(BOOL)setFrameDirectly; +- (void)applyToView:(UIView *)view withSpecialPropertiesHandling:(BOOL)setFrameDirectly; - (void)applyToLayer:(CALayer *)layer; + (_ASPendingState *)pendingViewStateFromLayer:(CALayer *)layer; diff --git a/AsyncDisplayKit/Private/_ASPendingState.mm b/AsyncDisplayKit/Private/_ASPendingState.mm index b85cbb57c3..303bf2a58c 100644 --- a/AsyncDisplayKit/Private/_ASPendingState.mm +++ b/AsyncDisplayKit/Private/_ASPendingState.mm @@ -745,7 +745,7 @@ static UIColor *defaultTintColor = nil; ASPendingStateApplyMetricsToLayer(self, layer); } -- (void)applyToView:(UIView *)view setFrameDirectly:(BOOL)setFrameDirectly +- (void)applyToView:(UIView *)view withSpecialPropertiesHandling:(BOOL)specialPropertiesHandling { /* Use our convenience setters blah here instead of layer.blah @@ -789,9 +789,16 @@ static UIColor *defaultTintColor = nil; if (flags.setClipsToBounds) view.clipsToBounds = clipsToBounds; - if (flags.setBackgroundColor) + if (flags.setBackgroundColor) { + // Set the background color to the layer as in the UIView bridge we use this value as background color layer.backgroundColor = backgroundColor; + // We have to make sure certain nodes get the background color call directly + if (specialPropertiesHandling) { + view.backgroundColor = [UIColor colorWithCGColor:backgroundColor]; + } + } + if (flags.setTintColor) view.tintColor = self.tintColor; @@ -907,8 +914,7 @@ static UIColor *defaultTintColor = nil; if (flags.setAccessibilityPath) view.accessibilityPath = accessibilityPath; - // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - make sure UIView gets setFrame: - if (flags.setFrame && setFrameDirectly) { + if (flags.setFrame && specialPropertiesHandling) { // Frame is only defined when transform is identity because we explicitly diverge from CALayer behavior and define frame without transform #if DEBUG // Checking if the transform is identity is expensive, so disable when unnecessary. We have assertions on in Release, so DEBUG is the only way I know of.