From d899f12f70fe9624db126c3027c4c4cdc795e93f Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Sat, 20 Feb 2016 15:24:44 -0800 Subject: [PATCH] [ASCellNode] Layout delegate should not be public as it must not be reset externally. Do not call layout delegate method before the cell node is loaded. This can happen if application code calls -setNeedsLayout on the cell manually, and can confuse UIKit state because we submit an empty batch update call on the next runloop. --- AsyncDisplayKit/ASCellNode+Internal.h | 24 +++++++++++++++++++++++- AsyncDisplayKit/ASCellNode.h | 18 ------------------ AsyncDisplayKit/ASCellNode.m | 5 +++-- AsyncDisplayKit/ASDisplayNode.mm | 1 - AsyncDisplayKit/ASTableView.mm | 1 + 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/AsyncDisplayKit/ASCellNode+Internal.h b/AsyncDisplayKit/ASCellNode+Internal.h index a660e46d80..8dba99cded 100644 --- a/AsyncDisplayKit/ASCellNode+Internal.h +++ b/AsyncDisplayKit/ASCellNode+Internal.h @@ -8,6 +8,28 @@ #import "ASCellNode.h" -@interface ASCellNode (Internal) +@protocol ASCellNodeLayoutDelegate + +/** + * Notifies the delegate that the specified cell node has done a relayout. + * The notification is done on main thread. + * + * This will not be called due to measurement passes before the node has loaded + * its view, even if triggered by -setNeedsLayout, as it is assumed these are + * not relevant to UIKit. Indeed, these calls can cause consistency issues. + * + * @param node A node informing the delegate about the relayout. + * @param sizeChanged `YES` if the node's `calculatedSize` changed during the relayout, `NO` otherwise. + */ +- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged; + +@end + +@interface ASCellNode () + +/* + * A delegate to be notified (on main thread) after a relayout. + */ +@property (nonatomic, weak) id layoutDelegate; @end diff --git a/AsyncDisplayKit/ASCellNode.h b/AsyncDisplayKit/ASCellNode.h index 8788d30015..5460627a0a 100644 --- a/AsyncDisplayKit/ASCellNode.h +++ b/AsyncDisplayKit/ASCellNode.h @@ -14,19 +14,6 @@ NS_ASSUME_NONNULL_BEGIN typedef NSUInteger ASCellNodeAnimation; -@protocol ASCellNodeLayoutDelegate - -/** - * Notifies the delegate that the specified cell node has done a relayout. - * The notification is done on main thread. - * - * @param node A node informing the delegate about the relayout. - * @param sizeChanged `YES` if the node's `calculatedSize` changed during the relayout, `NO` otherwise. - */ -- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged; - -@end - /** * Generic cell node. Subclass this instead of `ASDisplayNode` to use with `ASTableView` and `ASCollectionView`. */ @@ -71,11 +58,6 @@ typedef NSUInteger ASCellNodeAnimation; */ @property (nonatomic, assign) BOOL highlighted; -/* - * A delegate to be notified (on main thread) after a relayout. - */ -@property (nonatomic, weak) id layoutDelegate; - /* * ASCellNode must forward touch events in order for UITableView and UICollectionView tap handling to work. Overriding * these methods (e.g. for highlighting) requires the super method be called. diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index 3e1fccdbc8..b594cfcce2 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "ASCellNode.h" +#import "ASCellNode+Internal.h" #import "ASInternalHelpers.h" #import @@ -27,6 +27,7 @@ @end @implementation ASCellNode +@synthesize layoutDelegate = _layoutDelegate; - (instancetype)init { @@ -97,7 +98,7 @@ CGSize oldSize = self.calculatedSize; [super setNeedsLayout]; - if (_layoutDelegate != nil) { + if (_layoutDelegate != nil && self.isNodeLoaded) { BOOL sizeChanged = !CGSizeEqualToSize(oldSize, self.calculatedSize); ASPerformBlockOnMainThread(^{ [_layoutDelegate nodeDidRelayout:self sizeChanged:sizeChanged]; diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 0aec6ef564..636db362a2 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1882,7 +1882,6 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) - (ASSizeRange)constrainedSizeForCalculatedLayout { - ASDisplayNodeAssertThreadAffinity(self); return _constrainedSize; } diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 923e690236..c6eefd394b 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -10,6 +10,7 @@ #import "ASAssert.h" #import "ASBatchFetching.h" +#import "ASCellNode+Internal.h" #import "ASChangeSetDataController.h" #import "ASDelegateProxy.h" #import "ASDisplayNode+Beta.h"