diff --git a/AsyncDisplayKit/ASCellNode.h b/AsyncDisplayKit/ASCellNode.h index c8523125ad..7d3bdfd583 100644 --- a/AsyncDisplayKit/ASCellNode.h +++ b/AsyncDisplayKit/ASCellNode.h @@ -8,6 +8,22 @@ #import +@class ASCellNode; + +typedef NSUInteger ASCellNodeAnimation; + +@protocol ASCellNodeDelegate + +/** + * 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 suggestedAnimation A constant indicates how the delegate should animate. See UITableViewRowAnimation. + */ +- (void)node:(ASCellNode *)node didRelayoutWithSuggestedAnimation:(ASCellNodeAnimation)animation; +@end /** * Generic cell node. Subclass this instead of `ASDisplayNode` to use with `ASTableView` and `ASCollectionView`. @@ -53,6 +69,18 @@ */ @property (nonatomic, assign) BOOL highlighted; +/* + * A delegate to be notified (on main thread) after a relayout. + */ +@property (nonatomic, weak) id delegate; + +/* + * A constant that is passed to the delegate to indicate how a relayout is to be animated. + * + * @see UITableViewRowAnimation + */ +@property (nonatomic, assign) ASCellNodeAnimation relayoutAnimation; + /* * 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. @@ -62,6 +90,17 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; +/** + * Marks the node as needing layout. Convenience for use whether the view / layer is loaded or not. Safe to call from a background thread. + * + * If this node was measured, calling this method triggers an internal relayout: the calculated layout is invalidated, + * and the supernode is notified or (if this node is the root one) a full measurement pass is executed using the old constrained size. + * The delegate will then be notified on main thread. + * + * This method can be called inside of an animation block (to animate all of the layout changes). + */ +- (void)setNeedsLayout; + @end diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index ae6b3f6761..37df35e864 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -8,6 +8,7 @@ #import "ASCellNode.h" +#import "ASInternalHelpers.h" #import #import #import @@ -27,6 +28,7 @@ // use UITableViewCell defaults _selectionStyle = UITableViewCellSelectionStyleDefault; self.clipsToBounds = YES; + _relayoutAnimation = UITableViewRowAnimationAutomatic; return self; } @@ -49,6 +51,18 @@ ASDisplayNodeAssert(!layerBacked, @"ASCellNode does not support layer-backing."); } +- (void)setNeedsLayout +{ + ASDisplayNodeAssertThreadAffinity(self); + [super setNeedsLayout]; + + if (_delegate != nil) { + ASPerformBlockOnMainThread(^{ + [_delegate node:self didRelayoutWithSuggestedAnimation:_relayoutAnimation]; + }); + } +} + - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { ASDisplayNodeAssertMainThread(); @@ -122,7 +136,7 @@ static const CGFloat kFontSize = 18.0f; _text = [text copy]; _textNode.attributedString = [[NSAttributedString alloc] initWithString:_text attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:kFontSize]}]; - + [self setNeedsLayout]; } @end diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 4cea634226..eca5ba4883 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -223,16 +223,6 @@ */ - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths; -/** - * Relayouts the specified item. - * - * @param indexPath The index path identifying the item to relayout. - * - * @discussion This method must be called from the main thread. The relayout is excuted on main thread. - * The node of the specified item must be updated to cause layout changes before this method is called. - */ -- (void)relayoutItemAtIndexPath:(NSIndexPath *)indexPath; - /** * Moves the item at a specified location to a destination location. * diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index c964b89f3e..7c5c667f2f 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -12,7 +12,6 @@ #import "ASCollectionViewLayoutController.h" #import "ASRangeController.h" #import "ASCollectionDataController.h" -#import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" #import "UICollectionViewLayout+ASConvenience.h" #import "ASInternalHelpers.h" @@ -131,7 +130,7 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark - #pragma mark ASCollectionView. -@interface ASCollectionView () { +@interface ASCollectionView () { _ASCollectionViewProxy *_proxyDataSource; _ASCollectionViewProxy *_proxyDelegate; @@ -269,7 +268,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)reloadDataWithCompletion:(void (^)())completion { ASDisplayNodeAssert(self.asyncDelegate, @"ASCollectionView's asyncDelegate property must be set."); - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ _superIsPendingDataLoad = YES; [super reloadData]; }); @@ -458,14 +457,6 @@ static BOOL _isInterceptedSelector(SEL sel) [_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone]; } -- (void)relayoutItemAtIndexPath:(NSIndexPath *)indexPath -{ - ASDisplayNodeAssertMainThread(); - ASCellNode *node = [self nodeForItemAtIndexPath:indexPath]; - [node setNeedsLayout]; - [super reloadItemsAtIndexPaths:@[indexPath]]; -} - - (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath { ASDisplayNodeAssertMainThread(); @@ -664,6 +655,7 @@ static BOOL _isInterceptedSelector(SEL sel) { ASCellNode *node = [_asyncDataSource collectionView:self nodeForItemAtIndexPath:indexPath]; ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode"); + node.delegate = self; return node; } @@ -895,4 +887,15 @@ static BOOL _isInterceptedSelector(SEL sel) } } +#pragma mark - ASCellNodeDelegate + +- (void)node:(ASCellNode *)node didRelayoutWithSuggestedAnimation:(ASCellNodeAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + NSIndexPath *indexPath = [self indexPathForNode:node]; + if (indexPath != nil) { + [super reloadItemsAtIndexPaths:@[indexPath]]; + } +} + @end diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 27e6fc5a39..ad86f873fb 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -559,14 +559,6 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); * * If this node was measured, calling this method triggers an internal relayout: the calculated layout is invalidated, * and the supernode is notified or (if this node is the root one) a full measurement pass is executed using the old constrained size. - * - * Note: If the relayout causes a change in size of the root node that is attached to a container view, - * the container view must be notified to relayout. - * For ASTableView and ASCollectionView, instead of calling this method directly, - * it is recommended to call -relayoutRowAtIndexPath:withRowAnimation and -relayoutItemAtIndexPath: respectively. - * - * @see [ASTableView relayoutRowAtIndexPath:withRowAnimation:] - * @see [ASCollectionView relayoutItemAtIndexPath:] */ - (void)setNeedsLayout; diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index c299cb0d9c..dde440f2e2 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -57,17 +57,6 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) return ASSubclassOverridesSelector([ASDisplayNode class], subclass, selector); } -void ASDisplayNodePerformBlockOnMainThread(void (^block)()) -{ - if ([NSThread isMainThread]) { - block(); - } else { - dispatch_async(dispatch_get_main_queue(), ^{ - block(); - }); - } -} - void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)()) { ASDisplayNodeCAssertNotNil(block, @"block is required"); @@ -79,7 +68,7 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) // Hold the lock to avoid a race where the node gets loaded while the block is in-flight. ASDN::MutexLocker l(node->_propertyLock); if (node.nodeLoaded) { - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ block(); }); } else { diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index fc5aa93dc6..3de855564b 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -128,7 +128,7 @@ _image = image; ASDN::MutexUnlocker u(_imageLock); - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ [self invalidateCalculatedLayout]; [self setNeedsDisplay]; }); @@ -306,7 +306,7 @@ // If we have an image to display, display it, respecting our recrop flag. if (self.image) { - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ if (recropImmediately) [self displayImmediately]; else @@ -334,7 +334,7 @@ BOOL isCroppingImage = ((boundsSize.width < imageSize.width) || (boundsSize.height < imageSize.height)); // Re-display if we need to. - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ if (self.nodeLoaded && self.contentMode == UIViewContentModeScaleAspectFill && isCroppingImage) [self setNeedsDisplay]; }); diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index 98753ca9a6..d7ea79dc2d 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -214,18 +214,6 @@ */ - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; -/** - * Relayouts the specified row using a given animation effect. - * - * @param indexPath The index path identifying the row to relayout. - * - * @param animation A constant that indicates how the relayout is to be animated. See UITableViewRowAnimation. - * - * @discussion This method must be called from the main thread. The relayout is excuted on main thread. - * The node of the specified row must be updated to cause layout changes before this method is called. - */ -- (void)relayoutRowAtIndexPath:(NSIndexPath *)indexPath withRowAnimation:(UITableViewRowAnimation)animation; - /** * Moves the row at a specified location to a destination location. * diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 06991451e5..fa52ed0030 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -14,7 +14,6 @@ #import "ASCollectionViewLayoutController.h" #import "ASLayoutController.h" #import "ASRangeController.h" -#import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" #import "ASInternalHelpers.h" #import "ASLayout.h" @@ -152,7 +151,7 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark - #pragma mark ASTableView -@interface ASTableView () { +@interface ASTableView () { _ASTableViewProxy *_proxyDataSource; _ASTableViewProxy *_proxyDelegate; @@ -314,7 +313,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)reloadDataWithCompletion:(void (^)())completion { ASDisplayNodeAssert(self.asyncDelegate, @"ASTableView's asyncDelegate property must be set."); - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ [super reloadData]; }); [_dataController reloadDataWithAnimationOptions:UITableViewRowAnimationNone completion:completion]; @@ -457,14 +456,6 @@ static BOOL _isInterceptedSelector(SEL sel) [_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:animation]; } -- (void)relayoutRowAtIndexPath:(NSIndexPath *)indexPath withRowAnimation:(UITableViewRowAnimation)animation -{ - ASDisplayNodeAssertMainThread(); - ASCellNode *node = [self nodeForRowAtIndexPath:indexPath]; - [node setNeedsLayout]; - [super reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:animation]; -} - - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath { ASDisplayNodeAssertMainThread(); @@ -829,6 +820,7 @@ static BOOL _isInterceptedSelector(SEL sel) { ASCellNode *node = [_asyncDataSource tableView:self nodeForRowAtIndexPath:indexPath]; ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode"); + node.delegate = self; return node; } @@ -903,4 +895,15 @@ static BOOL _isInterceptedSelector(SEL sel) } } +#pragma mark - ASCellNodeDelegate + +- (void)node:(ASCellNode *)node didRelayoutWithSuggestedAnimation:(ASCellNodeAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + NSIndexPath *indexPath = [self indexPathForNode:node]; + if (indexPath != nil) { + [super reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:(UITableViewRowAnimation)animation]; + } +} + @end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 7d7e81568c..e9a728c42e 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -14,7 +14,7 @@ #import "ASCellNode.h" #import "ASDisplayNode.h" #import "ASMultidimensionalArrayUtils.h" -#import "ASDisplayNodeInternal.h" +#import "ASInternalHelpers.h" #import "ASLayout.h" //#define LOG(...) NSLog(__VA_ARGS__) @@ -208,7 +208,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads. NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(editingNodes); - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ _completedNodes[kind] = completedNodes; if (completionBlock) { completionBlock(nodes, indexPaths); @@ -227,7 +227,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths); _editingNodes[kind] = editingNodes; - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes[kind], indexPaths); ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_completedNodes[kind], indexPaths); if (completionBlock) { @@ -250,7 +250,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads. NSArray *sectionsForCompleted = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(sections); - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ [_completedNodes[kind] insertObjects:sectionsForCompleted atIndexes:indexSet]; if (completionBlock) { completionBlock(sections, indexSet); @@ -263,7 +263,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; if (indexSet.count == 0) return; [_editingNodes[kind] removeObjectsAtIndexes:indexSet]; - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ [_completedNodes[kind] removeObjectsAtIndexes:indexSet]; if (completionBlock) { completionBlock(indexSet); @@ -512,7 +512,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; LOG(@"endUpdatesWithCompletion - beginning"); [_editingTransactionQueue addOperationWithBlock:^{ - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ // Deep copy _completedNodes to _externalCompletedNodes. // Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate. _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes[ASDataControllerRowNodeKind]); @@ -532,7 +532,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_pendingEditCommandBlocks removeAllObjects]; [_editingTransactionQueue addOperationWithBlock:^{ - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ // Now that the transaction is done, _completedNodes can be accessed externally again. _externalCompletedNodes = nil; @@ -819,7 +819,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // i.e there might be some nodes that were measured using the old constrained size but haven't been added to _completedNodes // (see _layoutNodes:atIndexPaths:withAnimationOptions:). [_editingTransactionQueue addOperationWithBlock:^{ - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ for (NSString *kind in [_completedNodes keyEnumerator]) { [self _relayoutNodesOfKind:kind]; } diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 146b85897f..b511c4d43f 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -10,10 +10,10 @@ #import "ASAssert.h" #import "ASDisplayNodeExtras.h" -#import "ASDisplayNodeInternal.h" #import "ASMultiDimensionalArrayUtils.h" #import "ASRangeHandlerRender.h" #import "ASRangeHandlerPreload.h" +#import "ASInternalHelpers.h" @interface ASRangeController () { BOOL _rangeIsValid; @@ -177,13 +177,13 @@ #pragma mark - ASDataControllerDelegete - (void)dataControllerBeginUpdates:(ASDataController *)dataController { - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ [_delegate rangeControllerBeginUpdates:self]; }); } - (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion { - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ [_delegate rangeController:self endUpdatesAnimated:animated completion:completion]; }); } @@ -196,14 +196,14 @@ [nodeSizes addObject:[NSValue valueWithCGSize:node.calculatedSize]]; }]; - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ _rangeIsValid = NO; [_delegate rangeController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }); } - (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ _rangeIsValid = NO; [_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }); @@ -222,14 +222,14 @@ [sectionNodeSizes addObject:nodeSizes]; }]; - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ _rangeIsValid = NO; [_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; }); } - (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - ASDisplayNodePerformBlockOnMainThread(^{ + ASPerformBlockOnMainThread(^{ _rangeIsValid = NO; [_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; }); diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index 63ef4ed8da..652c1fa890 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -85,7 +85,7 @@ return _getFromLayer(cornerRadius); } --(void)setCornerRadius:(CGFloat)newCornerRadius +- (void)setCornerRadius:(CGFloat)newCornerRadius { _bridge_prologue; _setToLayer(cornerRadius, newCornerRadius); diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index c2daf155be..602da5aa17 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -20,7 +20,6 @@ #import "ASLayoutOptions.h" BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); -void ASDisplayNodePerformBlockOnMainThread(void (^block)()); void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)()); typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index dcd6b38b72..4d1e2ddca8 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -17,6 +17,7 @@ ASDISPLAYNODE_EXTERN_C_BEGIN BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector); BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector); +void ASPerformBlockOnMainThread(void (^block)()); CGFloat ASScreenScale(); diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.mm b/AsyncDisplayKit/Private/ASInternalHelpers.mm index d2337a44b8..89a0e81709 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.mm +++ b/AsyncDisplayKit/Private/ASInternalHelpers.mm @@ -46,6 +46,17 @@ static void ASDispatchOnceOnMainThread(dispatch_once_t *predicate, dispatch_bloc } } +void ASPerformBlockOnMainThread(void (^block)()) +{ + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + block(); + }); + } +} + CGFloat ASScreenScale() { static CGFloat _scale; diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index f6035e03be..847a2629c7 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -185,22 +185,13 @@ static const CGFloat kInnerPadding = 10.0f; - (void)toggleImageEnlargement { _isImageEnlarged = !_isImageEnlarged; + [self setNeedsLayout]; } - (void)toggleNodesSwap { _swappedTextAndImage = !_swappedTextAndImage; - - [UIView animateWithDuration:0.15 animations:^{ - self.alpha = 0; - } completion:^(BOOL finished) { - [self setNeedsLayout]; - [self.view layoutIfNeeded]; - - [UIView animateWithDuration:0.15 animations:^{ - self.alpha = 1; - }]; - }]; + [self setNeedsLayout]; } @end diff --git a/examples/Kittens/Sample/ViewController.m b/examples/Kittens/Sample/ViewController.m index dd2ac41e7b..63df8deed9 100644 --- a/examples/Kittens/Sample/ViewController.m +++ b/examples/Kittens/Sample/ViewController.m @@ -121,7 +121,6 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell // Assume only kitten nodes are selectable (see -tableView:shouldHighlightRowAtIndexPath:). KittenNode *node = (KittenNode *)[_tableView nodeForRowAtIndexPath:indexPath]; [node toggleImageEnlargement]; - [_tableView relayoutRowAtIndexPath:indexPath withRowAnimation:UITableViewRowAnimationAutomatic]; } - (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath