[ASDisplayNode:UIViewBridge] Refactor setFrame: to simplify logic

This commit is contained in:
Adlai Holler 2016-02-21 10:42:38 -08:00
parent 3ff833c4f5
commit b1c7f47b02

View File

@ -233,37 +233,53 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo
- (void)setFrame:(CGRect)rect - (void)setFrame:(CGRect)rect
{ {
_bridge_prologue_write; _bridge_prologue_write;
BOOL setFrameDirectly = _flags.synchronous && !_flags.layerBacked;
BOOL isMainThread = ASDisplayNodeThreadIsMain(); // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - make sure UIView gets setFrame:
struct ASDisplayNodeFlags flags = _flags;
BOOL setFrameDirectly = flags.synchronous && !flags.layerBacked;
BOOL nodeLoaded = __loaded; BOOL nodeLoaded = __loaded;
if (nodeLoaded && isMainThread && setFrameDirectly) { BOOL isMainThread = ASDisplayNodeThreadIsMain();
// For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - make sure UIView gets setFrame: if (!setFrameDirectly) {
BOOL canReadProperties = isMainThread || !nodeLoaded;
if (canReadProperties) {
// We don't have to set frame directly, and we can read current properties.
// Compute a new bounds and position and set them on self.
CGRect bounds = CGRectZero;
CGPoint position = CGPointZero;
ASBoundsAndPositionForFrame(rect, self.bounds.origin, self.anchorPoint, &bounds, &position);
// Frame is only defined when transform is identity because we explicitly diverge from CALayer behavior and define frame without transform self.bounds = bounds;
#if DEBUG self.position = position;
// 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. } else {
ASDisplayNodeAssert(CATransform3DIsIdentity(self.transform), @"-[ASDisplayNode setFrame:] - self.transform must be identity in order to set the frame property. (From Apple's UIView documentation: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.)"); // We don't have to set frame directly, but we can't read properties.
#endif // Store the frame in our pending state, and it'll get decomposed into
_view.frame = rect; // bounds and position when the pending state is applied.
} else if (!nodeLoaded || (isMainThread && !setFrameDirectly)) { _ASPendingState *pendingState = ASDisplayNodeGetPendingState(self);
/** if (nodeLoaded && !pendingState.hasChanges) {
* Sets a new frame to this node by changing its bounds and position. This method can be safely called even if [[ASPendingStateController sharedInstance] registerNode:self];
* the transform is a non-identity transform, because bounds and position can be set instead of frame. }
* This is NOT called for synchronous nodes (wrapping regular views), which may rely on a [UIView setFrame:] call. pendingState.frame = rect;
* A notable example of the latter is UITableView, which won't resize its internal container if only layer bounds are set. }
*/ } else {
CGRect bounds = CGRectZero; if (nodeLoaded && isMainThread) {
CGPoint position = CGPointZero; // We do have to set frame directly, and we're on main thread with a loaded node.
ASBoundsAndPositionForFrame(rect, self.bounds.origin, self.anchorPoint, &bounds, &position); // Just set the frame on the view.
// NOTE: Frame is only defined when transform is identity because we explicitly diverge from CALayer behavior and define frame without transform.
self.bounds = bounds; #if DEBUG
self.position = position; // 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.
} else if (nodeLoaded && !isMainThread) { ASDisplayNodeAssert(CATransform3DIsIdentity(self.transform), @"-[ASDisplayNode setFrame:] - self.transform must be identity in order to set the frame property. (From Apple's UIView documentation: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.)");
_ASPendingState *pendingState = ASDisplayNodeGetPendingState(self); #endif
if (!pendingState.hasChanges) { _view.frame = rect;
[[ASPendingStateController sharedInstance] registerNode:self]; } else {
// We do have to set frame directly, but either the node isn't loaded or we're on a non-main thread.
// Set the frame on the pending state, and it'll call setFrame: when applied.
_ASPendingState *pendingState = ASDisplayNodeGetPendingState(self);
if (nodeLoaded && !pendingState.hasChanges) {
[[ASPendingStateController sharedInstance] registerNode:self];
}
pendingState.frame = rect;
} }
pendingState.frame = rect;
} }
} }