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
This commit is contained in:
Michael Schneider
2016-04-19 10:50:22 -07:00
parent 8d20321d67
commit 3793dc024e
7 changed files with 32 additions and 59 deletions

View File

@@ -16,11 +16,6 @@
@interface _ASCollectionPendingState : NSObject @interface _ASCollectionPendingState : NSObject
@property (weak, nonatomic) id <ASCollectionDelegate> delegate; @property (weak, nonatomic) id <ASCollectionDelegate> delegate;
@property (weak, nonatomic) id <ASCollectionDataSource> dataSource; @property (weak, nonatomic) id <ASCollectionDataSource> 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 @end
@implementation _ASCollectionPendingState @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 - (ASCollectionView *)view
{ {
return (ASCollectionView *)[super view]; return (ASCollectionView *)[super view];

View File

@@ -83,6 +83,13 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
return ASSubclassOverridesSelector([ASDisplayNode class], subclass, 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) _ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node)
{ {
ASDN::MutexLocker l(node->_propertyLock); ASDN::MutexLocker l(node->_propertyLock);
@@ -954,8 +961,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
if (self.layerBacked) { if (self.layerBacked) {
[_pendingViewState applyToLayer:self.layer]; [_pendingViewState applyToLayer:self.layer];
} else { } else {
BOOL setFrameDirectly = (_flags.synchronous && !_flags.layerBacked); BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesSettingHandlingForFlags(_flags);
[_pendingViewState applyToView:self.view setFrameDirectly:setFrameDirectly]; [_pendingViewState applyToView:self.view withSpecialPropertiesHandling:specialPropertiesHandling];
} }
[_pendingViewState clearChanges]; [_pendingViewState clearChanges];

View File

@@ -14,18 +14,13 @@
@interface _ASTablePendingState : NSObject @interface _ASTablePendingState : NSObject
@property (weak, nonatomic) id <ASTableDelegate> delegate; @property (weak, nonatomic) id <ASTableDelegate> delegate;
@property (weak, nonatomic) id <ASTableDataSource> dataSource; @property (weak, nonatomic) id <ASTableDataSource> 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 @end
@implementation _ASTablePendingState @implementation _ASTablePendingState
@end @end
@interface ASTableNode () @interface ASTableNode ()
@property (nonatomic) _ASTablePendingState *pendingState; @property (nonatomic, strong) _ASTablePendingState *pendingState;
@end @end
@interface ASTableView () @interface ASTableView ()
@@ -79,7 +74,6 @@
self.pendingState = nil; self.pendingState = nil;
view.asyncDelegate = pendingState.delegate; view.asyncDelegate = pendingState.delegate;
view.asyncDataSource = pendingState.dataSource; 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 - (ASTableView *)view
{ {
return (ASTableView *)[super view]; return (ASTableView *)[super view];

View File

@@ -19,6 +19,7 @@
#import "ASPendingStateController.h" #import "ASPendingStateController.h"
#import "ASThread.h" #import "ASThread.h"
#import "ASTextNode.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. * 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: // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - make sure UIView gets setFrame:
struct ASDisplayNodeFlags flags = _flags; struct ASDisplayNodeFlags flags = _flags;
BOOL setFrameDirectly = flags.synchronous && !flags.layerBacked; BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesSettingHandlingForFlags(flags);
BOOL nodeLoaded = __loaded(self); BOOL nodeLoaded = __loaded(self);
BOOL isMainThread = ASDisplayNodeThreadIsMain(); BOOL isMainThread = ASDisplayNodeThreadIsMain();
if (!setFrameDirectly) { if (!specialPropertiesHandling) {
BOOL canReadProperties = isMainThread || !nodeLoaded; BOOL canReadProperties = isMainThread || !nodeLoaded;
if (canReadProperties) { if (canReadProperties) {
// We don't have to set frame directly, and we can read current properties. // 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) { if (shouldApply) {
CGColorRef oldBackgroundCGColor = _layer.backgroundColor; CGColorRef oldBackgroundCGColor = _layer.backgroundColor;
_layer.backgroundColor = newBackgroundCGColor; _layer.backgroundColor = newBackgroundCGColor;
BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesSettingHandlingForFlags(_flags);
if (specialPropertiesHandling) {
_view.backgroundColor = newBackgroundColor;
}
if (!CGColorEqualToColor(oldBackgroundCGColor, newBackgroundCGColor)) { if (!CGColorEqualToColor(oldBackgroundCGColor, newBackgroundCGColor)) {
[self setNeedsDisplay]; [self setNeedsDisplay];
} }

View File

@@ -25,8 +25,10 @@
@class _ASDisplayLayer; @class _ASDisplayLayer;
@class _ASPendingState; @class _ASPendingState;
@class ASSentinel; @class ASSentinel;
struct ASDisplayNodeFlags;
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector);
BOOL ASDisplayNodeNeedsSpecialPropertiesSettingHandlingForFlags(ASDisplayNodeFlags flags);
/// Get the pending view state for the node, creating one if needed. /// Get the pending view state for the node, creating one if needed.
_ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node); _ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node);

View File

@@ -24,7 +24,7 @@
// Supports all of the properties included in the ASDisplayNodeViewProperties protocol // 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; - (void)applyToLayer:(CALayer *)layer;
+ (_ASPendingState *)pendingViewStateFromLayer:(CALayer *)layer; + (_ASPendingState *)pendingViewStateFromLayer:(CALayer *)layer;

View File

@@ -745,7 +745,7 @@ static UIColor *defaultTintColor = nil;
ASPendingStateApplyMetricsToLayer(self, layer); 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 Use our convenience setters blah here instead of layer.blah
@@ -789,9 +789,16 @@ static UIColor *defaultTintColor = nil;
if (flags.setClipsToBounds) if (flags.setClipsToBounds)
view.clipsToBounds = clipsToBounds; 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; 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) if (flags.setTintColor)
view.tintColor = self.tintColor; view.tintColor = self.tintColor;
@@ -907,8 +914,7 @@ static UIColor *defaultTintColor = nil;
if (flags.setAccessibilityPath) if (flags.setAccessibilityPath)
view.accessibilityPath = accessibilityPath; view.accessibilityPath = accessibilityPath;
// For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - make sure UIView gets setFrame: if (flags.setFrame && specialPropertiesHandling) {
if (flags.setFrame && setFrameDirectly) {
// Frame is only defined when transform is identity because we explicitly diverge from CALayer behavior and define frame without transform // Frame is only defined when transform is identity because we explicitly diverge from CALayer behavior and define frame without transform
#if DEBUG #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. // 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.