From f13f61c2f07105a153606fc17d61c7fdbb554743 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 7 Oct 2015 21:23:12 +0300 Subject: [PATCH 001/127] Add relayout item/row APIs to ASTableView and ASCollectionView. --- AsyncDisplayKit/ASCollectionView.h | 10 ++++++++++ AsyncDisplayKit/ASCollectionView.mm | 8 ++++++++ AsyncDisplayKit/ASDisplayNode.h | 10 +++++++--- AsyncDisplayKit/ASTableView.h | 12 ++++++++++++ AsyncDisplayKit/ASTableView.mm | 8 ++++++++ examples/Kittens/Sample/KittenNode.mm | 1 - examples/Kittens/Sample/ViewController.m | 3 +-- 7 files changed, 46 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index d8f78c6e13..8b883d8e44 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -191,6 +191,16 @@ */ - (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 4287e39075..9e0dac3ca3 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -406,6 +406,14 @@ 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(); diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 11a138ea4f..7234ccfc03 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -556,9 +556,13 @@ 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 - * (table or collection view, for example), the container view must be notified to relayout. - * For ASTableView and ASCollectionView, an empty batch editing transaction must be triggered to animate to new row / item sizes. + * 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/ASTableView.h b/AsyncDisplayKit/ASTableView.h index b7588f782b..f920d27c96 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -206,6 +206,18 @@ */ - (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 6d83de9a95..d917b230b2 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -455,6 +455,14 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { [_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(); diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 847a2629c7..542ee353b6 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -185,7 +185,6 @@ static const CGFloat kInnerPadding = 10.0f; - (void)toggleImageEnlargement { _isImageEnlarged = !_isImageEnlarged; - [self setNeedsLayout]; } - (void)toggleNodesSwap diff --git a/examples/Kittens/Sample/ViewController.m b/examples/Kittens/Sample/ViewController.m index 7989fe15ce..41630435aa 100644 --- a/examples/Kittens/Sample/ViewController.m +++ b/examples/Kittens/Sample/ViewController.m @@ -118,11 +118,10 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [_tableView deselectRowAtIndexPath:indexPath animated:YES]; - [_tableView beginUpdates]; // Assume only kitten nodes are selectable (see -tableView:shouldHighlightRowAtIndexPath:). KittenNode *node = (KittenNode *)[_tableView nodeForRowAtIndexPath:indexPath]; [node toggleImageEnlargement]; - [_tableView endUpdates]; + [_tableView relayoutRowAtIndexPath:indexPath withRowAnimation:UITableViewRowAnimationAutomatic]; } - (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath From 91f3ba1f49f1a18cf34136faa9739d3f9830b71c Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 12 Oct 2015 21:34:44 +0300 Subject: [PATCH 002/127] Don't invalidateCalculatedLayout when ASTextCellNode's text is changed Because calling -invalidateCalculatedLayout removes the current constrained size and therefore any -setNeedsLayout calls in the future won't have a valid constrained size to proceed. Instead, cell nodes should be relaid-out using the new APIs introduced in ASTableView and ASCollectionView, which are -relayoutRowAtIndexPath:withRowAnimation and -relayoutItemAtIndexPath, respectively. --- AsyncDisplayKit/ASCellNode.m | 1 - 1 file changed, 1 deletion(-) diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index 80bc34598c..ae6b3f6761 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -123,7 +123,6 @@ static const CGFloat kFontSize = 18.0f; _textNode.attributedString = [[NSAttributedString alloc] initWithString:_text attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:kFontSize]}]; - [self invalidateCalculatedLayout]; } @end From 210a89e83cea3ce2613961c2e968389b28011f63 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 12 Oct 2015 22:24:19 +0300 Subject: [PATCH 003/127] Animate subnodes swap in Kitten example. --- examples/Kittens/Sample/KittenNode.mm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 542ee353b6..f6035e03be 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -190,7 +190,17 @@ static const CGFloat kInnerPadding = 10.0f; - (void)toggleNodesSwap { _swappedTextAndImage = !_swappedTextAndImage; - [self setNeedsLayout]; + + [UIView animateWithDuration:0.15 animations:^{ + self.alpha = 0; + } completion:^(BOOL finished) { + [self setNeedsLayout]; + [self.view layoutIfNeeded]; + + [UIView animateWithDuration:0.15 animations:^{ + self.alpha = 1; + }]; + }]; } @end From b09d6a3602c55aad2dffc182f369b78a9a09a753 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Tue, 13 Oct 2015 21:22:03 -0700 Subject: [PATCH 004/127] Demote Photos.framework image requests to UserInitiated quality of service --- AsyncDisplayKit/ASMultiplexImageNode.mm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AsyncDisplayKit/ASMultiplexImageNode.mm b/AsyncDisplayKit/ASMultiplexImageNode.mm index 648c9bb7a0..6fb1f63fed 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.mm +++ b/AsyncDisplayKit/ASMultiplexImageNode.mm @@ -606,6 +606,10 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent } }]; }]; + if (AS_AT_LEAST_IOS8) { + // If you don't set this, iOS will sometimes infer NSQualityOfServiceUserInteractive and promote the entire queue to that level, damaging system responsiveness + newImageRequestOp.qualityOfService = NSQualityOfServiceUserInitiated; + } _phImageRequestOperation = newImageRequestOp; [phImageRequestQueue addOperation:newImageRequestOp]; } From a53c8a3c34fad528017fd2d10300a4ff532365d4 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Wed, 14 Oct 2015 11:00:56 -0700 Subject: [PATCH 005/127] Improve error handling in ASMultiplexImageNode --- AsyncDisplayKit/ASMultiplexImageNode.h | 14 ++++++++++++++ AsyncDisplayKit/ASMultiplexImageNode.mm | 16 +++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/ASMultiplexImageNode.h b/AsyncDisplayKit/ASMultiplexImageNode.h index 302aa20ef5..cf9b283aa8 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.h +++ b/AsyncDisplayKit/ASMultiplexImageNode.h @@ -29,6 +29,20 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) { * Indicates that the best image identifier changed before a download for a worse identifier began. */ ASMultiplexImageNodeErrorCodeBestImageIdentifierChanged, + + /** + * Indicates that the Photos framework returned no image and no error. + * This may happen if the image is in iCloud and the user did not specify `allowsNetworkAccess` + * in their image request. + */ + ASMultiplexImageNodeErrorCodePhotosImageManagerFailedWithoutError, + + /** + * Indicates that the image node could not retrieve the PHAsset for a given asset identifier. + * This typically means that the user has not given Photos framework permissions yet or the asset + * has been removed from the device. + */ + ASMultiplexImageNodeErrorCodePHAssetIsUnavailable }; diff --git a/AsyncDisplayKit/ASMultiplexImageNode.mm b/AsyncDisplayKit/ASMultiplexImageNode.mm index 648c9bb7a0..cc0266d7be 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.mm +++ b/AsyncDisplayKit/ASMultiplexImageNode.mm @@ -577,8 +577,8 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent } if (imageAsset == nil) { - // Error. - completionBlock(nil, nil); + NSError *error = [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodePHAssetIsUnavailable userInfo:nil]; + completionBlock(nil, error); return; } @@ -597,12 +597,18 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent PHImageManager *imageManager = strongSelf.imageManager ?: PHImageManager.defaultManager; [imageManager requestImageForAsset:imageAsset targetSize:request.targetSize contentMode:request.contentMode options:options resultHandler:^(UIImage *image, NSDictionary *info) { + NSError *error = info[PHImageErrorKey]; + + if (error == nil && image == nil) { + error = [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodePhotosImageManagerFailedWithoutError userInfo:nil]; + } + if (NSThread.isMainThread) { dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ - completionBlock(image, info[PHImageErrorKey]); + completionBlock(image, error); }); } else { - completionBlock(image, info[PHImageErrorKey]); + completionBlock(image, error); } }]; }]; @@ -673,7 +679,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent { // If we failed to load, we stop the loading process. // Note that if we bailed before we began downloading because the best identifier changed, we don't bail, but rather just begin loading the best image identifier. - if (error && error.code != ASMultiplexImageNodeErrorCodeBestImageIdentifierChanged) + if (error && !([error.domain isEqual:ASMultiplexImageNodeErrorDomain] && error.code == ASMultiplexImageNodeErrorCodeBestImageIdentifierChanged)) return; OSSpinLockLock(&_imageIdentifiersLock); From 58a880eaeab88653610468115dcc3216920e3c7c Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Wed, 14 Oct 2015 19:27:23 -0700 Subject: [PATCH 006/127] Bump podspec version to 1.9 in preparation for release. --- AsyncDisplayKit.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit.podspec b/AsyncDisplayKit.podspec index 7d14f646a4..7da5b80099 100644 --- a/AsyncDisplayKit.podspec +++ b/AsyncDisplayKit.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |spec| spec.name = 'AsyncDisplayKit' - spec.version = '1.2.2' + spec.version = '1.9' spec.license = { :type => 'BSD' } spec.homepage = 'http://asyncdisplaykit.org' spec.authors = { 'Scott Goodson' => 'scottgoodson@gmail.com', 'Ryan Nystrom' => 'rnystrom@fb.com' } spec.summary = 'Smooth asynchronous user interfaces for iOS apps.' - spec.source = { :git => 'https://github.com/facebook/AsyncDisplayKit.git', :tag => '1.2.2' } + spec.source = { :git => 'https://github.com/facebook/AsyncDisplayKit.git', :tag => '1.9' } spec.documentation_url = 'http://asyncdisplaykit.org/appledoc/' From 8557d65104cbd77678f3ca4919e68d7a05f3a2a8 Mon Sep 17 00:00:00 2001 From: Vitaly Baev Date: Thu, 15 Oct 2015 13:25:12 +0300 Subject: [PATCH 007/127] ASCellNode selected/highlighted properties --- AsyncDisplayKit/ASCellNode.h | 10 ++++++++++ AsyncDisplayKit/ASTableView.mm | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/AsyncDisplayKit/ASCellNode.h b/AsyncDisplayKit/ASCellNode.h index ae061eb6ca..c8523125ad 100644 --- a/AsyncDisplayKit/ASCellNode.h +++ b/AsyncDisplayKit/ASCellNode.h @@ -43,6 +43,16 @@ //@property (atomic, retain) UIColor *backgroundColor; @property (nonatomic) UITableViewCellSelectionStyle selectionStyle; +/* + * A Boolean value that indicates whether the node is selected. + */ +@property (nonatomic, assign) BOOL selected; + +/* + * A Boolean value that indicates whether the node is highlighted. + */ +@property (nonatomic, assign) BOOL highlighted; + /* * 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/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 7c36c775a6..a232007c3e 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -135,6 +135,15 @@ static BOOL _isInterceptedSelector(SEL sel) [super didTransitionToState:state]; } +- (void)setSelected:(BOOL)selected +{ + _node.selected = selected; +} + +- (void)setHighlighted:(BOOL)highlighted { + _node.highlighted = highlighted; +} + @end From 03542c5436fbe8cc548e5834e33bc8f1b6932ed8 Mon Sep 17 00:00:00 2001 From: Vitaly Baev Date: Thu, 15 Oct 2015 20:01:55 +0300 Subject: [PATCH 008/127] Bump the bracket down --- AsyncDisplayKit/ASTableView.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index a232007c3e..20c058d658 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -140,7 +140,8 @@ static BOOL _isInterceptedSelector(SEL sel) _node.selected = selected; } -- (void)setHighlighted:(BOOL)highlighted { +- (void)setHighlighted:(BOOL)highlighted +{ _node.highlighted = highlighted; } From 1a78cd2e66361750bd67dd64a756d224ded8fa42 Mon Sep 17 00:00:00 2001 From: Vitaly Baev Date: Thu, 15 Oct 2015 20:27:42 +0300 Subject: [PATCH 009/127] ASCellNode selected/highlighted properties in ASCollectionView --- AsyncDisplayKit/ASCollectionView.mm | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index b1b29335b3..3c1cc4dc18 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -106,6 +106,28 @@ static BOOL _isInterceptedSelector(SEL sel) @end +#pragma mark - +#pragma mark ASCellNode<->UICollectionViewCell bridging. + +@class _ASCollectionViewCell; + +@interface _ASCollectionViewCell : UICollectionViewCell +@property (nonatomic, weak) ASCellNode *node; +@end + +@implementation _ASCollectionViewCell + +- (void)setSelected:(BOOL)selected +{ + _node.selected = selected; +} + +- (void)setHighlighted:(BOOL)highlighted +{ + _node.highlighted = highlighted; +} + +@end #pragma mark - #pragma mark ASCollectionView. @@ -204,7 +226,7 @@ static BOOL _isInterceptedSelector(SEL sel) self.backgroundColor = [UIColor whiteColor]; - [self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; + [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; return self; } @@ -412,12 +434,13 @@ static BOOL _isInterceptedSelector(SEL sel) { static NSString *reuseIdentifier = @"_ASCollectionViewCell"; - UICollectionViewCell *cell = [self dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; + _ASCollectionViewCell *cell = [self dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - [_rangeController configureContentView:cell.contentView forCellNode:node]; + cell.node = node; + return cell; } From cefbcef8311335092b5cf4dcfdd42a41476ff1c9 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 19:44:24 -0700 Subject: [PATCH 010/127] Documentation updates --- AsyncDisplayKit/Details/ASDataController.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 589d210f9d..ebb725bfb2 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -90,7 +90,6 @@ typedef NSUInteger ASDataControllerAnimationOptions; @end - /** * Controller to layout data in background, and managed data updating. * @@ -177,6 +176,9 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (NSArray *)nodesAtIndexPaths:(NSArray *)indexPaths; -- (NSArray *)completedNodes; // This provides efficient access to the entire _completedNodes multidimensional array. +/** + * Direct access to the nodes that have completed calculation and layout + */ +- (NSArray *)completedNodes; @end From 2956c0cd8a72e95fb76799892ed935a66c89db04 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 20:43:18 -0700 Subject: [PATCH 011/127] Extract flow layout methods into a separate delegate --- AsyncDisplayKit/ASCollectionView.h | 20 +++++++++++++++++-- AsyncDisplayKit/ASCollectionView.mm | 2 +- .../ASCollectionView/Sample/ViewController.m | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index d8f78c6e13..4b2048f6ec 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -342,18 +342,34 @@ */ - (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView; +@end + +@protocol ASCollectionViewDelegateFlowLayout + +@optional + /** * Passthrough support to UICollectionViewDelegateFlowLayout sectionInset behavior. * * @param collectionView The sender. * @param collectionViewLayout The layout object requesting the information. - * #param section The index number of the section whose insets are needed. + * @param section The index number of the section whose insets are needed. * * @discussion The same rules apply as the UICollectionView implementation, but this can also be used without a UICollectionViewFlowLayout. * https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewDelegateFlowLayout_protocol/index.html#//apple_ref/occ/intfm/UICollectionViewDelegateFlowLayout/collectionView:layout:insetForSectionAtIndex: * */ -- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section; +- (UIEdgeInsets)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section; + +/** + * Asks the delegate for the size of the header in the specified section. + */ +- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section; + +/** + * Asks the delegate for the size of the footer in the specified section. + */ +- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section; @end diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 3c1cc4dc18..d2288f39e6 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -628,7 +628,7 @@ static BOOL _isInterceptedSelector(SEL sel) } if (_asyncDelegateImplementsInsetSection) { - sectionInset = [_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section]; + sectionInset = [(id)_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section]; } if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) { diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index f87e26f9d7..a6ceb20f4d 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -13,7 +13,7 @@ #import -@interface ViewController () +@interface ViewController () { ASCollectionView *_collectionView; } @@ -95,7 +95,7 @@ [context completeBatchFetching:YES]; } -- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { +- (UIEdgeInsets)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0); } From 4e32c075d499720a79bf25e4e1c18d0ba7428931 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 20:45:10 -0700 Subject: [PATCH 012/127] Add launch storyboard to collection view example to fix screen size --- examples/ASCollectionView/Sample.xcodeproj/project.pbxproj | 4 ++++ examples/ASCollectionView/Sample/Info.plist | 2 ++ examples/ASCollectionView/Sample/Launchboard.storyboard | 7 +++++++ 3 files changed, 13 insertions(+) create mode 100644 examples/ASCollectionView/Sample/Launchboard.storyboard diff --git a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj index 7f64fc5596..03c3323afb 100644 --- a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 9BA2CEA11BB2579C00D18414 /* Launchboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */; settings = {ASSET_TAGS = (); }; }; AC3C4A641A11F47200143C57 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A631A11F47200143C57 /* main.m */; }; AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A661A11F47200143C57 /* AppDelegate.m */; }; AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A691A11F47200143C57 /* ViewController.m */; }; @@ -16,6 +17,7 @@ /* Begin PBXFileReference section */ 2DBAEE96397BB913350C4530 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Launchboard.storyboard; sourceTree = ""; }; AC3C4A5E1A11F47200143C57 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; AC3C4A621A11F47200143C57 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AC3C4A631A11F47200143C57 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -88,6 +90,7 @@ children = ( AC3C4A621A11F47200143C57 /* Info.plist */, AC3C4A631A11F47200143C57 /* main.m */, + 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */, ); name = "Supporting Files"; sourceTree = ""; @@ -159,6 +162,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9BA2CEA11BB2579C00D18414 /* Launchboard.storyboard in Resources */, AC3C4A8E1A11F80C00143C57 /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/examples/ASCollectionView/Sample/Info.plist b/examples/ASCollectionView/Sample/Info.plist index a3664b0b15..eeb71a8d35 100644 --- a/examples/ASCollectionView/Sample/Info.plist +++ b/examples/ASCollectionView/Sample/Info.plist @@ -26,6 +26,8 @@ 1 LSRequiresIPhoneOS + UILaunchStoryboardName + Launchboard UIRequiredDeviceCapabilities armv7 diff --git a/examples/ASCollectionView/Sample/Launchboard.storyboard b/examples/ASCollectionView/Sample/Launchboard.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/examples/ASCollectionView/Sample/Launchboard.storyboard @@ -0,0 +1,7 @@ + + + + + + + From 0d2332bd82ef3e97e7a5ca5fe35f305e6e9ec7d7 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 21:03:49 -0700 Subject: [PATCH 013/127] Allow ASCollectionView to register for supplementary container --- AsyncDisplayKit/ASCollectionView.h | 2 ++ AsyncDisplayKit/ASCollectionView.mm | 6 ++++++ examples/ASCollectionView/Sample/ViewController.m | 3 +++ 3 files changed, 11 insertions(+) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 4b2048f6ec..a5b0aae9c7 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -119,6 +119,8 @@ */ - (void)reloadData; +- (void)registerSupplementaryViewOfKind:(NSString *)elementKind; + /** * Inserts one or more sections. * diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index d2288f39e6..8148e55d7b 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -379,6 +379,12 @@ static BOOL _isInterceptedSelector(SEL sel) [self performBatchAnimated:YES updates:updates completion:completion]; } +- (void)registerSupplementaryViewOfKind:(NSString *)elementKind +{ + NSString *identifier = [NSString stringWithFormat:@"_ASCollectionSupplementaryView_%@", elementKind]; + [self registerClass:[UIView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier]; +} + - (void)insertSections:(NSIndexSet *)sections { ASDisplayNodeAssertMainThread(); diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index a6ceb20f4d..779f4e6666 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -39,6 +39,9 @@ _collectionView.asyncDelegate = self; _collectionView.backgroundColor = [UIColor whiteColor]; + [_collectionView registerSupplementaryViewOfKind:UICollectionElementKindSectionHeader]; + [_collectionView registerSupplementaryViewOfKind:UICollectionElementKindSectionFooter]; + return self; } From e492770aed2cc2eccfc20557921deb297772ca27 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 21:08:50 -0700 Subject: [PATCH 014/127] Expose collection view supplementary node data source method --- AsyncDisplayKit/ASCollectionView.h | 2 ++ examples/ASCollectionView/Sample/ViewController.m | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index a5b0aae9c7..7d3ea56fc9 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -274,6 +274,8 @@ @optional +- (ASDisplayNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + /** * Provides the constrained size range for measuring the node at the index path. * diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 779f4e6666..7e97f9bb53 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -76,6 +76,17 @@ return node; } +- (ASDisplayNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNode *node = [[ASDisplayNode alloc] init]; + if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { + node.backgroundColor = [UIColor blueColor]; + } else { + node.backgroundColor = [UIColor redColor]; + } + return node; +} + - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return 300; From e9eadac4aee180f76ccadbe86d54ec4219249b38 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 23:41:43 -0700 Subject: [PATCH 015/127] Stub out ASCollectionDataController subclass --- AsyncDisplayKit/ASCollectionView.mm | 43 ++++++++--- AsyncDisplayKit/ASTableView.mm | 4 +- .../Details/ASCollectionDataController.h | 26 +++++++ .../Details/ASCollectionDataController.m | 72 +++++++++++++++++++ AsyncDisplayKit/Details/ASDataController.h | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 6 +- AsyncDisplayKit/Details/ASRangeController.h | 4 +- AsyncDisplayKit/Details/ASRangeController.mm | 4 +- .../Details/ASRangeHandlerRender.mm | 2 +- 9 files changed, 142 insertions(+), 21 deletions(-) create mode 100644 AsyncDisplayKit/Details/ASCollectionDataController.h create mode 100644 AsyncDisplayKit/Details/ASCollectionDataController.m diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 8148e55d7b..4d52ef5e19 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -11,7 +11,7 @@ #import "ASAssert.h" #import "ASCollectionViewLayoutController.h" #import "ASRangeController.h" -#import "ASDataController.h" +#import "ASCollectionDataController.h" #import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" #import "UICollectionViewLayout+ASConvenience.h" @@ -37,9 +37,7 @@ static BOOL _isInterceptedSelector(SEL sel) // handled by ASCollectionView node<->cell machinery sel == @selector(collectionView:cellForItemAtIndexPath:) || sel == @selector(collectionView:layout:sizeForItemAtIndexPath:) || - - // TODO: Supplementary views are currently not supported. An assertion is triggered if the _asyncDataSource implements this method. - // sel == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) || + sel == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) || // handled by ASRangeController sel == @selector(numberOfSectionsInCollectionView:) || @@ -136,7 +134,7 @@ static BOOL _isInterceptedSelector(SEL sel) _ASCollectionViewProxy *_proxyDataSource; _ASCollectionViewProxy *_proxyDelegate; - ASDataController *_dataController; + ASCollectionDataController *_dataController; ASRangeController *_rangeController; ASCollectionViewLayoutController *_layoutController; @@ -201,7 +199,7 @@ static BOOL _isInterceptedSelector(SEL sel) _rangeController.delegate = self; _rangeController.layoutController = _layoutController; - _dataController = [[ASDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; + _dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; _dataController.delegate = _rangeController; _dataController.dataSource = self; @@ -381,8 +379,8 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)registerSupplementaryViewOfKind:(NSString *)elementKind { - NSString *identifier = [NSString stringWithFormat:@"_ASCollectionSupplementaryView_%@", elementKind]; - [self registerClass:[UIView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier]; + [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:elementKind + withReuseIdentifier:[self __reuseIdentifierForKind:elementKind]]; } - (void)insertSections:(NSIndexSet *)sections @@ -433,6 +431,16 @@ static BOOL _isInterceptedSelector(SEL sel) [_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:kASCollectionViewAnimationNone]; } +- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return [_dataController nodeAtIndexPath:indexPath]; +} + +- (NSString *)__reuseIdentifierForKind:(NSString *)kind +{ + return [NSString stringWithFormat:@"_ASCollectionSupplementaryView_%@", kind]; +} + #pragma mark - #pragma mark Intercepted selectors. @@ -443,7 +451,8 @@ static BOOL _isInterceptedSelector(SEL sel) _ASCollectionViewCell *cell = [self dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - [_rangeController configureContentView:cell.contentView forCellNode:node]; + + [_rangeController configureContentView:cell.contentView forNode:node]; cell.node = node; @@ -455,6 +464,15 @@ static BOOL _isInterceptedSelector(SEL sel) return [[_dataController nodeAtIndexPath:indexPath] calculatedSize]; } +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + NSString *identifier = [self __reuseIdentifierForKind:kind]; + UICollectionReusableView *view = [self dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:identifier forIndexPath:indexPath]; + ASDisplayNode *node = [_dataController supplementaryNodeOfKind:kind atIndexPath:indexPath]; + [_rangeController configureContentView:view forNode:node]; + return view; +} + - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { _superIsPendingDataLoad = NO; @@ -613,6 +631,11 @@ static BOOL _isInterceptedSelector(SEL sel) return node; } +- (ASDisplayNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; +} + - (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { ASSizeRange constrainedSize; @@ -659,7 +682,7 @@ static BOOL _isInterceptedSelector(SEL sel) return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; } -- (NSUInteger)dataControllerNumberOfSections:(ASDataController *)dataController { +- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { if ([_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) { return [_asyncDataSource numberOfSectionsInCollectionView:self]; } else { diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 20c058d658..a8dc8c6f9d 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -530,7 +530,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - [_rangeController configureContentView:cell.contentView forCellNode:node]; + [_rangeController configureContentView:cell.contentView forNode:node]; cell.node = node; cell.backgroundColor = node.backgroundColor; @@ -859,7 +859,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { return [_asyncDataSource tableView:self numberOfRowsInSection:section]; } -- (NSUInteger)dataControllerNumberOfSections:(ASDataController *)dataController +- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { if ([_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) { return [_asyncDataSource numberOfSectionsInTableView:self]; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h new file mode 100644 index 0000000000..c623dfbbcf --- /dev/null +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -0,0 +1,26 @@ +// +// ASCollectionDataController.h +// Pods +// +// Created by Levi McCallum on 9/22/15. +// +// + +#import + +@class ASDisplayNode, ASCollectionDataController; +@protocol ASDataControllerSource; + +@protocol ASCollectionDataControllerSource + +- (ASDisplayNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (NSArray *)supplementaryKindsInDataController:(ASCollectionDataController *)dataController; + +@end + +@interface ASCollectionDataController : ASDataController + +- (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +@end \ No newline at end of file diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.m b/AsyncDisplayKit/Details/ASCollectionDataController.m new file mode 100644 index 0000000000..cf772c3b67 --- /dev/null +++ b/AsyncDisplayKit/Details/ASCollectionDataController.m @@ -0,0 +1,72 @@ +// +// ASCollectionDataController.m +// Pods +// +// Created by Levi McCallum on 9/22/15. +// +// + +#import "ASCollectionDataController.h" + +#import "ASAssert.h" + +@interface ASDataController (Subclasses) + +/** + * Queues the given operation until an `endUpdates` synchronize update is completed. + * + * If this method is called outside of a begin/endUpdates batch update, the block is + * executed immediately. + */ +- (void)performEditCommandWithBlock:(void (^)(void))block; + +/** + * Safely locks access to the data source and executes the given block, unlocking once complete. + * + * When `asyncDataFetching` is enabled, the block is executed on a background thread. + */ +- (void)accessDataSourceWithBlock:(dispatch_block_t)block; + +@end + +@implementation ASCollectionDataController { + NSMutableDictionary *_completedSupplementaryNodes; + NSMutableDictionary *_editingSupplementaryNodes; +} + +- (void)initialSupplementaryLoading +{ + [self performEditCommandWithBlock:^{ + ASDisplayNodeAssertMainThread(); + [self accessDataSourceWithBlock:^{ + NSArray *elementKinds = [self.collectionDataSource supplementaryKindsInDataController:self]; + }]; + }]; +} + +- (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + return _completedSupplementaryNodes[kind][indexPath.section][indexPath.item]; +} + +- (id)collectionDataSource +{ + return (id)self.dataSource; +} + +#pragma mark - Internal Data Querying + +- (void)_insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths +{ + if (indexPaths.count == 0) + return; +} + +- (void)_deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths +{ + if (indexPaths.count == 0) + return; +} + +@end diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index ebb725bfb2..39d0b78792 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -40,7 +40,7 @@ typedef NSUInteger ASDataControllerAnimationOptions; /** Fetch the number of sections. */ -- (NSUInteger)dataControllerNumberOfSections:(ASDataController *)dataController; +- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController; /** Lock the data source for data fetching. diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 6a5b361ff0..a963f44ae2 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -239,7 +239,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASDisplayNodeAssertMainThread(); [self accessDataSourceWithBlock:^{ NSMutableArray *indexPaths = [NSMutableArray array]; - NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self]; + NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self]; // insert sections [self insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionNum)] withAnimationOptions:0]; @@ -266,7 +266,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ - NSUInteger sectionCount = [_dataSource dataControllerNumberOfSections:self]; + NSUInteger sectionCount = [_dataSource numberOfSectionsInDataController:self]; NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; [self _populateFromEntireDataSourceWithMutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; @@ -335,7 +335,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (void)_populateFromEntireDataSourceWithMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { - NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self]; + NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self]; for (NSUInteger i = 0; i < sectionNum; i++) { NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i]; diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index 9d5bbe57f0..a7fc3e54df 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -41,9 +41,9 @@ * * @param contentView UIView to add a (sized) node's view to. * - * @param node The ASCellNode to be added. + * @param node The node to be added. Often an ASCellNode. */ -- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node; +- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)node; /** * Delegate and ultimate data source. Must not be nil. diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index b22db853bf..985e6f60cc 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -48,7 +48,7 @@ #pragma mark - View manipulation -- (void)moveNode:(ASCellNode *)node toView:(UIView *)view +- (void)moveNode:(ASDisplayNode *)node toView:(UIView *)view { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssert(node, @"Cannot move a nil node to a view"); @@ -158,7 +158,7 @@ return rangeType == ASLayoutRangeTypeRender; } -- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)cellNode +- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)cellNode { if (cellNode.view.superview == contentView) { // this content view is already correctly configured diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index 0363abba2f..0b6efd8568 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -64,7 +64,7 @@ // This happens if the UITableViewCell is reused after scrolling offscreen. Because the node has already been given the opportunity to display, we do not // proactively re-host it within the workingWindow (improving efficiency). Some time later, it may fall outside the working range, in which case calling // -recursivelyClearContents is critical. If the user scrolls back and it is re-hosted in a UITableViewCell, the content will still exist as it is not cleared - // by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forCellNode:]. + // by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forNode:]. // Condition #4 is suboptimal in some cases, as it is conceivable that memory warnings could trigger clearing content that is inside the working range. However, enforcing the // preservation of this content could result in the app being killed, which is not likely preferable over briefly seeing placeholders in the event the user scrolls backwards. // Nonetheless, future changes to the implementation will likely eliminate this behavior to simplify debugging and extensibility of working range functionality. From ed1b737ead2ef0e795667ed13bebd70c5a4ae4c7 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 23 Sep 2015 07:53:03 -0700 Subject: [PATCH 016/127] Define initial supplementary node creation behavior --- AsyncDisplayKit-Prefix.gcda | Bin 0 -> 656 bytes AsyncDisplayKit/ASTableView.mm | 16 ++-- .../Details/ASCollectionDataController.h | 24 ++++-- .../Details/ASCollectionDataController.m | 77 ++++++++++++------ .../Details/ASDataController+Subclasses.h | 30 +++++++ AsyncDisplayKit/Details/ASDataController.mm | 71 ++++++++-------- Base/ASLog.h | 3 + 7 files changed, 146 insertions(+), 75 deletions(-) create mode 100644 AsyncDisplayKit-Prefix.gcda create mode 100644 AsyncDisplayKit/Details/ASDataController+Subclasses.h diff --git a/AsyncDisplayKit-Prefix.gcda b/AsyncDisplayKit-Prefix.gcda new file mode 100644 index 0000000000000000000000000000000000000000..450c9162de2c4e70a2d89835d0e42d57404c1766 GIT binary patch literal 656 zcmbV|&q@O^5XM^tPogLaf*0{1s0U4}Al?>Tp|q5;sR)V;+3d7kU6Lh9mGvTsuOWEw zLXs(Hw)aZ)|#jS=~_e-)6;bN1NZgt_tA!b0p)mH;x`ZeD;z2 zo{;BhR`6k5^xHy&D#0N(ogQ>WxItwY)fuCCrdJpV)kXT6qvT}eRTAId-BY~0LJr4G z?z6ESsr$-7+Nm`;Fa~gGL=LG#Q)|@^cL1!T&bqC`N>#^aGB?_3Rtfm`JVl%#WBvz_ zpmbr_I*54{!Z7r_Z4VMo8*oOX&eqxxvh5`8bkLxZ2(HL6 #import +#import -@class ASDisplayNode, ASCollectionDataController; +@class ASDisplayNode; +@class ASCollectionDataController; @protocol ASDataControllerSource; @protocol ASCollectionDataControllerSource @@ -17,6 +21,10 @@ - (NSArray *)supplementaryKindsInDataController:(ASCollectionDataController *)dataController; +- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; + +- (NSUInteger)dataController:(ASCollectionDataController *)dataController rowsInSection:(NSUInteger)section supplementaryKind:(NSString *)kind; + @end @interface ASCollectionDataController : ASDataController diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.m b/AsyncDisplayKit/Details/ASCollectionDataController.m index cf772c3b67..8863d0b3b9 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.m +++ b/AsyncDisplayKit/Details/ASCollectionDataController.m @@ -1,33 +1,18 @@ -// -// ASCollectionDataController.m -// Pods -// -// Created by Levi McCallum on 9/22/15. -// -// +/* Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #import "ASCollectionDataController.h" #import "ASAssert.h" - -@interface ASDataController (Subclasses) - -/** - * Queues the given operation until an `endUpdates` synchronize update is completed. - * - * If this method is called outside of a begin/endUpdates batch update, the block is - * executed immediately. - */ -- (void)performEditCommandWithBlock:(void (^)(void))block; - -/** - * Safely locks access to the data source and executes the given block, unlocking once complete. - * - * When `asyncDataFetching` is enabled, the block is executed on a background thread. - */ -- (void)accessDataSourceWithBlock:(dispatch_block_t)block; - -@end +#import "ASMultidimensionalArrayUtils.h" +#import "ASDisplayNode.h" +#import "ASDisplayNodeInternal.h" +#import "ASDataController+Subclasses.h" @implementation ASCollectionDataController { NSMutableDictionary *_completedSupplementaryNodes; @@ -40,9 +25,32 @@ ASDisplayNodeAssertMainThread(); [self accessDataSourceWithBlock:^{ NSArray *elementKinds = [self.collectionDataSource supplementaryKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { + _completedSupplementaryNodes[kind] = [NSMutableArray array]; + _editingSupplementaryNodes[kind] = [NSMutableArray array]; + + NSMutableArray *indexPaths = [NSMutableArray array]; + NSMutableArray *nodes = [NSMutableArray array]; + [self _populateAllNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; + [self batchLayoutNodes:nodes atIndexPaths:indexPaths completion:nil]; + }]; }]; }]; } + +- (void)_populateAllNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths +{ + NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; + for (NSUInteger i = 0; i < sectionCount; i++) { + NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i]; + NSUInteger rowCount = [self.collectionDataSource dataController:self rowsInSection:i supplementaryKind:kind]; + for (NSUInteger j = 0; j < rowCount; j++) { + NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j]; + [indexPaths addObject:indexPath]; + [nodes addObject:[self.collectionDataSource dataController:self supplementaryNodeOfKind:kind atIndexPath:indexPath]]; + } + } +} - (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { @@ -61,6 +69,13 @@ { if (indexPaths.count == 0) return; + NSMutableArray *editingNodes = [self _editingNodesOfKind:kind]; + ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes); + NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(editingNodes); + ASDisplayNodePerformBlockOnMainThread(^{ + _completedSupplementaryNodes[kind] = completedNodes; + // TODO: Notify change + }); } - (void)_deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths @@ -69,4 +84,14 @@ return; } +- (NSMutableArray *)_completedNodesOfKind:(NSString *)kind +{ + return _completedSupplementaryNodes[kind]; +} + +- (NSMutableArray *)_editingNodesOfKind:(NSString *)kind +{ + return _editingSupplementaryNodes[kind]; +} + @end diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h new file mode 100644 index 0000000000..1e14ccc6eb --- /dev/null +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ASDataController.h" + +@interface ASDataController (Subclasses) + +/** + * Queues the given operation until an `endUpdates` synchronize update is completed. + * + * If this method is called outside of a begin/endUpdates batch update, the block is + * executed immediately. + */ +- (void)performEditCommandWithBlock:(void (^)(void))block; + +/** + * Safely locks access to the data source and executes the given block, unlocking once complete. + * + * When `asyncDataFetching` is enabled, the block is executed on a background thread. + */ +- (void)accessDataSourceWithBlock:(dispatch_block_t)block; + +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))block; + +@end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index a963f44ae2..ed79598d55 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -10,15 +10,13 @@ #import +#import "ASLog.h" #import "ASAssert.h" #import "ASCellNode.h" #import "ASDisplayNode.h" #import "ASMultidimensionalArrayUtils.h" #import "ASDisplayNodeInternal.h" -//#define LOG(...) NSLog(__VA_ARGS__) -#define LOG(...) - const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; static void *kASSizingQueueContext = &kASSizingQueueContext; @@ -115,7 +113,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *, NSArray *))block { ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); @@ -152,11 +150,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Block the _editingTransactionQueue from executing a new edit transaction until layout is done & _editingNodes array is updated. dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER); free(nodeBoundSizes); - // Insert finished nodes into data storage - [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; + + block(nodes, indexPaths); } -- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))block { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; @@ -166,10 +164,19 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths withAnimationOptions:animationOptions]; + [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths completion:block]; } } +- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + + [self batchLayoutNodes:nodes atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { + // Insert finished nodes into data storage + [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; + }]; +} + #pragma mark - Internal Data Querying + Editing - (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions @@ -192,7 +199,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { if (indexPaths.count == 0) return; - LOG(@"_deleteNodesAtIndexPaths:%@, full index paths in _editingNodes = %@", indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); + ASLOG(@"_deleteNodesAtIndexPaths:%@, full index paths in _editingNodes = %@", indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths); ASDisplayNodePerformBlockOnMainThread(^{ @@ -275,7 +282,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - reloadData"); + ASLOG(@"Edit Transaction - reloadData"); // Remove everything that existed before the reload, now that we're ready to insert replacements NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes); @@ -369,7 +376,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; _batchUpdateCounter--; if (_batchUpdateCounter == 0) { - LOG(@"endUpdatesWithCompletion - beginning"); + ASLOG(@"endUpdatesWithCompletion - beginning"); [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ @@ -377,16 +384,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate. _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes); - LOG(@"endUpdatesWithCompletion - begin updates call to delegate"); + ASLOG(@"endUpdatesWithCompletion - begin updates call to delegate"); [_delegate dataControllerBeginUpdates:self]; }); }]; // Running these commands may result in blocking on an _editingTransactionQueue operation that started even before -beginUpdates. // Each subsequent command in the queue will also wait on the full asynchronous completion of the prior command's edit transaction. - LOG(@"endUpdatesWithCompletion - %zd blocks to run", _pendingEditCommandBlocks.count); + ASLOG(@"endUpdatesWithCompletion - %zd blocks to run", _pendingEditCommandBlocks.count); [_pendingEditCommandBlocks enumerateObjectsUsingBlock:^(dispatch_block_t block, NSUInteger idx, BOOL *stop) { - LOG(@"endUpdatesWithCompletion - running block #%zd", idx); + ASLOG(@"endUpdatesWithCompletion - running block #%zd", idx); block(); }]; [_pendingEditCommandBlocks removeAllObjects]; @@ -396,7 +403,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Now that the transaction is done, _completedNodes can be accessed externally again. _externalCompletedNodes = nil; - LOG(@"endUpdatesWithCompletion - calling delegate end"); + ASLOG(@"endUpdatesWithCompletion - calling delegate end"); [_delegate dataController:self endUpdatesAnimated:animated completion:completion]; }); }]; @@ -420,7 +427,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - insertSections: %@", indexSet); + ASLOG(@"Edit Command - insertSections: %@", indexSet); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ @@ -432,7 +439,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - insertSections: %@", indexSet); + ASLOG(@"Edit Transaction - insertSections: %@", indexSet); NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:indexSet.count]; for (NSUInteger i = 0; i < indexSet.count; i++) { [sectionArray addObject:[NSMutableArray array]]; @@ -449,12 +456,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - deleteSections: %@", indexSet); + ASLOG(@"Edit Command - deleteSections: %@", indexSet); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements - LOG(@"Edit Transaction - deleteSections: %@", indexSet); + ASLOG(@"Edit Transaction - deleteSections: %@", indexSet); NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, indexSet); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -467,7 +474,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - reloadSections: %@", sections); + ASLOG(@"Edit Command - reloadSections: %@", sections); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -486,7 +493,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, sections); - LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); + ASLOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -501,14 +508,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - moveSection"); + ASLOG(@"Edit Command - moveSection"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements - LOG(@"Edit Transaction - moveSection"); + ASLOG(@"Edit Transaction - moveSection"); NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, [NSIndexSet indexSetWithIndex:section]); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths); @@ -533,7 +540,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - insertRows: %@", indexPaths); + ASLOG(@"Edit Command - insertRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -549,7 +556,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - insertRows: %@", indexPaths); + ASLOG(@"Edit Transaction - insertRows: %@", indexPaths); [self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; }]; @@ -560,7 +567,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - deleteRows: %@", indexPaths); + ASLOG(@"Edit Command - deleteRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -569,7 +576,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - deleteRows: %@", indexPaths); + ASLOG(@"Edit Transaction - deleteRows: %@", indexPaths); [self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions]; }]; }]; @@ -579,7 +586,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - reloadRows: %@", indexPaths); + ASLOG(@"Edit Command - reloadRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -599,7 +606,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - reloadRows: %@", indexPaths); + ASLOG(@"Edit Transaction - reloadRows: %@", indexPaths); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; [self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; @@ -611,7 +618,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - relayoutRows"); + ASLOG(@"Edit Command - relayoutRows"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; void (^relayoutNodesBlock)(NSMutableArray *) = ^void(NSMutableArray *nodes) { @@ -646,11 +653,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - moveRow: %@ > %@", indexPath, newIndexPath); + ASLOG(@"Edit Command - moveRow: %@ > %@", indexPath, newIndexPath); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); + ASLOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, [NSArray arrayWithObject:indexPath]); NSArray *indexPaths = [NSArray arrayWithObject:indexPath]; [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; diff --git a/Base/ASLog.h b/Base/ASLog.h index 4c85bdf99a..c2f3de15c4 100644 --- a/Base/ASLog.h +++ b/Base/ASLog.h @@ -8,6 +8,9 @@ #pragma once +//#define ASLOG(...) NSLog(__VA_ARGS__) +#define ASLOG(...) + #define ASMultiplexImageNodeLogDebug(...) #define ASMultiplexImageNodeCLogDebug(...) From 658b78d55215417605e6491dbaa52ac43faac7d3 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sat, 26 Sep 2015 10:30:24 -0700 Subject: [PATCH 017/127] Use objective-c++ for subclass --- ...ASCollectionDataController.m => ASCollectionDataController.mm} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename AsyncDisplayKit/Details/{ASCollectionDataController.m => ASCollectionDataController.mm} (100%) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.m b/AsyncDisplayKit/Details/ASCollectionDataController.mm similarity index 100% rename from AsyncDisplayKit/Details/ASCollectionDataController.m rename to AsyncDisplayKit/Details/ASCollectionDataController.mm From da7a2a5d481f18d73a9770e7638c3add6422b7e3 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 08:59:40 -0700 Subject: [PATCH 018/127] Further implement data controller support and layout introspection --- AsyncDisplayKit/ASCollectionView.h | 11 +++++-- AsyncDisplayKit/ASCollectionView.mm | 15 +++++++-- .../Details/ASCollectionDataController.h | 9 +++-- .../Details/ASCollectionDataController.mm | 18 +++++++--- .../ASCollectionViewFlowLayoutInspector.h | 31 +++++++++++++++++ .../ASCollectionViewFlowLayoutInspector.m | 33 +++++++++++++++++++ .../Details/ASDataController+Subclasses.h | 5 ++- AsyncDisplayKit/Details/ASDataController.mm | 15 +++++---- .../ASCollectionView/Sample/ViewController.m | 4 +-- 9 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h create mode 100644 AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 7d3ea56fc9..91226a5d64 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -17,7 +17,7 @@ @class ASCellNode; @protocol ASCollectionViewDataSource; @protocol ASCollectionViewDelegate; - +@protocol ASCollectionViewLayoutInspecting; /** * Node-based collection view. @@ -80,6 +80,13 @@ */ @property (nonatomic, assign) CGFloat leadingScreensForBatching; +/** + * Optional introspection object for the collection view's layout. + * + * TODO: Discuss more about this delegate + */ +@property (nonatomic, weak) id layoutDelegate; + /** * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. * The asyncDataSource must be updated to reflect the changes before the update block completes. @@ -119,7 +126,7 @@ */ - (void)reloadData; -- (void)registerSupplementaryViewOfKind:(NSString *)elementKind; +- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind; /** * Inserts one or more sections. diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 4d52ef5e19..fb9dbeae1a 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -16,6 +16,7 @@ #import "ASBatchFetching.h" #import "UICollectionViewLayout+ASConvenience.h" #import "ASInternalHelpers.h" +#import "ASCollectionViewFlowLayoutInspector.h" // FIXME: Temporary nonsense import until method names are finalized and exposed #import "ASDisplayNode+Subclasses.h" @@ -137,6 +138,7 @@ static BOOL _isInterceptedSelector(SEL sel) ASCollectionDataController *_dataController; ASRangeController *_rangeController; ASCollectionViewLayoutController *_layoutController; + ASCollectionViewFlowLayoutInspector *_flowLayoutInspector; BOOL _performingBatchUpdates; NSMutableArray *_batchUpdateBlocks; @@ -202,7 +204,11 @@ static BOOL _isInterceptedSelector(SEL sel) _dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; _dataController.delegate = _rangeController; _dataController.dataSource = self; - + + _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] init]; + // TODO: Implement a better path of falling-back to a flow layout + _flowLayoutInspector.layout = (UICollectionViewFlowLayout *)layout; + _batchContext = [[ASBatchContext alloc] init]; _leadingScreensForBatching = 1.0; @@ -377,7 +383,7 @@ static BOOL _isInterceptedSelector(SEL sel) [self performBatchAnimated:YES updates:updates completion:completion]; } -- (void)registerSupplementaryViewOfKind:(NSString *)elementKind +- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind { [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:[self __reuseIdentifierForKind:elementKind]]; @@ -677,6 +683,11 @@ static BOOL _isInterceptedSelector(SEL sel) return constrainedSize; } +- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [self.layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; +} + - (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section { return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h index c938955608..fddab0eb46 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.h +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -17,9 +17,14 @@ @protocol ASCollectionDataControllerSource -- (ASDisplayNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; -- (NSArray *)supplementaryKindsInDataController:(ASCollectionDataController *)dataController; +/** + The constrained size range for layout. + */ +- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController; - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 8863d0b3b9..243b1b4612 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -14,6 +14,12 @@ #import "ASDisplayNodeInternal.h" #import "ASDataController+Subclasses.h" +@interface ASCollectionDataController () + +- (id)collectionDataSource; + +@end + @implementation ASCollectionDataController { NSMutableDictionary *_completedSupplementaryNodes; NSMutableDictionary *_editingSupplementaryNodes; @@ -24,21 +30,23 @@ [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); [self accessDataSourceWithBlock:^{ - NSArray *elementKinds = [self.collectionDataSource supplementaryKindsInDataController:self]; + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { _completedSupplementaryNodes[kind] = [NSMutableArray array]; _editingSupplementaryNodes[kind] = [NSMutableArray array]; NSMutableArray *indexPaths = [NSMutableArray array]; NSMutableArray *nodes = [NSMutableArray array]; - [self _populateAllNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; - [self batchLayoutNodes:nodes atIndexPaths:indexPaths completion:nil]; + [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; + [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { + return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } completion:nil]; }]; }]; }]; } -- (void)_populateAllNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths +- (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; for (NSUInteger i = 0; i < sectionCount; i++) { @@ -65,6 +73,7 @@ #pragma mark - Internal Data Querying +// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController - (void)_insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths { if (indexPaths.count == 0) @@ -78,6 +87,7 @@ }); } +// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController - (void)_deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths { if (indexPaths.count == 0) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h new file mode 100644 index 0000000000..911a000e5c --- /dev/null +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -0,0 +1,31 @@ +// +// ASCollectionViewFlowLayoutInspector.h +// Pods +// +// Created by Levi McCallum on 9/29/15. +// +// + +#import + +#import + +@class ASCollectionView; + +@protocol ASCollectionViewLayoutInspecting + +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; + +@end + +@interface ASCollectionViewFlowLayoutInspector : NSObject + +@property (nonatomic, weak) UICollectionViewFlowLayout *layout; + +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; + +@end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m new file mode 100644 index 0000000000..274f576c61 --- /dev/null +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -0,0 +1,33 @@ +// +// ASCollectionViewFlowLayoutInspector.m +// Pods +// +// Created by Levi McCallum on 9/29/15. +// +// + +#import "ASCollectionViewFlowLayoutInspector.h" + +#import "ASCollectionView.h" + +@implementation ASCollectionViewFlowLayoutInspector + +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + // TODO: Implement some heuristic that follows the width/height constraints of header and footer supplementary views + return ASSizeRangeMake(CGSizeZero, CGSizeMake(FLT_MAX, FLT_MAX)); +} + +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section +{ + NSUInteger count = 0; + if (self.layout.headerReferenceSize.width > 0 || self.layout.headerReferenceSize.height > 0) { + count++; + } + if (self.layout.footerReferenceSize.width > 0 || self.layout.footerReferenceSize.height > 0) { + count++; + } + return count; +} + +@end diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 1e14ccc6eb..e07bb5d031 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -25,6 +25,9 @@ */ - (void)accessDataSourceWithBlock:(dispatch_block_t)block; -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))block; +/** + * Measure and layout the given nodes in optimized batches, constraining each to a given size. + */ +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; @end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index ed79598d55..844e61706b 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -113,7 +113,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *, NSArray *))block +- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); @@ -129,7 +129,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger k = j; k < j + batchCount; k++) { ASCellNode *node = nodes[k]; if (!node.isNodeLoaded) { - nodeBoundSizes[k] = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPaths[k]]; + nodeBoundSizes[k] = constraintedSizeBlock(indexPaths[k]); } } @@ -151,10 +151,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER); free(nodeBoundSizes); - block(nodes, indexPaths); + completionBlock(nodes, indexPaths); } -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))block +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; @@ -164,14 +164,15 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths completion:block]; + [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constraintedSizeBlock completion:completionBlock]; } } - (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - - [self batchLayoutNodes:nodes atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { + [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { + return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + } completion:^(NSArray *nodes, NSArray *indexPaths) { // Insert finished nodes into data storage [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 7e97f9bb53..885ef3e00f 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -39,8 +39,8 @@ _collectionView.asyncDelegate = self; _collectionView.backgroundColor = [UIColor whiteColor]; - [_collectionView registerSupplementaryViewOfKind:UICollectionElementKindSectionHeader]; - [_collectionView registerSupplementaryViewOfKind:UICollectionElementKindSectionFooter]; + [_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; + [_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter]; return self; } From 5dee3f62cc03e9e584017c067e6c9a7158f2032b Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 09:14:22 -0700 Subject: [PATCH 019/127] Extract number of supplementary sections into the inspector --- AsyncDisplayKit/ASCollectionView.mm | 14 ++++++++++++-- .../Details/ASCollectionDataController.h | 2 +- .../Details/ASCollectionDataController.mm | 2 +- .../Details/ASCollectionViewFlowLayoutInspector.h | 5 +++++ .../Details/ASCollectionViewFlowLayoutInspector.m | 4 ++++ 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index fb9dbeae1a..2b1977bba8 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -683,14 +683,24 @@ static BOOL _isInterceptedSelector(SEL sel) return constrainedSize; } +- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section +{ + return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; +} + - (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { return [self.layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; } -- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section +- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { - return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; + return [self.layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; +} + +- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; +{ + return [self.layoutDelegate collectionView:self numerOfSectionsForSupplementaryKind:kind]; } - (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h index fddab0eb46..08f8775270 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.h +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -28,7 +28,7 @@ - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; -- (NSUInteger)dataController:(ASCollectionDataController *)dataController rowsInSection:(NSUInteger)section supplementaryKind:(NSString *)kind; +- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; @end diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 243b1b4612..1fa63a4999 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -51,7 +51,7 @@ NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; for (NSUInteger i = 0; i < sectionCount; i++) { NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i]; - NSUInteger rowCount = [self.collectionDataSource dataController:self rowsInSection:i supplementaryKind:kind]; + NSUInteger rowCount = [self.collectionDataSource dataController:self supplementaryViewsOfKind:kind inSection:i]; for (NSUInteger j = 0; j < rowCount; j++) { NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j]; [indexPaths addObject:indexPath]; diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 911a000e5c..e28ff0878f 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -16,16 +16,21 @@ - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; + - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; @end @interface ASCollectionViewFlowLayoutInspector : NSObject +@property (nonatomic, weak) ASCollectionView *collectionView; @property (nonatomic, weak) UICollectionViewFlowLayout *layout; - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; + - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index 274f576c61..0a0b1598dc 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -18,6 +18,10 @@ return ASSizeRangeMake(CGSizeZero, CGSizeMake(FLT_MAX, FLT_MAX)); } +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind { + return self.collectionView.numberOfSections; +} + - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { NSUInteger count = 0; From d76544420b3a1df5057e91c4d1d9bb655a2ee3a4 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 14:43:26 -0700 Subject: [PATCH 020/127] Add new files to xcodeproj --- AsyncDisplayKit.xcodeproj/project.pbxproj | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 1a58fabfe9..7c0956d4d3 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -140,6 +140,11 @@ 205F0E211B376416007741D0 /* CGRect+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; 205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; }; 242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; }; + 251B8EF71BBB3D690087C538 /* ASCollectionDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */; settings = {ASSET_TAGS = (); }; }; + 251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; settings = {ASSET_TAGS = (); }; }; + 251B8EF91BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ASSET_TAGS = (); }; }; + 251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; settings = {ASSET_TAGS = (); }; }; + 251B8EFB1BBB3D690087C538 /* ASDataController+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */; settings = {ASSET_TAGS = (); }; }; 2767E9411BB19BD600EA9B77 /* ASViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2767E9421BB19BD600EA9B77 /* ASViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */; settings = {ASSET_TAGS = (); }; }; 2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2911485B1A77147A005D0878 /* ASControlNodeTests.m */; }; @@ -544,6 +549,11 @@ 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CGRect+ASConvenience.h"; sourceTree = ""; }; 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CGRect+ASConvenience.m"; sourceTree = ""; }; 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderTests.m; sourceTree = ""; }; + 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionDataController.h; sourceTree = ""; }; + 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionDataController.mm; sourceTree = ""; }; + 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewFlowLayoutInspector.h; sourceTree = ""; }; + 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewFlowLayoutInspector.m; sourceTree = ""; }; + 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDataController+Subclasses.h"; sourceTree = ""; }; 2911485B1A77147A005D0878 /* ASControlNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNodeTests.m; sourceTree = ""; }; 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutRangeType.h; sourceTree = ""; }; 292C599A1A956527007E5DD6 /* ASRangeHandlerPreload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandlerPreload.h; sourceTree = ""; }; @@ -847,6 +857,11 @@ children = ( CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */, CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */, + 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */, + 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */, + 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */, + 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */, + 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */, 058D09E2195D050800B7D73C /* _ASDisplayLayer.h */, 058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */, 058D09E4195D050800B7D73C /* _ASDisplayView.h */, @@ -1062,6 +1077,7 @@ 054963491A1EA066000F8E56 /* ASBasicImageDownloader.h in Headers */, 2967F9E21AB0A5190072E4AB /* ASBasicImageDownloaderInternal.h in Headers */, 299DA1A91A828D2900162D41 /* ASBatchContext.h in Headers */, + 251B8EF91BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h in Headers */, 044285071BAA63FE00D16268 /* ASBatchFetching.h in Headers */, 055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */, ACF6ED1C1B17843500DA7C62 /* ASCenterLayoutSpec.h in Headers */, @@ -1091,6 +1107,7 @@ ACF6ED221B17843500DA7C62 /* ASInsetLayoutSpec.h in Headers */, ACF6ED4B1B17847A00DA7C62 /* ASInternalHelpers.h in Headers */, ACF6ED241B17843500DA7C62 /* ASLayout.h in Headers */, + 251B8EFB1BBB3D690087C538 /* ASDataController+Subclasses.h in Headers */, ACF6ED2A1B17843500DA7C62 /* ASLayoutable.h in Headers */, 9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */, 464052241A3F83C40061C0BA /* ASLayoutController.h in Headers */, @@ -1125,6 +1142,7 @@ 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */, ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */, 055F1A3419ABD3E3004DAFF1 /* ASTableView.h in Headers */, + 251B8EF71BBB3D690087C538 /* ASCollectionDataController.h in Headers */, 0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */, 058D0A51195D05CB00B7D73C /* ASTextNode.h in Headers */, 058D0A5B195D05DC00B7D73C /* ASTextNodeCoreTextAdditions.h in Headers */, @@ -1474,6 +1492,7 @@ ACF6ED251B17843500DA7C62 /* ASLayout.mm in Sources */, 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */, 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */, + 251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */, ACF6ED271B17843500DA7C62 /* ASLayoutSpec.mm in Sources */, 0516FA411A1563D200B4EBED /* ASMultiplexImageNode.mm in Sources */, 058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */, @@ -1490,6 +1509,7 @@ D785F6631A74327E00291744 /* ASScrollNode.m in Sources */, 058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */, 9C8221971BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */, + 251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */, ACF6ED301B17843500DA7C62 /* ASStackLayoutSpec.mm in Sources */, ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */, ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */, From 4c6585d3b32a96eba0d61a99f77c630a9a2dd921 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 15:10:50 -0700 Subject: [PATCH 021/127] Fix build errors from bad merge --- AsyncDisplayKit/ASCollectionView.mm | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 2b1977bba8..18b39f9ee4 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -437,11 +437,6 @@ static BOOL _isInterceptedSelector(SEL sel) [_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:kASCollectionViewAnimationNone]; } -- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath -{ - return [_dataController nodeAtIndexPath:indexPath]; -} - - (NSString *)__reuseIdentifierForKind:(NSString *)kind { return [NSString stringWithFormat:@"_ASCollectionSupplementaryView_%@", kind]; @@ -700,7 +695,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; { - return [self.layoutDelegate collectionView:self numerOfSectionsForSupplementaryKind:kind]; + return [self.layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; } - (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { From a659ce7bf8945a140c68913dcd8ec2b07bb4c917 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 15:16:30 -0700 Subject: [PATCH 022/127] Cleanup setup of default flow layout inspector --- AsyncDisplayKit/ASCollectionView.mm | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 18b39f9ee4..1e4154852f 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -205,10 +205,6 @@ static BOOL _isInterceptedSelector(SEL sel) _dataController.delegate = _rangeController; _dataController.dataSource = self; - _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] init]; - // TODO: Implement a better path of falling-back to a flow layout - _flowLayoutInspector.layout = (UICollectionViewFlowLayout *)layout; - _batchContext = [[ASBatchContext alloc] init]; _leadingScreensForBatching = 1.0; @@ -228,6 +224,15 @@ static BOOL _isInterceptedSelector(SEL sel) // and should not trigger a relayout. _ignoreMaxSizeChange = CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, CGSizeZero); + // Register the default layout inspector delegate for flow layouts, custom layouts + // will need to roll their own ASCollectionViewLayoutInspecting implementation. + if ([layout asdk_isFlowLayout]) { + _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] init]; + _flowLayoutInspector.layout = (UICollectionViewFlowLayout *)layout; + _flowLayoutInspector.collectionView = self; + _layoutDelegate = _flowLayoutInspector; + } + self.backgroundColor = [UIColor whiteColor]; [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; From 73ddebed81b48d38b29f2f846556b35320b1b765 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 18:01:20 -0700 Subject: [PATCH 023/127] Implement UICollectionViewFlowLayout heuristics in a default inspector --- AsyncDisplayKit/ASCollectionView.mm | 35 ++++---- .../ASCollectionViewFlowLayoutInspector.h | 9 ++ .../ASCollectionViewFlowLayoutInspector.m | 83 ++++++++++++++++--- 3 files changed, 99 insertions(+), 28 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 1e4154852f..a91be86ecd 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -688,21 +688,6 @@ static BOOL _isInterceptedSelector(SEL sel) return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; } -- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath -{ - return [self.layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; -} - -- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section -{ - return [self.layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; -} - -- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; -{ - return [self.layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; -} - - (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { if ([_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) { return [_asyncDataSource numberOfSectionsInCollectionView:self]; @@ -731,8 +716,24 @@ static BOOL _isInterceptedSelector(SEL sel) } } -#pragma mark - -#pragma mark ASRangeControllerDelegate. +#pragma mark - ASCollectionViewDataControllerSource Supplementary view support + +- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; +} + +- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section +{ + return [_layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; +} + +- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; +{ + return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; +} + +#pragma mark - ASRangeControllerDelegate. - (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController { ASDisplayNodeAssertMainThread(); diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index e28ff0878f..962b6bfd18 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -14,10 +14,19 @@ @protocol ASCollectionViewLayoutInspecting +/** + * Asks the inspector + */ - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +/** + * Asks the inspector for the number of supplementary sections in the collection view for the given kind. + */ - (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; +/** + * Asks the inspector for the number of supplementary views for the given kind in the specified section. + */ - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index 0a0b1598dc..d8cbd2fa7e 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -6,32 +6,93 @@ // // +#import + #import "ASCollectionViewFlowLayoutInspector.h" #import "ASCollectionView.h" -@implementation ASCollectionViewFlowLayoutInspector +@implementation ASCollectionViewFlowLayoutInspector { + BOOL _delegateImplementsReferenceSizeForHeader; + BOOL _delegateImplementsReferenceSizeForFooter; +} + +#pragma mark - Accessors + +- (void)setLayout:(UICollectionViewFlowLayout *)layout +{ + _layout = layout; + _delegateImplementsReferenceSizeForHeader = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; + _delegateImplementsReferenceSizeForFooter = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; +} + +#pragma mark - ASCollectionViewLayoutInspecting - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - // TODO: Implement some heuristic that follows the width/height constraints of header and footer supplementary views - return ASSizeRangeMake(CGSizeZero, CGSizeMake(FLT_MAX, FLT_MAX)); + CGSize constrainedSize = CGSizeMake(FLT_MAX, FLT_MAX); + CGSize supplementarySize = [self sizeForSupplementaryViewOfKind:kind inSection:indexPath.section]; + if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) { + constrainedSize.height = supplementarySize.height; + } else { + constrainedSize.width = supplementarySize.width; + } + return ASSizeRangeMake(CGSizeZero, constrainedSize); } -- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind { - return self.collectionView.numberOfSections; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind +{ + return [collectionView.asyncDataSource numberOfSectionsInCollectionView:collectionView]; } - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { - NSUInteger count = 0; - if (self.layout.headerReferenceSize.width > 0 || self.layout.headerReferenceSize.height > 0) { - count++; + return [self layoutHasSupplementaryViewOfKind:kind inSection:section] ? 1 : 0; +} + +#pragma mark - Private helpers + +- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section +{ + if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { + if (_delegateImplementsReferenceSizeForHeader) { + return [[self layoutDelegate] collectionView:_collectionView layout:_layout referenceSizeForHeaderInSection:section]; + } else { + return [self.layout headerReferenceSize]; + } + } else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { + if (_delegateImplementsReferenceSizeForFooter) { + return [[self layoutDelegate] collectionView:_collectionView layout:_layout referenceSizeForFooterInSection:section]; + } else { + return [self.layout footerReferenceSize]; + } + } else { + return CGSizeZero; } - if (self.layout.footerReferenceSize.width > 0 || self.layout.footerReferenceSize.height > 0) { - count++; +} + +- (BOOL)layoutHasSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section +{ + CGSize size = [self sizeForSupplementaryViewOfKind:kind inSection:section]; + if ([self usedLayoutValueForSize:size] > 0) { + return YES; + } else { + return NO; } - return count; +} + +- (CGFloat)usedLayoutValueForSize:(CGSize)size +{ + if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) { + return size.height; + } else { + return size.width; + } +} + +- (id)layoutDelegate +{ + return (id)self.collectionView.delegate; } @end From c9f93cde76d9a227a651c2150dc4fdba230e60a8 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 18:06:00 -0700 Subject: [PATCH 024/127] Update copywright preamble --- .../Details/ASCollectionViewFlowLayoutInspector.h | 14 +++++++------- .../Details/ASCollectionViewFlowLayoutInspector.m | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 962b6bfd18..9adb624c96 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -1,10 +1,10 @@ -// -// ASCollectionViewFlowLayoutInspector.h -// Pods -// -// Created by Levi McCallum on 9/29/15. -// -// +/* Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #import diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index d8cbd2fa7e..f3a825173c 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -1,10 +1,10 @@ -// -// ASCollectionViewFlowLayoutInspector.m -// Pods -// -// Created by Levi McCallum on 9/29/15. -// -// +/* Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #import From bdd786f593ed4232e11ea5ed75d9be243a27c737 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 30 Sep 2015 11:22:17 -0700 Subject: [PATCH 025/127] Document internal storage behaviors of ASDataController --- AsyncDisplayKit/Details/ASDataController.mm | 75 ++++++++++++++------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 844e61706b..0debc7cc4b 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -113,6 +113,33 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +{ + NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; + + // Processing in batches + for (NSUInteger i = 0; i < indexPaths.count; i += blockSize) { + NSRange batchedRange = NSMakeRange(i, MIN(indexPaths.count - i, blockSize)); + NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; + NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; + + [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constraintedSizeBlock completion:completionBlock]; + } +} + +/** + * Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store. + */ +- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { + return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + } completion:^(NSArray *nodes, NSArray *indexPaths) { + // Insert finished nodes into data storage + [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; + }]; +} + - (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); @@ -154,32 +181,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; completionBlock(nodes, indexPaths); } -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock -{ - NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; - - // Processing in batches - for (NSUInteger i = 0; i < indexPaths.count; i += blockSize) { - NSRange batchedRange = NSMakeRange(i, MIN(indexPaths.count - i, blockSize)); - NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; - NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - - [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constraintedSizeBlock completion:completionBlock]; - } -} - -- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { - return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; - } completion:^(NSArray *nodes, NSArray *indexPaths) { - // Insert finished nodes into data storage - [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; - }]; -} - #pragma mark - Internal Data Querying + Editing +/** + * Inserts the specified nodes into the given index paths and notifies the delegate of newly inserted nodes. + * + * @discussion Nodes are first inserted into the editing store, then the completed store is replaced by a deep copy + * of the editing nodes. The delegate is invoked on the main thread. + */ - (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { if (indexPaths.count == 0) @@ -196,6 +205,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }); } +/** + * Removes the specified nodes at the given index paths and notifies the delegate of the nodes removed. + * + * @discussion Nodes are first removed from the editing store then removed from the completed store on the main thread. + * Once the backing stores are consistent, the delegate is invoked on the main thread. + */ - (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { if (indexPaths.count == 0) @@ -211,6 +226,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }); } +/** + * Inserts sections, represented as arrays, into the backing store at the given indicies and notifies the delegate. + * + * @discussion The section arrays are inserted into the editing store, then a deep copy of the sections are inserted + * in the completed store on the main thread. The delegate is invoked on the main thread. + */ - (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { if (indexSet.count == 0) @@ -227,6 +248,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }); } +/** + * Removes sections at the given indicies from the backing store and notifies the delegate. + * + * @discussion Section array are first removed from the editing store, then the associated section in the completed + * store is removed on the main thread. The delegate is invoked on the main thread. + */ - (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { if (indexSet.count == 0) From a3dce24fdccaf9783de5193572023ebaa3678322 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 30 Sep 2015 12:10:48 -0700 Subject: [PATCH 026/127] Document data controller node population --- AsyncDisplayKit/Details/ASDataController.mm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 0debc7cc4b..6b91f4beb1 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -354,6 +354,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } } +/** + * Fetches row nodes and their specified index paths for the provided sections from the data source. + * + * @discussion Results are stored in the passed mutable arrays. + */ - (void)_populateFromDataSourceWithSectionIndexSet:(NSIndexSet *)indexSet mutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { @@ -368,6 +373,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +/** + * Fetches row nodes and their specified index paths for all sections from the data source. + * + * @discussion Results are stored in the passed mutable arrays. + */ - (void)_populateFromEntireDataSourceWithMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self]; From 835f9e99cac65737f19d72e4ad5e840edf1ba582 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 30 Sep 2015 13:25:39 -0700 Subject: [PATCH 027/127] Batch render supplementary views on reload data --- AsyncDisplayKit/ASCollectionView.mm | 20 +++++++--- .../Details/ASCollectionDataController.mm | 39 ++++++++++--------- .../Details/ASDataController+Subclasses.h | 10 +++++ AsyncDisplayKit/Details/ASDataController.mm | 15 +++++++ 4 files changed, 61 insertions(+), 23 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index a91be86ecd..2747d11e11 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -153,6 +153,8 @@ static BOOL _isInterceptedSelector(SEL sel) CGSize _maxSizeForNodesConstrainedSize; BOOL _ignoreMaxSizeChange; + NSMutableArray *_registeredSupplementaryKinds; + /** * If YES, the `UICollectionView` will reload its data on next layout pass so we should not forward any updates to it. @@ -233,6 +235,8 @@ static BOOL _isInterceptedSelector(SEL sel) _layoutDelegate = _flowLayoutInspector; } + _registeredSupplementaryKinds = [NSMutableArray array]; + self.backgroundColor = [UIColor whiteColor]; [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; @@ -390,6 +394,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)registerSupplementaryNodeOfKind:(NSString *)elementKind { + [_registeredSupplementaryKinds addObject:elementKind]; [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:[self __reuseIdentifierForKind:elementKind]]; } @@ -637,11 +642,6 @@ static BOOL _isInterceptedSelector(SEL sel) return node; } -- (ASDisplayNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath -{ - return [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; -} - - (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { ASSizeRange constrainedSize; @@ -718,6 +718,16 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark - ASCollectionViewDataControllerSource Supplementary view support +- (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; +} + +- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController +{ + return _registeredSupplementaryKinds; +} + - (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 1fa63a4999..4d305a358a 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -23,29 +23,32 @@ @implementation ASCollectionDataController { NSMutableDictionary *_completedSupplementaryNodes; NSMutableDictionary *_editingSupplementaryNodes; + + NSMutableDictionary *_pendingNodes; + NSMutableDictionary *_pendingIndexPaths; } -- (void)initialSupplementaryLoading +- (void)prepareForReloadData { - [self performEditCommandWithBlock:^{ - ASDisplayNodeAssertMainThread(); - [self accessDataSourceWithBlock:^{ - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { - _completedSupplementaryNodes[kind] = [NSMutableArray array]; - _editingSupplementaryNodes[kind] = [NSMutableArray array]; - - NSMutableArray *indexPaths = [NSMutableArray array]; - NSMutableArray *nodes = [NSMutableArray array]; - [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; - [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { - return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; - } completion:nil]; - }]; - }]; + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { + NSMutableArray *indexPaths = [NSMutableArray array]; + NSMutableArray *nodes = [NSMutableArray array]; + [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; + _pendingNodes[kind] = nodes; + _pendingIndexPaths[kind] = indexPaths; }]; } - + +- (void)willReloadData +{ + [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + [self batchLayoutNodes:nodes atIndexPaths:_pendingIndexPaths[kind] constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { + return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } completion:nil]; + }]; +} + - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index e07bb5d031..21a6a28f57 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -30,4 +30,14 @@ */ - (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +/** + * An opportunity for a subclass to access the data source before entering into the editing queue + */ +- (void)prepareForReloadData; + +/** + * Subclasses can override this to reload data after the abstract data controller deletes its old data and before it reloads the new. + */ +- (void)willReloadData; + @end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 6b91f4beb1..d47cc072f1 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -308,6 +308,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Measure nodes whose views are loaded before we leave the main thread [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; + + // Allow subclasses to perform setup before going into the edit transaction + [self prepareForReloadData]; [_editingTransactionQueue addOperationWithBlock:^{ ASLOG(@"Edit Transaction - reloadData"); @@ -319,6 +322,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _editingNodes.count)]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; + [self willReloadData]; + // Insert each section NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount]; for (int i = 0; i < sectionCount; i++) { @@ -337,6 +342,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +- (void)prepareForReloadData +{ + // Implemented by subclasses +} + +- (void)willReloadData +{ + // Implemented by subclasses +} + #pragma mark - Data Source Access (Calling _dataSource) - (void)accessDataSourceWithBlock:(dispatch_block_t)block From afda471bd0ba1ff836c4b62fe7dbb78e2fa1c670 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sat, 3 Oct 2015 15:03:09 -0700 Subject: [PATCH 028/127] Clean up interfacing with internal editing/completed stores --- .../Details/ASCollectionDataController.mm | 57 ++---- .../ASCollectionViewFlowLayoutInspector.m | 6 +- .../Details/ASDataController+Subclasses.h | 20 +- AsyncDisplayKit/Details/ASDataController.mm | 176 ++++++++++++------ AsyncDisplayKit/Details/ASRangeController.mm | 6 +- .../ASCollectionView/Sample/ViewController.m | 1 + 6 files changed, 161 insertions(+), 105 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 4d305a358a..2e19c3974e 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -8,6 +8,7 @@ #import "ASCollectionDataController.h" +#import "ASLog.h" #import "ASAssert.h" #import "ASMultidimensionalArrayUtils.h" #import "ASDisplayNode.h" @@ -21,17 +22,18 @@ @end @implementation ASCollectionDataController { - NSMutableDictionary *_completedSupplementaryNodes; - NSMutableDictionary *_editingSupplementaryNodes; - NSMutableDictionary *_pendingNodes; NSMutableDictionary *_pendingIndexPaths; } - (void)prepareForReloadData { + _pendingNodes = [NSMutableDictionary dictionary]; + _pendingIndexPaths = [NSMutableDictionary dictionary]; + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { + ASLOG(@"Populating elements of kind: %@", kind); NSMutableArray *indexPaths = [NSMutableArray array]; NSMutableArray *nodes = [NSMutableArray array]; [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; @@ -43,9 +45,21 @@ - (void)willReloadData { [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + ASLOG(@"Batch layout nodes of kind: %@, (%@)", kind, nodes); + + // Insert each section + NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; + NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount]; + for (int i = 0; i < sectionCount; i++) { + [sections addObject:[[NSMutableArray alloc] init]]; + } + [self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] completion:nil]; + [self batchLayoutNodes:nodes atIndexPaths:_pendingIndexPaths[kind] constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; - } completion:nil]; + } completion:^(NSArray *nodes, NSArray *indexPaths) { + [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; + }]; }]; } @@ -66,7 +80,7 @@ - (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); - return _completedSupplementaryNodes[kind][indexPath.section][indexPath.item]; + return [self internalCompletedNodes][kind][indexPath.section][indexPath.item]; } - (id)collectionDataSource @@ -74,37 +88,4 @@ return (id)self.dataSource; } -#pragma mark - Internal Data Querying - -// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController -- (void)_insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths -{ - if (indexPaths.count == 0) - return; - NSMutableArray *editingNodes = [self _editingNodesOfKind:kind]; - ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes); - NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(editingNodes); - ASDisplayNodePerformBlockOnMainThread(^{ - _completedSupplementaryNodes[kind] = completedNodes; - // TODO: Notify change - }); -} - -// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController -- (void)_deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths -{ - if (indexPaths.count == 0) - return; -} - -- (NSMutableArray *)_completedNodesOfKind:(NSString *)kind -{ - return _completedSupplementaryNodes[kind]; -} - -- (NSMutableArray *)_editingNodesOfKind:(NSString *)kind -{ - return _editingSupplementaryNodes[kind]; -} - @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index f3a825173c..41b39c144a 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -42,7 +42,11 @@ - (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind { - return [collectionView.asyncDataSource numberOfSectionsInCollectionView:collectionView]; + if ([collectionView.asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) { + return [collectionView.asyncDataSource numberOfSectionsInCollectionView:collectionView]; + } else { + return 1; + } } - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 21a6a28f57..84f07bf34d 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -25,11 +25,6 @@ */ - (void)accessDataSourceWithBlock:(dispatch_block_t)block; -/** - * Measure and layout the given nodes in optimized batches, constraining each to a given size. - */ -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; - /** * An opportunity for a subclass to access the data source before entering into the editing queue */ @@ -40,4 +35,19 @@ */ - (void)willReloadData; +- (NSMutableDictionary *)internalCompletedNodes; + +/** + * Measure and layout the given nodes in optimized batches, constraining each to a given size. + */ +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; + +- (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; + +- (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; + +- (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock; + +- (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock; + @end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index d47cc072f1..479ba381dd 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -19,12 +19,16 @@ const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; +NSString * const ASRowNodeKind = @"_ASRowNodeKind"; + static void *kASSizingQueueContext = &kASSizingQueueContext; @interface ASDataController () { NSMutableArray *_externalCompletedNodes; // Main thread only. External data access can immediately query this if available. - NSMutableArray *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable. - NSMutableArray *_editingNodes; // Modified on _editingTransactionQueue only. Updates propogated to _completedNodes. + NSMutableDictionary *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable. + NSMutableDictionary *_editingNodes; // Modified on _editingTransactionQueue only. Updates propogated to _completedNodes. + + NSMutableArray *_pendingEditCommandBlocks; // To be run on the main thread. Handles begin/endUpdates tracking. NSOperationQueue *_editingTransactionQueue; // Serial background queue. Dispatches concurrent layout and manages _editingNodes. @@ -50,9 +54,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; return nil; } - _completedNodes = [NSMutableArray array]; - _editingNodes = [NSMutableArray array]; + _completedNodes = [NSMutableDictionary dictionary]; + _editingNodes = [NSMutableDictionary dictionary]; + _completedNodes[ASRowNodeKind] = [NSMutableArray array]; + _editingNodes[ASRowNodeKind] = [NSMutableArray array]; + _pendingEditCommandBlocks = [NSMutableArray array]; _editingTransactionQueue = [[NSOperationQueue alloc] init]; @@ -178,7 +185,78 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER); free(nodeBoundSizes); - completionBlock(nodes, indexPaths); + if (completionBlock) + completionBlock(nodes, indexPaths); +} + +#pragma mark - External Data Querying + Editing + +- (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +{ + if (indexPaths.count == 0) + return; + + NSMutableArray *editingNodes = _editingNodes[kind]; + ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes); + _editingNodes[kind] = editingNodes; + + // 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(^{ + _completedNodes[kind] = completedNodes; + if (completionBlock) { + completionBlock(nodes, indexPaths); + } + }); +} + +- (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +{ + if (indexPaths.count == 0) + return; + ASLOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForMultidimensionalArray(_editingNodes[kind])); + NSMutableArray *editingNodes = _editingNodes[kind]; + ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths); + _editingNodes[kind] = editingNodes; + + ASDisplayNodePerformBlockOnMainThread(^{ + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes[kind], indexPaths); + ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_completedNodes[kind], indexPaths); + if (completionBlock) { + completionBlock(nodes, indexPaths); + } + }); +} + +- (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock +{ + if (indexSet.count == 0) + return; + [_editingNodes[kind] insertObjects:sections atIndexes:indexSet]; + + // 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(^{ + [_completedNodes[kind] insertObjects:sectionsForCompleted atIndexes:indexSet]; + if (completionBlock) { + completionBlock(sections, indexSet); + } + }); +} + +- (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock +{ + if (indexSet.count == 0) + return; + [_editingNodes[kind] removeObjectsAtIndexes:indexSet]; + ASDisplayNodePerformBlockOnMainThread(^{ + [_completedNodes[kind] removeObjectsAtIndexes:indexSet]; + if (completionBlock) { + completionBlock(indexSet); + } + }); } #pragma mark - Internal Data Querying + Editing @@ -191,18 +269,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - if (indexPaths.count == 0) - return; - ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths, nodes); - - // 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(^{ - _completedNodes = completedNodes; + [self insertNodes:nodes ofKind:ASRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { if (_delegateDidInsertNodes) [_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; - }); + }]; } /** @@ -213,17 +283,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - if (indexPaths.count == 0) - return; - ASLOG(@"_deleteNodesAtIndexPaths:%@, full index paths in _editingNodes = %@", indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); - ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths); - - ASDisplayNodePerformBlockOnMainThread(^{ - NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths); - ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths); + [self deleteNodesOfKind:ASRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { if (_delegateDidDeleteNodes) [_delegate dataController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; - }); + }]; } /** @@ -234,18 +297,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - if (indexSet.count == 0) - return; - [_editingNodes insertObjects:sections atIndexes:indexSet]; - - // 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(^{ - [_completedNodes insertObjects:sectionsForCompleted atIndexes:indexSet]; + [self insertSections:sections ofKind:ASRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) { if (_delegateDidInsertSections) [_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions]; - }); + }]; } /** @@ -256,14 +311,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - if (indexSet.count == 0) - return; - [_editingNodes removeObjectsAtIndexes:indexSet]; - ASDisplayNodePerformBlockOnMainThread(^{ - [_completedNodes removeObjectsAtIndexes:indexSet]; + [self deleteSectionsOfKind:ASRowNodeKind atIndexSet:indexSet completion:^(NSIndexSet *indexSet) { if (_delegateDidDeleteSections) [_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; - }); + }]; } #pragma mark - Initial Load & Full Reload (External API) @@ -316,7 +367,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASLOG(@"Edit Transaction - reloadData"); // Remove everything that existed before the reload, now that we're ready to insert replacements - NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind]); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _editingNodes.count)]; @@ -435,7 +486,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASDisplayNodePerformBlockOnMainThread(^{ // 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); + _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes[ASRowNodeKind]); ASLOG(@"endUpdatesWithCompletion - begin updates call to delegate"); [_delegate dataControllerBeginUpdates:self]; @@ -515,7 +566,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements ASLOG(@"Edit Transaction - deleteSections: %@", indexSet); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, indexSet); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], indexSet); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; @@ -544,9 +595,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, sections); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], sections); - ASLOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); + ASLOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind])); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -570,8 +621,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASLOG(@"Edit Transaction - moveSection"); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, [NSIndexSet indexSetWithIndex:section]); - NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], [NSIndexSet indexSetWithIndex:section]); + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], indexPaths); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; // update the section of indexpaths @@ -696,7 +747,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // (see _layoutNodes:atIndexPaths:withAnimationOptions:). [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ - relayoutNodesBlock(_completedNodes); + relayoutNodesBlock(_completedNodes[ASRowNodeKind]); }); }]; }]; @@ -711,7 +762,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ ASLOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); - NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, [NSArray arrayWithObject:indexPath]); + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], [NSArray arrayWithObject:indexPath]); NSArray *indexPaths = [NSArray arrayWithObject:indexPath]; [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -722,6 +773,13 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +#pragma mark - Data Querying (Subclass API) + +- (NSMutableDictionary *)internalCompletedNodes +{ + return _completedNodes; +} + #pragma mark - Data Querying (External API) - (NSUInteger)numberOfSections @@ -771,7 +829,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (NSArray *)completedNodes { ASDisplayNodeAssertMainThread(); - return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes; + return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes[ASRowNodeKind]; } #pragma mark - Dealloc @@ -779,15 +837,17 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (void)dealloc { ASDisplayNodeAssertMainThread(); - [_completedNodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { - [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { - if (node.isNodeLoaded) { - if (node.layerBacked) { - [node.layer removeFromSuperlayer]; - } else { - [node.view removeFromSuperview]; + [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL * _Nonnull stop) { + [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { + [section enumerateObjectsUsingBlock:^(ASDisplayNode *node, NSUInteger rowIndex, BOOL *stop) { + if (node.isNodeLoaded) { + if (node.layerBacked) { + [node.layer removeFromSuperlayer]; + } else { + [node.view removeFromSuperview]; + } } - } + }]; }]; }]; } diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 985e6f60cc..663f9bea1c 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -158,9 +158,9 @@ return rangeType == ASLayoutRangeTypeRender; } -- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)cellNode +- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)node { - if (cellNode.view.superview == contentView) { + if (node.view.superview == contentView) { // this content view is already correctly configured return; } @@ -170,7 +170,7 @@ [view removeFromSuperview]; } - [self moveNode:cellNode toView:contentView]; + [self moveNode:node toView:contentView]; } diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 885ef3e00f..6f0ca8e5aa 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -33,6 +33,7 @@ UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + layout.headerReferenceSize = CGSizeMake(50.0, 50.0); _collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:YES]; _collectionView.asyncDataSource = self; From f041a273b59128cc09b920142bf84255a10e8938 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sat, 3 Oct 2015 18:51:14 -0700 Subject: [PATCH 029/127] Use rows mutable array for section deletion --- AsyncDisplayKit/Details/ASDataController.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 479ba381dd..ed67bc6f19 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -370,7 +370,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind]); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; - NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _editingNodes.count)]; + NSMutableArray *editingNodes = _editingNodes[ASRowNodeKind]; + NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; [self willReloadData]; From 6e49e1f4d539601e9bb625c7fa161b54f3c23eb9 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sat, 3 Oct 2015 19:18:25 -0700 Subject: [PATCH 030/127] Initialize mutable editing nodes before section insertion --- AsyncDisplayKit/Details/ASDataController.mm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index ed67bc6f19..b1b0e9ce93 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -195,7 +195,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { if (indexPaths.count == 0) return; - + NSMutableArray *editingNodes = _editingNodes[kind]; ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes); _editingNodes[kind] = editingNodes; @@ -233,6 +233,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { if (indexSet.count == 0) return; + + if (_editingNodes[kind] == nil) { + _editingNodes[kind] = [NSMutableArray array]; + } + [_editingNodes[kind] insertObjects:sections atIndexes:indexSet]; // Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads. From e410595f428a470a5abdc6528a4c91fd48cac18e Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 4 Oct 2015 18:05:07 -0700 Subject: [PATCH 031/127] Add functioning header/footer supplementary view support to example --- .../Sample.xcodeproj/project.pbxproj | 6 +++ .../Sample/SupplementaryNode.h | 18 +++++++ .../Sample/SupplementaryNode.m | 54 +++++++++++++++++++ .../ASCollectionView/Sample/ViewController.m | 6 ++- 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 examples/ASCollectionView/Sample/SupplementaryNode.h create mode 100644 examples/ASCollectionView/Sample/SupplementaryNode.m diff --git a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj index 03c3323afb..959990a70e 100644 --- a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 9B92C8811BC17D3000EE46B2 /* SupplementaryNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B92C8801BC17D3000EE46B2 /* SupplementaryNode.m */; settings = {ASSET_TAGS = (); }; }; 9BA2CEA11BB2579C00D18414 /* Launchboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */; settings = {ASSET_TAGS = (); }; }; AC3C4A641A11F47200143C57 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A631A11F47200143C57 /* main.m */; }; AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A661A11F47200143C57 /* AppDelegate.m */; }; @@ -17,6 +18,8 @@ /* Begin PBXFileReference section */ 2DBAEE96397BB913350C4530 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + 9B92C87F1BC17D3000EE46B2 /* SupplementaryNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SupplementaryNode.h; sourceTree = ""; }; + 9B92C8801BC17D3000EE46B2 /* SupplementaryNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SupplementaryNode.m; sourceTree = ""; }; 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Launchboard.storyboard; sourceTree = ""; }; AC3C4A5E1A11F47200143C57 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; AC3C4A621A11F47200143C57 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -78,6 +81,8 @@ AC3C4A691A11F47200143C57 /* ViewController.m */, AC3C4A8D1A11F80C00143C57 /* Images.xcassets */, AC3C4A611A11F47200143C57 /* Supporting Files */, + 9B92C87F1BC17D3000EE46B2 /* SupplementaryNode.h */, + 9B92C8801BC17D3000EE46B2 /* SupplementaryNode.m */, ); indentWidth = 2; path = Sample; @@ -208,6 +213,7 @@ buildActionMask = 2147483647; files = ( AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */, + 9B92C8811BC17D3000EE46B2 /* SupplementaryNode.m in Sources */, AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */, AC3C4A641A11F47200143C57 /* main.m in Sources */, ); diff --git a/examples/ASCollectionView/Sample/SupplementaryNode.h b/examples/ASCollectionView/Sample/SupplementaryNode.h new file mode 100644 index 0000000000..e64ac9e3f6 --- /dev/null +++ b/examples/ASCollectionView/Sample/SupplementaryNode.h @@ -0,0 +1,18 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +@interface SupplementaryNode : ASDisplayNode + +- (instancetype)initWithText:(NSString *)text; + +@end diff --git a/examples/ASCollectionView/Sample/SupplementaryNode.m b/examples/ASCollectionView/Sample/SupplementaryNode.m new file mode 100644 index 0000000000..baddef62c5 --- /dev/null +++ b/examples/ASCollectionView/Sample/SupplementaryNode.m @@ -0,0 +1,54 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "SupplementaryNode.h" + +#import +#import +#import + +static CGFloat kInsets = 15.0; + +@implementation SupplementaryNode { + ASTextNode *_textNode; +} + +- (instancetype)initWithText:(NSString *)text +{ + self = [super init]; + if (self != nil) { + _textNode = [[ASTextNode alloc] init]; + _textNode.attributedString = [[NSAttributedString alloc] initWithString:text + attributes:[self textAttributes]]; + [self addSubnode:_textNode]; + } + return self; +} + +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + ASCenterLayoutSpec *center = [[ASCenterLayoutSpec alloc] init]; + center.child = _textNode; + UIEdgeInsets insets = UIEdgeInsetsMake(kInsets, kInsets, kInsets, kInsets); + return [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:center]; +} + +#pragma mark - Text Formatting + +- (NSDictionary *)textAttributes +{ + return @{ + NSFontAttributeName: [UIFont systemFontOfSize:18.0], + NSForegroundColorAttributeName: [UIColor whiteColor], + }; +} + +@end diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 6f0ca8e5aa..9587bea933 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -12,6 +12,7 @@ #import "ViewController.h" #import +#import "SupplementaryNode.h" @interface ViewController () { @@ -32,8 +33,8 @@ return nil; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; - layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; layout.headerReferenceSize = CGSizeMake(50.0, 50.0); + layout.footerReferenceSize = CGSizeMake(50.0, 50.0); _collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:YES]; _collectionView.asyncDataSource = self; @@ -79,7 +80,8 @@ - (ASDisplayNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - ASDisplayNode *node = [[ASDisplayNode alloc] init]; + NSString *text = [kind isEqualToString:UICollectionElementKindSectionHeader] ? @"Header" : @"Footer"; + SupplementaryNode *node = [[SupplementaryNode alloc] initWithText:text]; if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { node.backgroundColor = [UIColor blueColor]; } else { From aa1842e4848f8262cff01d4c6939c442c5e640b9 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 4 Oct 2015 18:16:07 -0700 Subject: [PATCH 032/127] Remove non-null definitions --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 2e19c3974e..4de5bd3ca6 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -32,7 +32,7 @@ _pendingIndexPaths = [NSMutableDictionary dictionary]; NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { ASLOG(@"Populating elements of kind: %@", kind); NSMutableArray *indexPaths = [NSMutableArray array]; NSMutableArray *nodes = [NSMutableArray array]; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index b1b0e9ce93..b55b62e528 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -843,7 +843,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (void)dealloc { ASDisplayNodeAssertMainThread(); - [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL * _Nonnull stop) { + [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { [section enumerateObjectsUsingBlock:^(ASDisplayNode *node, NSUInteger rowIndex, BOOL *stop) { if (node.isNodeLoaded) { From e4f274aebb8cf1f2f10e0e8f35f741033ee81768 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 4 Oct 2015 18:37:28 -0700 Subject: [PATCH 033/127] Expose completed nodes array by kind --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 2 +- AsyncDisplayKit/Details/ASDataController+Subclasses.h | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 4de5bd3ca6..03b981a2a3 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -80,7 +80,7 @@ - (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); - return [self internalCompletedNodes][kind][indexPath.section][indexPath.item]; + return [self completedNodesOfKind:kind][indexPath.section][indexPath.item]; } - (id)collectionDataSource diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 84f07bf34d..fda6e69cca 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -35,7 +35,7 @@ */ - (void)willReloadData; -- (NSMutableDictionary *)internalCompletedNodes; +- (NSArray *)completedNodesOfKind:(NSString *)kind; /** * Measure and layout the given nodes in optimized batches, constraining each to a given size. diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index b55b62e528..94592578c8 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -781,9 +781,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Data Querying (Subclass API) -- (NSMutableDictionary *)internalCompletedNodes +- (NSArray *)completedNodesOfKind:(NSString *)kind { - return _completedNodes; + return _completedNodes[kind]; } #pragma mark - Data Querying (External API) From 1c2046cdf4be9deea2af1d63866856185125f4bf Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 10:20:50 -0700 Subject: [PATCH 034/127] Localize debugging LOG definition --- AsyncDisplayKit/ASTableView.mm | 16 +++--- .../Details/ASCollectionDataController.mm | 8 +-- AsyncDisplayKit/Details/ASDataController.mm | 52 ++++++++++--------- Base/ASLog.h | 3 -- 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index e5122e71b5..a8dc8c6f9d 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -8,7 +8,6 @@ #import "ASTableView.h" -#import "ASLog.h" #import "ASAssert.h" #import "ASDataController.h" #import "ASCollectionViewLayoutController.h" @@ -22,6 +21,9 @@ // FIXME: Temporary nonsense import until method names are finalized and exposed #import "ASDisplayNode+Subclasses.h" +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + #pragma mark - #pragma mark Proxying. @@ -647,7 +649,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController { ASDisplayNodeAssertMainThread(); - ASLOG(@"--- UITableView beginUpdates"); + LOG(@"--- UITableView beginUpdates"); if (!self.asyncDataSource) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes @@ -663,7 +665,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeController:(ASRangeController *)rangeController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion { ASDisplayNodeAssertMainThread(); - ASLOG(@"--- UITableView endUpdates"); + LOG(@"--- UITableView endUpdates"); if (!self.asyncDataSource) { if (completion) { @@ -749,7 +751,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - ASLOG(@"UITableView insertRows:%ld rows", indexPaths.count); + LOG(@"UITableView insertRows:%ld rows", indexPaths.count); if (!self.asyncDataSource) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes @@ -768,7 +770,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - ASLOG(@"UITableView deleteRows:%ld rows", indexPaths.count); + LOG(@"UITableView deleteRows:%ld rows", indexPaths.count); if (!self.asyncDataSource) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes @@ -787,7 +789,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - ASLOG(@"UITableView insertSections:%@", indexSet); + LOG(@"UITableView insertSections:%@", indexSet); if (!self.asyncDataSource) { @@ -803,7 +805,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - ASLOG(@"UITableView deleteSections:%@", indexSet); + LOG(@"UITableView deleteSections:%@", indexSet); if (!self.asyncDataSource) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 03b981a2a3..d8dd39ec18 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -8,13 +8,15 @@ #import "ASCollectionDataController.h" -#import "ASLog.h" #import "ASAssert.h" #import "ASMultidimensionalArrayUtils.h" #import "ASDisplayNode.h" #import "ASDisplayNodeInternal.h" #import "ASDataController+Subclasses.h" +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + @interface ASCollectionDataController () - (id)collectionDataSource; @@ -33,7 +35,7 @@ NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { - ASLOG(@"Populating elements of kind: %@", kind); + LOG(@"Populating elements of kind: %@", kind); NSMutableArray *indexPaths = [NSMutableArray array]; NSMutableArray *nodes = [NSMutableArray array]; [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; @@ -45,7 +47,7 @@ - (void)willReloadData { [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { - ASLOG(@"Batch layout nodes of kind: %@, (%@)", kind, nodes); + LOG(@"Batch layout nodes of kind: %@, (%@)", kind, nodes); // Insert each section NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 94592578c8..4bc6328c42 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -10,13 +10,15 @@ #import -#import "ASLog.h" #import "ASAssert.h" #import "ASCellNode.h" #import "ASDisplayNode.h" #import "ASMultidimensionalArrayUtils.h" #import "ASDisplayNodeInternal.h" +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; NSString * const ASRowNodeKind = @"_ASRowNodeKind"; @@ -215,7 +217,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { if (indexPaths.count == 0) return; - ASLOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForMultidimensionalArray(_editingNodes[kind])); + LOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForMultidimensionalArray(_editingNodes[kind])); NSMutableArray *editingNodes = _editingNodes[kind]; ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths); _editingNodes[kind] = editingNodes; @@ -369,7 +371,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self prepareForReloadData]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - reloadData"); + LOG(@"Edit Transaction - reloadData"); // Remove everything that existed before the reload, now that we're ready to insert replacements NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind]); @@ -486,7 +488,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; _batchUpdateCounter--; if (_batchUpdateCounter == 0) { - ASLOG(@"endUpdatesWithCompletion - beginning"); + LOG(@"endUpdatesWithCompletion - beginning"); [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ @@ -494,16 +496,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate. _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes[ASRowNodeKind]); - ASLOG(@"endUpdatesWithCompletion - begin updates call to delegate"); + LOG(@"endUpdatesWithCompletion - begin updates call to delegate"); [_delegate dataControllerBeginUpdates:self]; }); }]; // Running these commands may result in blocking on an _editingTransactionQueue operation that started even before -beginUpdates. // Each subsequent command in the queue will also wait on the full asynchronous completion of the prior command's edit transaction. - ASLOG(@"endUpdatesWithCompletion - %zd blocks to run", _pendingEditCommandBlocks.count); + LOG(@"endUpdatesWithCompletion - %zd blocks to run", _pendingEditCommandBlocks.count); [_pendingEditCommandBlocks enumerateObjectsUsingBlock:^(dispatch_block_t block, NSUInteger idx, BOOL *stop) { - ASLOG(@"endUpdatesWithCompletion - running block #%zd", idx); + LOG(@"endUpdatesWithCompletion - running block #%zd", idx); block(); }]; [_pendingEditCommandBlocks removeAllObjects]; @@ -513,7 +515,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Now that the transaction is done, _completedNodes can be accessed externally again. _externalCompletedNodes = nil; - ASLOG(@"endUpdatesWithCompletion - calling delegate end"); + LOG(@"endUpdatesWithCompletion - calling delegate end"); [_delegate dataController:self endUpdatesAnimated:animated completion:completion]; }); }]; @@ -537,7 +539,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - insertSections: %@", indexSet); + LOG(@"Edit Command - insertSections: %@", indexSet); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ @@ -549,7 +551,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - insertSections: %@", indexSet); + LOG(@"Edit Transaction - insertSections: %@", indexSet); NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:indexSet.count]; for (NSUInteger i = 0; i < indexSet.count; i++) { [sectionArray addObject:[NSMutableArray array]]; @@ -566,12 +568,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - deleteSections: %@", indexSet); + LOG(@"Edit Command - deleteSections: %@", indexSet); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements - ASLOG(@"Edit Transaction - deleteSections: %@", indexSet); + LOG(@"Edit Transaction - deleteSections: %@", indexSet); NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], indexSet); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -584,7 +586,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - reloadSections: %@", sections); + LOG(@"Edit Command - reloadSections: %@", sections); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -603,7 +605,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], sections); - ASLOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind])); + LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind])); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -618,14 +620,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - moveSection"); + LOG(@"Edit Command - moveSection"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements - ASLOG(@"Edit Transaction - moveSection"); + LOG(@"Edit Transaction - moveSection"); NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], [NSIndexSet indexSetWithIndex:section]); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], indexPaths); @@ -650,7 +652,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - insertRows: %@", indexPaths); + LOG(@"Edit Command - insertRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -666,7 +668,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - insertRows: %@", indexPaths); + LOG(@"Edit Transaction - insertRows: %@", indexPaths); [self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; }]; @@ -677,7 +679,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - deleteRows: %@", indexPaths); + LOG(@"Edit Command - deleteRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -686,7 +688,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - deleteRows: %@", indexPaths); + LOG(@"Edit Transaction - deleteRows: %@", indexPaths); [self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions]; }]; }]; @@ -696,7 +698,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - reloadRows: %@", indexPaths); + LOG(@"Edit Command - reloadRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -716,7 +718,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - reloadRows: %@", indexPaths); + LOG(@"Edit Transaction - reloadRows: %@", indexPaths); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; [self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; @@ -728,7 +730,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - relayoutRows"); + LOG(@"Edit Command - relayoutRows"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; void (^relayoutNodesBlock)(NSMutableArray *) = ^void(NSMutableArray *nodes) { @@ -763,11 +765,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - moveRow: %@ > %@", indexPath, newIndexPath); + LOG(@"Edit Command - moveRow: %@ > %@", indexPath, newIndexPath); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); + LOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], [NSArray arrayWithObject:indexPath]); NSArray *indexPaths = [NSArray arrayWithObject:indexPath]; [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; diff --git a/Base/ASLog.h b/Base/ASLog.h index c2f3de15c4..4c85bdf99a 100644 --- a/Base/ASLog.h +++ b/Base/ASLog.h @@ -8,9 +8,6 @@ #pragma once -//#define ASLOG(...) NSLog(__VA_ARGS__) -#define ASLOG(...) - #define ASMultiplexImageNodeLogDebug(...) #define ASMultiplexImageNodeCLogDebug(...) From 0086949a65cbec108be9d6ad6064165bcd9ede44 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 10:23:02 -0700 Subject: [PATCH 035/127] Fix typo --- AsyncDisplayKit/Details/ASDataController+Subclasses.h | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index fda6e69cca..6a65036fbc 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -40,7 +40,7 @@ /** * Measure and layout the given nodes in optimized batches, constraining each to a given size. */ -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; - (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 4bc6328c42..e1dff93f81 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -122,7 +122,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; @@ -132,7 +132,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constraintedSizeBlock completion:completionBlock]; + [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constrainedSizeBlock completion:completionBlock]; } } @@ -149,7 +149,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); @@ -165,7 +165,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger k = j; k < j + batchCount; k++) { ASCellNode *node = nodes[k]; if (!node.isNodeLoaded) { - nodeBoundSizes[k] = constraintedSizeBlock(indexPaths[k]); + nodeBoundSizes[k] = constrainedSizeBlock(indexPaths[k]); } } From ecbc29fecab7ebac8e1318a78d367b9c56c0b526 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 10:31:54 -0700 Subject: [PATCH 036/127] Fix travis build --- AsyncDisplayKit.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 7c0956d4d3..6e105257a3 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -211,6 +211,8 @@ 509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; 509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; }; 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; }; + 9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; }; 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1568,6 +1570,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */, + 9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */, B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.m in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */, From b436a0100021cf152ea93878bd64c924e31c4a4d Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 10:35:29 -0700 Subject: [PATCH 037/127] Assert supplementary node kind registration --- AsyncDisplayKit/ASCollectionView.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 2747d11e11..4ee5e2f63f 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -394,6 +394,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)registerSupplementaryNodeOfKind:(NSString *)elementKind { + ASDisplayNodeAssert(elementKind != nil, @"A kind is needed for supplementary node registration"); [_registeredSupplementaryKinds addObject:elementKind]; [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:[self __reuseIdentifierForKind:elementKind]]; From ba4298e6bcf08d2a7d269afd54356c9dc0c4d21b Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 10:36:47 -0700 Subject: [PATCH 038/127] Optimize string concatenation --- AsyncDisplayKit/ASCollectionView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 4ee5e2f63f..b2f40d35e6 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -450,7 +450,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (NSString *)__reuseIdentifierForKind:(NSString *)kind { - return [NSString stringWithFormat:@"_ASCollectionSupplementaryView_%@", kind]; + return [@"_ASCollectionSupplementaryView_" stringByAppendingString:kind]; } #pragma mark - From 34f3065825c0c16f18d2948901b3ac2d2841de83 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 13:56:23 -0700 Subject: [PATCH 039/127] Use local method for introspecting data controller constrained sizes --- .../Details/ASCollectionDataController.mm | 13 ++++- .../Details/ASDataController+Subclasses.h | 24 +++++++- AsyncDisplayKit/Details/ASDataController.h | 2 + AsyncDisplayKit/Details/ASDataController.mm | 55 ++++++++++--------- 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index d8dd39ec18..69f041a62d 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -57,14 +57,21 @@ } [self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] completion:nil]; - [self batchLayoutNodes:nodes atIndexPaths:_pendingIndexPaths[kind] constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { - return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; - } completion:^(NSArray *nodes, NSArray *indexPaths) { + [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:^(NSArray *nodes, NSArray *indexPaths) { [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; }]; }]; } +- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + if ([kind isEqualToString:ASDataControllerRowNodeKind]) { + return [super constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + } else { + return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } +} + - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 6a65036fbc..03e1e32089 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -35,19 +35,39 @@ */ - (void)willReloadData; +/** + * Read only access to the underlying completed nodes of the given kind + */ - (NSArray *)completedNodesOfKind:(NSString *)kind; /** - * Measure and layout the given nodes in optimized batches, constraining each to a given size. + * Measure and layout the given nodes in optimized batches, constraining each to a given size in `constrainedSizeForNodeOfKind:atIndexPath:`. */ -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +- (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +/** + * Provides the size range for a specific node during the layout process. + */ +- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +/** + * Inserts the given nodes of the specified kind into the backing store, calling completion on the main thread when the write finishes. + */ - (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +/** + * Deletes the given nodes of the specified kind in the backing store, calling completion on the main thread when the deletion finishes. + */ - (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +/** + * Inserts the given sections of the specified kind in the backing store, calling completion on the main thread when finished. + */ - (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock; +/** + * Deletes the given sections of the specified kind in the backing store, calling completion on the main thread when finished. + */ - (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock; @end diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 39d0b78792..ea4e4694a3 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -14,6 +14,8 @@ @class ASCellNode; @class ASDataController; +FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind; + typedef NSUInteger ASDataControllerAnimationOptions; /** diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index e1dff93f81..0eab204e35 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -21,7 +21,7 @@ const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; -NSString * const ASRowNodeKind = @"_ASRowNodeKind"; +NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; static void *kASSizingQueueContext = &kASSizingQueueContext; @@ -59,8 +59,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; _completedNodes = [NSMutableDictionary dictionary]; _editingNodes = [NSMutableDictionary dictionary]; - _completedNodes[ASRowNodeKind] = [NSMutableArray array]; - _editingNodes[ASRowNodeKind] = [NSMutableArray array]; + _completedNodes[ASDataControllerRowNodeKind] = [NSMutableArray array]; + _editingNodes[ASDataControllerRowNodeKind] = [NSMutableArray array]; _pendingEditCommandBlocks = [NSMutableArray array]; @@ -115,14 +115,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, __unused BOOL * stop) { ASCellNode *node = nodes[idx]; if (node.isNodeLoaded) { - ASSizeRange constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath]; [node measureWithSizeRange:constrainedSize]; node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); } }]; } -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +- (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; @@ -132,7 +132,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constrainedSizeBlock completion:completionBlock]; + [self _layoutNodes:batchedNodes ofKind:kind atIndexPaths:batchedIndexPaths completion:completionBlock]; } } @@ -141,15 +141,13 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { - return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; - } completion:^(NSArray *nodes, NSArray *indexPaths) { + [self batchLayoutNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { // Insert finished nodes into data storage [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; } -- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +- (void)_layoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); @@ -165,7 +163,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger k = j; k < j + batchCount; k++) { ASCellNode *node = nodes[k]; if (!node.isNodeLoaded) { - nodeBoundSizes[k] = constrainedSizeBlock(indexPaths[k]); + nodeBoundSizes[k] = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPaths[k]]; } } @@ -191,6 +189,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; completionBlock(nodes, indexPaths); } +- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; +} + #pragma mark - External Data Querying + Editing - (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock @@ -276,7 +279,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self insertNodes:nodes ofKind:ASRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { + [self insertNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { if (_delegateDidInsertNodes) [_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; @@ -290,7 +293,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self deleteNodesOfKind:ASRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { + [self deleteNodesOfKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { if (_delegateDidDeleteNodes) [_delegate dataController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; @@ -304,7 +307,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self insertSections:sections ofKind:ASRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) { + [self insertSections:sections ofKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) { if (_delegateDidInsertSections) [_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions]; }]; @@ -318,7 +321,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self deleteSectionsOfKind:ASRowNodeKind atIndexSet:indexSet completion:^(NSIndexSet *indexSet) { + [self deleteSectionsOfKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:^(NSIndexSet *indexSet) { if (_delegateDidDeleteSections) [_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; }]; @@ -374,10 +377,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; LOG(@"Edit Transaction - reloadData"); // Remove everything that existed before the reload, now that we're ready to insert replacements - NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind]); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind]); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; - NSMutableArray *editingNodes = _editingNodes[ASRowNodeKind]; + NSMutableArray *editingNodes = _editingNodes[ASDataControllerRowNodeKind]; NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; @@ -494,7 +497,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASDisplayNodePerformBlockOnMainThread(^{ // 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[ASRowNodeKind]); + _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes[ASDataControllerRowNodeKind]); LOG(@"endUpdatesWithCompletion - begin updates call to delegate"); [_delegate dataControllerBeginUpdates:self]; @@ -574,7 +577,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements LOG(@"Edit Transaction - deleteSections: %@", indexSet); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], indexSet); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], indexSet); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; @@ -603,9 +606,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], sections); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections); - LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind])); + LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind])); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -629,8 +632,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; LOG(@"Edit Transaction - moveSection"); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], [NSIndexSet indexSetWithIndex:section]); - NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], indexPaths); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], [NSIndexSet indexSetWithIndex:section]); + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASDataControllerRowNodeKind], indexPaths); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; // update the section of indexpaths @@ -755,7 +758,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // (see _layoutNodes:atIndexPaths:withAnimationOptions:). [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ - relayoutNodesBlock(_completedNodes[ASRowNodeKind]); + relayoutNodesBlock(_completedNodes[ASDataControllerRowNodeKind]); }); }]; }]; @@ -770,7 +773,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ LOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); - NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], [NSArray arrayWithObject:indexPath]); + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASDataControllerRowNodeKind], [NSArray arrayWithObject:indexPath]); NSArray *indexPaths = [NSArray arrayWithObject:indexPath]; [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -837,7 +840,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (NSArray *)completedNodes { ASDisplayNodeAssertMainThread(); - return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes[ASRowNodeKind]; + return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes[ASDataControllerRowNodeKind]; } #pragma mark - Dealloc From 8efca60bac6167e56eadb5aea719a09d3ba7c37d Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:26:23 -0700 Subject: [PATCH 040/127] Handle initial sizing delegate implementation in flow layout inspector --- AsyncDisplayKit/ASCollectionView.mm | 26 ++++++++++++++----- .../ASCollectionViewFlowLayoutInspector.h | 4 ++- .../ASCollectionViewFlowLayoutInspector.m | 26 ++++++++++++++----- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index b2f40d35e6..3dff9423af 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -228,13 +228,8 @@ static BOOL _isInterceptedSelector(SEL sel) // Register the default layout inspector delegate for flow layouts, custom layouts // will need to roll their own ASCollectionViewLayoutInspecting implementation. - if ([layout asdk_isFlowLayout]) { - _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] init]; - _flowLayoutInspector.layout = (UICollectionViewFlowLayout *)layout; - _flowLayoutInspector.collectionView = self; - _layoutDelegate = _flowLayoutInspector; - } - + _layoutDelegate = [self flowLayoutInspector]; + _registeredSupplementaryKinds = [NSMutableArray array]; self.backgroundColor = [UIColor whiteColor]; @@ -252,6 +247,21 @@ static BOOL _isInterceptedSelector(SEL sel) super.dataSource = nil; } +/** + * A layout inspector implementation specific for the sizing behavior of UICollectionViewFlowLayouts + * + * @discussion Will return nil if the current layout is not a flow layout + */ +- (ASCollectionViewFlowLayoutInspector *)flowLayoutInspector +{ + if (_flowLayoutInspector == nil) { + UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout; + _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:self + flowLayout:layout]; + } + return _flowLayoutInspector; +} + #pragma mark - #pragma mark Overrides. @@ -326,6 +336,8 @@ static BOOL _isInterceptedSelector(SEL sel) super.delegate = (id)_proxyDelegate; _asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); } + + _flowLayoutInspector.collectionView = self; } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 9adb624c96..94d65e2117 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -33,8 +33,10 @@ @interface ASCollectionViewFlowLayoutInspector : NSObject -@property (nonatomic, weak) ASCollectionView *collectionView; @property (nonatomic, weak) UICollectionViewFlowLayout *layout; +@property (nonatomic, weak) ASCollectionView *collectionView; + +- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout; - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index 41b39c144a..4c486d5d00 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -19,11 +19,25 @@ #pragma mark - Accessors -- (void)setLayout:(UICollectionViewFlowLayout *)layout +- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout { - _layout = layout; - _delegateImplementsReferenceSizeForHeader = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; - _delegateImplementsReferenceSizeForFooter = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; + if (flowLayout == nil) { + return nil; + } + + self = [super init]; + if (self != nil) { + self.collectionView = collectionView; + _layout = flowLayout; + } + return self; +} + +- (void)setCollectionView:(ASCollectionView *)collectionView +{ + _collectionView = collectionView; + _delegateImplementsReferenceSizeForHeader = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; + _delegateImplementsReferenceSizeForFooter = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; } #pragma mark - ASCollectionViewLayoutInspecting @@ -94,9 +108,9 @@ } } -- (id)layoutDelegate +- (id)layoutDelegate { - return (id)self.collectionView.delegate; + return (id)_collectionView.asyncDelegate; } @end From 2958c848aa747ed467a4cb755c3b9d5ff5073cc5 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:27:17 -0700 Subject: [PATCH 041/127] Asset presence of async delegate before accessing layout delegate --- AsyncDisplayKit/ASCollectionView.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 3dff9423af..8fba37b7b8 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -743,16 +743,19 @@ static BOOL _isInterceptedSelector(SEL sel) - (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { + ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; } - (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { + ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); return [_layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; } - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; { + ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; } From 769a67569c3a965fde662f4122d0c23fb855b292 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:28:11 -0700 Subject: [PATCH 042/127] Batch layout as display nodes --- AsyncDisplayKit/Details/ASDataController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 0eab204e35..458166424f 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -169,7 +169,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; dispatch_group_async(layoutGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (NSUInteger k = j; k < j + batchCount; k++) { - ASCellNode *node = nodes[k]; + ASDisplayNode *node = nodes[k]; // Only measure nodes whose views aren't loaded, since we're in the background. // We should already have measured loaded nodes before we left the main thread, using _layoutNodesWithMainThreadAffinity: if (!node.isNodeLoaded) { From 5839e5bf3eabfb24466a13fc773af9b2439c7646 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:28:42 -0700 Subject: [PATCH 043/127] Assert supplementary node validity before data controller use --- AsyncDisplayKit/ASCollectionView.mm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 8fba37b7b8..bfd943925a 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -733,7 +733,10 @@ static BOOL _isInterceptedSelector(SEL sel) - (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - return [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; + ASDisplayNode *node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; + ASDisplayNodeAssert(node != nil, @"A node must be returned for a supplementary node"); + ASDisplayNodeAssert(!node.nodeLoaded, @"The supplementary node must not be loaded"); + return node; } - (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController From b58649fdcb0e010a22cdc27430ee64d994f36271 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:28:54 -0700 Subject: [PATCH 044/127] Clean up supplementary nodes in data controller store on each reload --- .../Details/ASCollectionDataController.mm | 10 +++++++++- .../Details/ASDataController+Subclasses.h | 12 ++++++++++++ AsyncDisplayKit/Details/ASDataController.mm | 10 ++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 69f041a62d..1f94ee5e5f 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -47,7 +47,13 @@ - (void)willReloadData { [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { - LOG(@"Batch layout nodes of kind: %@, (%@)", kind, nodes); + // Remove everything that existed before the reload, now that we're ready to insert replacements + NSArray *indexPaths = [self indexPathsForEditingNodesOfKind:kind]; + [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; + + NSArray *editingNodes = [self editingNodesOfKind:kind]; + NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)]; + [self deleteSectionsOfKind:kind atIndexSet:indexSet completion:nil]; // Insert each section NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; @@ -60,6 +66,8 @@ [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:^(NSArray *nodes, NSArray *indexPaths) { [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; }]; + _pendingNodes[kind] = [NSArray array]; + _pendingIndexPaths[kind] = [NSArray array]; }]; } diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 03e1e32089..d1ae9507cd 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -32,9 +32,21 @@ /** * Subclasses can override this to reload data after the abstract data controller deletes its old data and before it reloads the new. + * + * @discussion Invoked on the editing transaction queue. */ - (void)willReloadData; +/** + * Provides a collection of index paths for nodes of the given kind that are currently in the editing store + */ +- (NSArray *)indexPathsForEditingNodesOfKind:(NSString *)kind; + +/** + * Read-only access to the underlying editing nodes of the given kind + */ +- (NSArray *)editingNodesOfKind:(NSString *)kind; + /** * Read only access to the underlying completed nodes of the given kind */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 458166424f..41475ec050 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -786,6 +786,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Data Querying (Subclass API) +- (NSArray *)indexPathsForEditingNodesOfKind:(NSString *)kind +{ + return _editingNodes[kind] != nil ? ASIndexPathsForMultidimensionalArray(_editingNodes[kind]) : [NSArray array]; +} + +- (NSArray *)editingNodesOfKind:(NSString *)kind +{ + return _editingNodes[kind] != nil ? _editingNodes[kind] : [NSArray array]; +} + - (NSArray *)completedNodesOfKind:(NSString *)kind { return _completedNodes[kind]; From 432136740fc8a37e048b9d27685ba55a1e2dd6a8 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:29:23 -0700 Subject: [PATCH 045/127] Constrain flow layout header/footer size to collection view bounds --- .../Details/ASCollectionViewFlowLayoutInspector.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index 4c486d5d00..a34b6d66ca 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -44,12 +44,12 @@ - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - CGSize constrainedSize = CGSizeMake(FLT_MAX, FLT_MAX); + CGSize constrainedSize; CGSize supplementarySize = [self sizeForSupplementaryViewOfKind:kind inSection:indexPath.section]; if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) { - constrainedSize.height = supplementarySize.height; + constrainedSize = CGSizeMake(_collectionView.bounds.size.width, supplementarySize.height); } else { - constrainedSize.width = supplementarySize.width; + constrainedSize = CGSizeMake(supplementarySize.height, _collectionView.bounds.size.height); } return ASSizeRangeMake(CGSizeZero, constrainedSize); } From 71966f3549d80f459a22f463e5df138c36e5ac28 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 10:14:29 -0700 Subject: [PATCH 046/127] Relayout all nodes in backing store on `relayoutAllNodes` --- AsyncDisplayKit/ASCollectionView.mm | 2 +- AsyncDisplayKit/ASTableView.mm | 2 +- AsyncDisplayKit/Details/ASDataController.h | 6 ++- AsyncDisplayKit/Details/ASDataController.mm | 43 ++++++++++++--------- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index bfd943925a..da66f48df9 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -596,7 +596,7 @@ static BOOL _isInterceptedSelector(SEL sel) _ignoreMaxSizeChange = NO; } else { [self performBatchAnimated:NO updates:^{ - [_dataController relayoutAllRows]; + [_dataController relayoutAllNodes]; } completion:nil]; } } diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index a8dc8c6f9d..e2faa2b9c0 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -405,7 +405,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _ignoreMaxWidthChange = NO; } else { [self beginUpdates]; - [_dataController relayoutAllRows]; + [_dataController relayoutAllNodes]; [self endUpdates]; } } diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index ea4e4694a3..e605aa86d6 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -157,10 +157,12 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; /** - * Re-measures all loaded nodes. Used to respond to a change in size of the containing view + * Re-measures all loaded nodes in the backing store. + * + * @discussion Used to respond to a change in size of the containing view * (e.g. ASTableView or ASCollectionView after an orientation change). */ -- (void)relayoutAllRows; +- (void)relayoutAllNodes; - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 41475ec050..d54ca3af10 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -729,41 +729,46 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)relayoutAllRows +- (void)relayoutAllNodes { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); LOG(@"Edit Command - relayoutRows"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; - - void (^relayoutNodesBlock)(NSMutableArray *) = ^void(NSMutableArray *nodes) { - if (!nodes.count) { - return; - } - - [self accessDataSourceWithBlock:^{ - [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { - [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; - ASSizeRange constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); - }]; - }]; - }]; - }; // Can't relayout right away because _completedNodes may not be up-to-date, // 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(^{ - relayoutNodesBlock(_completedNodes[ASDataControllerRowNodeKind]); + [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + [self _relayoutNodesOfKind:kind]; + }]; }); }]; }]; } +- (void)_relayoutNodesOfKind:(NSString *)kind +{ + ASDisplayNodeAssertMainThread(); + NSArray *nodes = [self completedNodesOfKind:kind]; + if (!nodes.count) { + return; + } + + [self accessDataSourceWithBlock:^{ + [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { + [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + }]; + }]; + }]; +} + - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { [self performEditCommandWithBlock:^{ From ed7a73361be9a89e159071c43dabb9e93efdc9db Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 11:06:29 -0700 Subject: [PATCH 047/127] Use simpler completed nodes loop --- AsyncDisplayKit/Details/ASDataController.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index d54ca3af10..5a73c0200c 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -741,9 +741,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // (see _layoutNodes:atIndexPaths:withAnimationOptions:). [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ - [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + for (NSString *kind in [_completedNodes keyEnumerator]) { [self _relayoutNodesOfKind:kind]; - }]; + } }); }]; }]; From f9bba315df9ed8c4d3354ff9a1ebb372ef382c4f Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:00:13 -0700 Subject: [PATCH 048/127] Stub out subclass hooks for section insert/remove/update/moving --- .../Details/ASCollectionDataController.mm | 48 +++++++++++-- .../Details/ASDataController+Subclasses.h | 14 ++++ AsyncDisplayKit/Details/ASDataController.mm | 71 ++++++++++++++++--- 3 files changed, 117 insertions(+), 16 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 1f94ee5e5f..d3c91d6f7d 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -28,6 +28,11 @@ NSMutableDictionary *_pendingIndexPaths; } +- (void)willPerformInitialDataLoading +{ + // TODO: Implement +} + - (void)prepareForReloadData { _pendingNodes = [NSMutableDictionary dictionary]; @@ -71,13 +76,29 @@ }]; } -- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (void)prepareInsertSections:(NSIndexSet *)sections { - if ([kind isEqualToString:ASDataControllerRowNodeKind]) { - return [super constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; - } else { - return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; - } + // TODO: Implement +} + +- (void)willInsertSections:(NSIndexSet *)sections +{ + // TODO: Implement +} + +- (void)willDeleteSections:(NSIndexSet *)sections +{ + // TODO: Implement +} + +- (void)willReloadSections:(NSIndexSet *)sections +{ + // TODO: Implement +} + +- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection +{ + // TODO: Implement } - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths @@ -94,12 +115,27 @@ } } +#pragma mark - Sizing query + +- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + if ([kind isEqualToString:ASDataControllerRowNodeKind]) { + return [super constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + } else { + return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } +} + +#pragma mark - External supplementary store querying + - (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); return [self completedNodesOfKind:kind][indexPath.section][indexPath.item]; } +#pragma mark - Private Helpers + - (id)collectionDataSource { return (id)self.dataSource; diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index d1ae9507cd..ea0d711f53 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -25,6 +25,8 @@ */ - (void)accessDataSourceWithBlock:(dispatch_block_t)block; +- (void)willPerformInitialDataLoading; + /** * An opportunity for a subclass to access the data source before entering into the editing queue */ @@ -37,6 +39,18 @@ */ - (void)willReloadData; +- (void)prepareInsertSections:(NSIndexSet *)sections; + +- (void)willInsertSections:(NSIndexSet *)sections; + +- (void)willDeleteSections:(NSIndexSet *)sections; + +- (void)prepareForReloadSections:(NSIndexSet *)sections; + +- (void)willReloadSections:(NSIndexSet *)sections; + +- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection; + /** * Provides a collection of index paths for nodes of the given kind that are currently in the editing store */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 5a73c0200c..89eab8b8e4 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -334,6 +334,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); [self accessDataSourceWithBlock:^{ + [self willPerformInitialDataLoading]; + NSMutableArray *indexPaths = [NSMutableArray array]; NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self]; @@ -404,6 +406,13 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +#pragma mark - Initial & Reload Data Hooks (Subclass API) + +- (void)willPerformInitialDataLoading +{ + // Implemented by subclasses +} + - (void)prepareForReloadData { // Implemented by subclasses @@ -538,7 +547,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Section Editing (External API) -- (void)insertSections:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)insertSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); @@ -546,41 +555,47 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ + [self prepareInsertSections:sections]; + NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; - [self _populateFromDataSourceWithSectionIndexSet:indexSet mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; + [self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; // Measure nodes whose views are loaded before we leave the main thread [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ + [self willInsertSections:sections]; + LOG(@"Edit Transaction - insertSections: %@", indexSet); - NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:indexSet.count]; - for (NSUInteger i = 0; i < indexSet.count; i++) { + NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count]; + for (NSUInteger i = 0; i < sections.count; i++) { [sectionArray addObject:[NSMutableArray array]]; } - [self _insertSections:sectionArray atIndexSet:indexSet withAnimationOptions:animationOptions]; + [self _insertSections:sectionArray atIndexSet:sections withAnimationOptions:animationOptions]; [self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions]; }]; }]; }]; } -- (void)deleteSections:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)deleteSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - deleteSections: %@", indexSet); + LOG(@"Edit Command - deleteSections: %@", sections); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ + [self willDeleteSections:sections]; + // remove elements - LOG(@"Edit Transaction - deleteSections: %@", indexSet); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], indexSet); + LOG(@"Edit Transaction - deleteSections: %@", sections); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; - [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; + [self _deleteSectionsAtIndexSet:sections withAnimationOptions:animationOptions]; }]; }]; } @@ -594,6 +609,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ + [self prepareForReloadSections:sections]; + NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; [self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; @@ -628,6 +645,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ + [self willMoveSection:section toSection:newSection]; + // remove elements LOG(@"Edit Transaction - moveSection"); @@ -649,6 +668,38 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +#pragma mark - Section Insertion Hooks (Subclass API) + +- (void)prepareInsertSections:(NSIndexSet *)sections +{ + // Implemented by subclass +} + +- (void)willInsertSections:(NSIndexSet *)sections +{ + // Implemented by subclass +} + +- (void)willDeleteSections:(NSIndexSet *)sections +{ + // Implemented by subclass +} + +- (void)prepareForReloadSections:(NSIndexSet *)sections +{ + // Implemented by subclass +} + +- (void)willReloadSections:(NSIndexSet *)sections +{ + // Implemented by subclass +} + +- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection +{ + // Implemented by subclass +} + #pragma mark - Row Editing (External API) - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions From d36c8a36c6ed8a75cd02e285824552a45d41e6a5 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:01:17 -0700 Subject: [PATCH 049/127] Introduce ability to populate internal supplementary nodes at specific sections --- .../Details/ASCollectionDataController.mm | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index d3c91d6f7d..5108eec8b8 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -115,6 +115,19 @@ } } +- (void)_populateSupplementaryNodesOfKind:(NSString *)kind withSections:(NSIndexSet *)sections mutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths +{ + [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + NSUInteger rowNum = [self.collectionDataSource dataController:self supplementaryViewsOfKind:kind inSection:idx]; + NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx]; + for (NSUInteger i = 0; i < rowNum; i++) { + NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i]; + [indexPaths addObject:indexPath]; + [nodes addObject:[self.collectionDataSource dataController:self supplementaryNodeOfKind:kind atIndexPath:indexPath]]; + } + }]; +} + #pragma mark - Sizing query - (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath From 2cc55513a49f4b27df0c7b785286fc47cf40ec00 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:01:41 -0700 Subject: [PATCH 050/127] Nil out pending store kinds after processing --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 5108eec8b8..17e5b00c98 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -71,8 +71,8 @@ [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:^(NSArray *nodes, NSArray *indexPaths) { [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; }]; - _pendingNodes[kind] = [NSArray array]; - _pendingIndexPaths[kind] = [NSArray array]; + _pendingNodes[kind] = nil; + _pendingIndexPaths[kind] = nil; }]; } From e1ea2d2151d2ef6506a328dcd2fecceedecddecf Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:02:28 -0700 Subject: [PATCH 051/127] Handle supplementary node generation during insertion of new section --- .../Details/ASCollectionDataController.mm | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 17e5b00c98..98d4fb4895 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -78,12 +78,28 @@ - (void)prepareInsertSections:(NSIndexSet *)sections { - // TODO: Implement + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + LOG(@"Populating elements of kind: %@, for sections: %@", kind, sections); + NSMutableArray *nodes = [NSMutableArray array]; + NSMutableArray *indexPaths = [NSMutableArray array]; + [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths]; + _pendingNodes[kind] = nodes; + _pendingIndexPaths[kind] = indexPaths; + }]; } - (void)willInsertSections:(NSIndexSet *)sections { - // TODO: Implement + [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count]; + for (NSUInteger i = 0; i < sections.count; i++) { + [sectionArray addObject:[NSMutableArray array]]; + } + + [self insertSections:sectionArray ofKind:kind atIndexSet:sections completion:nil]; + [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:nil]; + }]; } - (void)willDeleteSections:(NSIndexSet *)sections From d77df45bfd55ad79d8ecd15b625974ac660c51e6 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:03:16 -0700 Subject: [PATCH 052/127] Delete supplementary nodes when section is deleted --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 98d4fb4895..7eee33fffe 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -103,6 +103,17 @@ } - (void)willDeleteSections:(NSIndexSet *)sections +{ + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], sections); + + [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; + [self deleteSectionsOfKind:kind atIndexSet:sections completion:nil]; + }]; +} + +- (void)prepareForReloadSections:(NSIndexSet *)sections { // TODO: Implement } From 9235ce5ac285ff068c49c7d9bb05c953e29a8201 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:10:28 -0700 Subject: [PATCH 053/127] Support reloading supplementary nodes when sections reload --- .../Details/ASCollectionDataController.mm | 20 +++++++++++++++++-- AsyncDisplayKit/Details/ASDataController.mm | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 7eee33fffe..733594fe4f 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -99,6 +99,8 @@ [self insertSections:sectionArray ofKind:kind atIndexSet:sections completion:nil]; [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:nil]; + _pendingNodes[kind] = nil; + _pendingIndexPaths[kind] = nil; }]; } @@ -115,12 +117,26 @@ - (void)prepareForReloadSections:(NSIndexSet *)sections { - // TODO: Implement + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + NSMutableArray *nodes = [NSMutableArray array]; + NSMutableArray *indexPaths = [NSMutableArray array]; + [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths]; + _pendingNodes[kind] = nodes; + _pendingIndexPaths[kind] = indexPaths; + }]; } - (void)willReloadSections:(NSIndexSet *)sections { - // TODO: Implement + [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], sections); + [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; + // reinsert the elements + [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:nil]; + _pendingNodes[kind] = nil; + _pendingIndexPaths[kind] = nil; + }]; } - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 89eab8b8e4..e4b0f81fde 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -623,6 +623,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ + [self willReloadSections:sections]; + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections); LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind])); From 3f0092435afadcb43fbf35640dd2c6a1e439d1da Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:17:11 -0700 Subject: [PATCH 054/127] Add support to move supplementary nodes with section position changes --- .../Details/ASCollectionDataController.mm | 15 ++++++++++++++- .../Details/ASDataController+Subclasses.h | 4 ++-- AsyncDisplayKit/Details/ASDataController.mm | 6 +++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 733594fe4f..e1a0327af3 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -141,7 +141,20 @@ - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection { - // TODO: Implement + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], [NSIndexSet indexSetWithIndex:section]); + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths([self editingNodesOfKind:kind], indexPaths); + [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; + + // update the section of indexpaths + NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:newSection]; + NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] initWithCapacity:indexPaths.count]; + [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { + [updatedIndexPaths addObject:[sectionIndexPath indexPathByAddingIndex:[indexPath indexAtPosition:indexPath.length - 1]]]; + }]; + [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; + }]; } - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index ea0d711f53..1bb3615e7e 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -59,12 +59,12 @@ /** * Read-only access to the underlying editing nodes of the given kind */ -- (NSArray *)editingNodesOfKind:(NSString *)kind; +- (NSMutableArray *)editingNodesOfKind:(NSString *)kind; /** * Read only access to the underlying completed nodes of the given kind */ -- (NSArray *)completedNodesOfKind:(NSString *)kind; +- (NSMutableArray *)completedNodesOfKind:(NSString *)kind; /** * Measure and layout the given nodes in optimized batches, constraining each to a given size in `constrainedSizeForNodeOfKind:atIndexPath:`. diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index e4b0f81fde..85b4ab1925 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -849,12 +849,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; return _editingNodes[kind] != nil ? ASIndexPathsForMultidimensionalArray(_editingNodes[kind]) : [NSArray array]; } -- (NSArray *)editingNodesOfKind:(NSString *)kind +- (NSMutableArray *)editingNodesOfKind:(NSString *)kind { - return _editingNodes[kind] != nil ? _editingNodes[kind] : [NSArray array]; + return _editingNodes[kind] != nil ? _editingNodes[kind] : [NSMutableArray array]; } -- (NSArray *)completedNodesOfKind:(NSString *)kind +- (NSMutableArray *)completedNodesOfKind:(NSString *)kind { return _completedNodes[kind]; } From 8a77072e3ce1a796cce6a3632eb55b4aeba6b2c1 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 7 Oct 2015 15:42:28 -0700 Subject: [PATCH 055/127] Add tests for flow layout inspector section calculation --- AsyncDisplayKit.xcodeproj/project.pbxproj | 4 + ...ASCollectionViewFlowLayoutInspectorTests.m | 81 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 6e105257a3..f096375ce4 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -145,6 +145,7 @@ 251B8EF91BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ASSET_TAGS = (); }; }; 251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; settings = {ASSET_TAGS = (); }; }; 251B8EFB1BBB3D690087C538 /* ASDataController+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */; settings = {ASSET_TAGS = (); }; }; + 2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */; settings = {ASSET_TAGS = (); }; }; 2767E9411BB19BD600EA9B77 /* ASViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2767E9421BB19BD600EA9B77 /* ASViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */; settings = {ASSET_TAGS = (); }; }; 2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2911485B1A77147A005D0878 /* ASControlNodeTests.m */; }; @@ -556,6 +557,7 @@ 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewFlowLayoutInspector.h; sourceTree = ""; }; 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewFlowLayoutInspector.m; sourceTree = ""; }; 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDataController+Subclasses.h"; sourceTree = ""; }; + 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewFlowLayoutInspectorTests.m; sourceTree = ""; }; 2911485B1A77147A005D0878 /* ASControlNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNodeTests.m; sourceTree = ""; }; 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutRangeType.h; sourceTree = ""; }; 292C599A1A956527007E5DD6 /* ASRangeHandlerPreload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandlerPreload.h; sourceTree = ""; }; @@ -841,6 +843,7 @@ 058D0A37195D057000B7D73C /* ASTextNodeWordKernerTests.mm */, 058D09C6195D04C000B7D73C /* Supporting Files */, 052EE06A1A15A0D8002C6279 /* TestResources */, + 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */, ); path = AsyncDisplayKitTests; sourceTree = ""; @@ -1544,6 +1547,7 @@ 2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */, ACF6ED5D1B178DC700DA7C62 /* ASDimensionTests.mm in Sources */, 058D0A38195D057000B7D73C /* ASDisplayLayerTests.m in Sources */, + 2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */, 058D0A39195D057000B7D73C /* ASDisplayNodeAppearanceTests.m in Sources */, 058D0A3A195D057000B7D73C /* ASDisplayNodeTests.m in Sources */, 058D0A3B195D057000B7D73C /* ASDisplayNodeTestsHelper.m in Sources */, diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m new file mode 100644 index 0000000000..ea82418df5 --- /dev/null +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -0,0 +1,81 @@ +/* Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +#import "ASCollectionView.h" +#import "ASCollectionViewFlowLayoutInspector.h" + +@interface TestDataSource : NSObject +@end + +@implementation TestDataSource + +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return [[ASCellNode alloc] init]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return 0; +} + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView +{ + return 2; +} + +@end + +@interface ASCollectionViewFlowLayoutInspectorTests : XCTestCase + +@end + +@implementation ASCollectionViewFlowLayoutInspectorTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +#pragma mark - #collectionView:constrainedSizeForSupplementaryNodeOfKind:atIndexPath: + + + +#pragma mark - #collectionView:numberOfSectionsForSupplementaryKind: + +- (void)testThatItRespondsWithTheDefaultNumberOfSections +{ + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryKind:UICollectionElementKindSectionHeader]; + XCTAssert(sections == 1, @"should return 1 by default"); +} + +- (void)testThatItProvidesTheNumberOfSectionsInTheDataSource +{ + TestDataSource *dataSource = [[TestDataSource alloc] init]; + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryKind:UICollectionElementKindSectionHeader]; + XCTAssert(sections == 2, @"should return 2"); +} + +#pragma mark - #collectionView:supplementaryViewsOfKind:inSection: + +@end From 1216d748a1d5c296a4906e9ffec75dce2a706e8c Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 7 Oct 2015 16:00:29 -0700 Subject: [PATCH 056/127] Test number of supplementary views in section inspector method --- ...ASCollectionViewFlowLayoutInspectorTests.m | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m index ea82418df5..8bbbc7224d 100644 --- a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -12,10 +12,13 @@ #import "ASCollectionView.h" #import "ASCollectionViewFlowLayoutInspector.h" -@interface TestDataSource : NSObject +/** + * Test Data Source + */ +@interface InspectorTestDataSource : NSObject @end -@implementation TestDataSource +@implementation InspectorTestDataSource - (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath { @@ -38,6 +41,22 @@ @end +/** + * Test Delegate + */ +@interface ReferenceSizeTestDelegate : NSObject + +@end + +@implementation ReferenceSizeTestDelegate + +- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section +{ + return CGSizeMake(125.0, 125.0); +} + +@end + @implementation ASCollectionViewFlowLayoutInspectorTests - (void)setUp { @@ -67,7 +86,7 @@ - (void)testThatItProvidesTheNumberOfSectionsInTheDataSource { - TestDataSource *dataSource = [[TestDataSource alloc] init]; + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; collectionView.asyncDataSource = dataSource; @@ -78,4 +97,44 @@ #pragma mark - #collectionView:supplementaryViewsOfKind:inSection: +- (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheDelegate +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionHeader inSection:0]; + XCTAssert(count == 1, @"should have a header supplementary view"); +} + +- (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheLayout +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.footerReferenceSize = CGSizeMake(125.0, 125.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionFooter inSection:0]; + XCTAssert(count == 1, @"should have a footer supplementary view"); +} + +- (void)testThatItReturnsNoneWhenNoReferenceSizeIsImplemented +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionFooter inSection:0]; + XCTAssert(count == 0, @"should not have a footer supplementary view"); +} + @end From 8a9720dd12e94f0e46f2e4bb093b7cf659134849 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 7 Oct 2015 18:25:30 -0700 Subject: [PATCH 057/127] Add tests for constrained size method of flow layout inspector --- ...ASCollectionViewFlowLayoutInspectorTests.m | 198 +++++++++++++++++- 1 file changed, 192 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m index 8bbbc7224d..78171d8c32 100644 --- a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -42,13 +42,13 @@ @end /** - * Test Delegate + * Test Delegate for Header Reference Size Implementation */ -@interface ReferenceSizeTestDelegate : NSObject +@interface HeaderReferenceSizeTestDelegate : NSObject @end -@implementation ReferenceSizeTestDelegate +@implementation HeaderReferenceSizeTestDelegate - (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section { @@ -57,6 +57,22 @@ @end +/** + * Test Delegate for Footer Reference Size Implementation + */ +@interface FooterReferenceSizeTestDelegate : NSObject + +@end + +@implementation FooterReferenceSizeTestDelegate + +- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section +{ + return CGSizeMake(125.0, 125.0); +} + +@end + @implementation ASCollectionViewFlowLayoutInspectorTests - (void)setUp { @@ -71,7 +87,177 @@ #pragma mark - #collectionView:constrainedSizeForSupplementaryNodeOfKind:atIndexPath: +// Vertical +// Delegate implementation + +- (void)testThatItReturnsAVerticalConstrainedSizeFromTheHeaderDelegateImplementation +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); +} + +- (void)testThatItReturnsAVerticalConstrainedSizeFromTheFooterDelegateImplementation +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + FooterReferenceSizeTestDelegate *delegate = [[FooterReferenceSizeTestDelegate alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); +} + +// Size implementation + +- (void)testThatItReturnsAVerticalConstrainedSizeFromTheHeaderProperty +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + layout.headerReferenceSize = CGSizeMake(125.0, 125.0); + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); +} + +- (void)testThatItReturnsAVerticalConstrainedSizeFromTheFooterProperty +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + layout.footerReferenceSize = CGSizeMake(125.0, 125.0); + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); +} + +// Horizontal + +- (void)testThatItReturnsAHorizontalConstrainedSizeFromTheHeaderDelegateImplementation +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); +} + +- (void)testThatItReturnsAHorizontalConstrainedSizeFromTheFooterDelegateImplementation +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + FooterReferenceSizeTestDelegate *delegate = [[FooterReferenceSizeTestDelegate alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); +} + +// Size implementation + +- (void)testThatItReturnsAHorizontalConstrainedSizeFromTheHeaderProperty +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + layout.headerReferenceSize = CGSizeMake(125.0, 125.0); + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.width)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); +} + +- (void)testThatItReturnsAHorizontalConstrainedSizeFromTheFooterProperty +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + layout.footerReferenceSize = CGSizeMake(125.0, 125.0); + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); +} + +- (void)testThatItReturnsZeroSizeWhenNoReferenceSizeIsImplemented +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeZero); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a zero size"); +} #pragma mark - #collectionView:numberOfSectionsForSupplementaryKind: @@ -100,7 +286,7 @@ - (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheDelegate { InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; - ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; collectionView.asyncDataSource = dataSource; @@ -113,7 +299,7 @@ - (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheLayout { InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; - ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; layout.footerReferenceSize = CGSizeMake(125.0, 125.0); ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; @@ -127,7 +313,7 @@ - (void)testThatItReturnsNoneWhenNoReferenceSizeIsImplemented { InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; - ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; collectionView.asyncDataSource = dataSource; From eb5c79598c563f19ca6a91583c05856baf5b3cf7 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:25:49 -0700 Subject: [PATCH 058/127] Clean up arrangement of data controller subclassing methods --- .../Details/ASDataController+Subclasses.h | 21 ++++++------------- AsyncDisplayKit/Details/ASDataController.mm | 11 ++++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 1bb3615e7e..16ab8cc028 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -10,21 +10,6 @@ @interface ASDataController (Subclasses) -/** - * Queues the given operation until an `endUpdates` synchronize update is completed. - * - * If this method is called outside of a begin/endUpdates batch update, the block is - * executed immediately. - */ -- (void)performEditCommandWithBlock:(void (^)(void))block; - -/** - * Safely locks access to the data source and executes the given block, unlocking once complete. - * - * When `asyncDataFetching` is enabled, the block is executed on a background thread. - */ -- (void)accessDataSourceWithBlock:(dispatch_block_t)block; - - (void)willPerformInitialDataLoading; /** @@ -51,6 +36,8 @@ - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection; +#pragma mark - Internal editing & completed store querying + /** * Provides a collection of index paths for nodes of the given kind that are currently in the editing store */ @@ -66,6 +53,8 @@ */ - (NSMutableArray *)completedNodesOfKind:(NSString *)kind; +#pragma mark - Node sizing + /** * Measure and layout the given nodes in optimized batches, constraining each to a given size in `constrainedSizeForNodeOfKind:atIndexPath:`. */ @@ -76,6 +65,8 @@ */ - (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +#pragma mark - Node & Section Insertion/Deletion API + /** * Inserts the given nodes of the specified kind into the backing store, calling completion on the main thread when the write finishes. */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 85b4ab1925..84e979b713 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -425,6 +425,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Data Source Access (Calling _dataSource) +/** + * Safely locks access to the data source and executes the given block, unlocking once complete. + * + * @discussion When `asyncDataFetching` is enabled, the block is executed on a background thread. + */ - (void)accessDataSourceWithBlock:(dispatch_block_t)block { if (_asyncDataFetchingEnabled) { @@ -534,6 +539,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } } +/** + * Queues the given operation until an `endUpdates` synchronize update is completed. + * + * If this method is called outside of a begin/endUpdates batch update, the block is + * executed immediately. + */ - (void)performEditCommandWithBlock:(void (^)(void))block { // This method needs to block the thread and synchronously perform the operation if we are not From 4fb4119f9c50e8305936366aa9708807f7b579c3 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:32:34 -0700 Subject: [PATCH 059/127] Clarify data controller subclass hooks --- AsyncDisplayKit/Details/ASDataController.mm | 47 ++++++++++----------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 84e979b713..8ae2454c78 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -406,23 +406,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -#pragma mark - Initial & Reload Data Hooks (Subclass API) - -- (void)willPerformInitialDataLoading -{ - // Implemented by subclasses -} - -- (void)prepareForReloadData -{ - // Implemented by subclasses -} - -- (void)willReloadData -{ - // Implemented by subclasses -} - #pragma mark - Data Source Access (Calling _dataSource) /** @@ -681,36 +664,52 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -#pragma mark - Section Insertion Hooks (Subclass API) + +#pragma mark - Backing store manipulation optional hooks (Subclass API) + +- (void)willPerformInitialDataLoading +{ + // Optional template hook for subclasses (See ASDataController+Subclasses.h) +} + +- (void)prepareForReloadData +{ + // Optional template hook for subclasses (See ASDataController+Subclasses.h) +} + +- (void)willReloadData +{ + // Optional template hook for subclasses (See ASDataController+Subclasses.h) +} - (void)prepareInsertSections:(NSIndexSet *)sections { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } - (void)willInsertSections:(NSIndexSet *)sections { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } - (void)willDeleteSections:(NSIndexSet *)sections { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } - (void)prepareForReloadSections:(NSIndexSet *)sections { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } - (void)willReloadSections:(NSIndexSet *)sections { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } #pragma mark - Row Editing (External API) From 848dc26a0defc84073b758f7195a712a5e8e3189 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:33:09 -0700 Subject: [PATCH 060/127] Fix hook method naming --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 2 +- AsyncDisplayKit/Details/ASDataController+Subclasses.h | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index e1a0327af3..f05e77780b 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -76,7 +76,7 @@ }]; } -- (void)prepareInsertSections:(NSIndexSet *)sections +- (void)prepareForInsertSections:(NSIndexSet *)sections { NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 16ab8cc028..1c3a54a2c0 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -24,7 +24,7 @@ */ - (void)willReloadData; -- (void)prepareInsertSections:(NSIndexSet *)sections; +- (void)prepareForInsertSections:(NSIndexSet *)sections; - (void)willInsertSections:(NSIndexSet *)sections; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 8ae2454c78..a04cfb3679 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -549,7 +549,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ - [self prepareInsertSections:sections]; + [self prepareForInsertSections:sections]; NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; @@ -682,7 +682,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Optional template hook for subclasses (See ASDataController+Subclasses.h) } -- (void)prepareInsertSections:(NSIndexSet *)sections +- (void)prepareForInsertSections:(NSIndexSet *)sections { // Optional template hook for subclasses (See ASDataController+Subclasses.h) } From 0870d50e36a040cb8d1c47b43cb85acd330314f9 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:35:27 -0700 Subject: [PATCH 061/127] Clarify layout delegate assertion messages --- AsyncDisplayKit/ASCollectionView.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index da66f48df9..bcabc9c0a4 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -746,19 +746,19 @@ static BOOL _isInterceptedSelector(SEL sel) - (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); + ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; } - (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { - ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); + ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); return [_layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; } - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; { - ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); + ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; } From fddb0061b07f99c9af6b4f95a24c9dd109b57d86 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:47:35 -0700 Subject: [PATCH 062/127] Use ASCellNodes for supplementary nodes --- AsyncDisplayKit/ASCollectionView.h | 2 +- AsyncDisplayKit/ASCollectionView.mm | 10 +++++----- AsyncDisplayKit/ASTableView.mm | 2 +- AsyncDisplayKit/Details/ASCollectionDataController.h | 4 ++-- AsyncDisplayKit/Details/ASCollectionDataController.mm | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 4 ++-- AsyncDisplayKit/Details/ASRangeController.h | 4 ++-- AsyncDisplayKit/Details/ASRangeController.mm | 6 +++--- AsyncDisplayKit/Details/ASRangeHandlerRender.mm | 2 +- examples/ASCollectionView/Sample/ViewController.m | 2 +- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 91226a5d64..338792c3d8 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -281,7 +281,7 @@ @optional -- (ASDisplayNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; /** * Provides the constrained size range for measuring the node at the index path. diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index bcabc9c0a4..94ff1104d7 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -476,7 +476,7 @@ static BOOL _isInterceptedSelector(SEL sel) ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - [_rangeController configureContentView:cell.contentView forNode:node]; + [_rangeController configureContentView:cell.contentView forCellNode:node]; cell.node = node; @@ -492,8 +492,8 @@ static BOOL _isInterceptedSelector(SEL sel) { NSString *identifier = [self __reuseIdentifierForKind:kind]; UICollectionReusableView *view = [self dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:identifier forIndexPath:indexPath]; - ASDisplayNode *node = [_dataController supplementaryNodeOfKind:kind atIndexPath:indexPath]; - [_rangeController configureContentView:view forNode:node]; + ASCellNode *node = [_dataController supplementaryNodeOfKind:kind atIndexPath:indexPath]; + [_rangeController configureContentView:view forCellNode:node]; return view; } @@ -731,9 +731,9 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark - ASCollectionViewDataControllerSource Supplementary view support -- (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (ASCellNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - ASDisplayNode *node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; + ASCellNode *node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; ASDisplayNodeAssert(node != nil, @"A node must be returned for a supplementary node"); ASDisplayNodeAssert(!node.nodeLoaded, @"The supplementary node must not be loaded"); return node; diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index e2faa2b9c0..314d7664c6 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -530,7 +530,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - [_rangeController configureContentView:cell.contentView forNode:node]; + [_rangeController configureContentView:cell.contentView forCellNode:node]; cell.node = node; cell.backgroundColor = node.backgroundColor; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h index 08f8775270..9c2aa46b68 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.h +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -17,7 +17,7 @@ @protocol ASCollectionDataControllerSource -- (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (ASCellNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; /** The constrained size range for layout. @@ -34,6 +34,6 @@ @interface ASCollectionDataController : ASDataController -- (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; @end \ No newline at end of file diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index f05e77780b..13bf7ae915 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -197,7 +197,7 @@ #pragma mark - External supplementary store querying -- (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); return [self completedNodesOfKind:kind][indexPath.section][indexPath.item]; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index a04cfb3679..552bd1292a 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -169,7 +169,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; dispatch_group_async(layoutGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (NSUInteger k = j; k < j + batchCount; k++) { - ASDisplayNode *node = nodes[k]; + ASCellNode *node = nodes[k]; // Only measure nodes whose views aren't loaded, since we're in the background. // We should already have measured loaded nodes before we left the main thread, using _layoutNodesWithMainThreadAffinity: if (!node.isNodeLoaded) { @@ -928,7 +928,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASDisplayNodeAssertMainThread(); [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { - [section enumerateObjectsUsingBlock:^(ASDisplayNode *node, NSUInteger rowIndex, BOOL *stop) { + [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { if (node.isNodeLoaded) { if (node.layerBacked) { [node.layer removeFromSuperlayer]; diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index a7fc3e54df..cf4cc37354 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -41,9 +41,9 @@ * * @param contentView UIView to add a (sized) node's view to. * - * @param node The node to be added. Often an ASCellNode. + * @param cellNode The cell node to be added. */ -- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)node; +- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node; /** * Delegate and ultimate data source. Must not be nil. diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 663f9bea1c..266fb1a855 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -48,7 +48,7 @@ #pragma mark - View manipulation -- (void)moveNode:(ASDisplayNode *)node toView:(UIView *)view +- (void)moveCellNode:(ASCellNode *)node toView:(UIView *)view { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssert(node, @"Cannot move a nil node to a view"); @@ -158,7 +158,7 @@ return rangeType == ASLayoutRangeTypeRender; } -- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)node +- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node { if (node.view.superview == contentView) { // this content view is already correctly configured @@ -170,7 +170,7 @@ [view removeFromSuperview]; } - [self moveNode:node toView:contentView]; + [self moveCellNode:node toView:contentView]; } diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index 0b6efd8568..0363abba2f 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -64,7 +64,7 @@ // This happens if the UITableViewCell is reused after scrolling offscreen. Because the node has already been given the opportunity to display, we do not // proactively re-host it within the workingWindow (improving efficiency). Some time later, it may fall outside the working range, in which case calling // -recursivelyClearContents is critical. If the user scrolls back and it is re-hosted in a UITableViewCell, the content will still exist as it is not cleared - // by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forNode:]. + // by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forCellNode:]. // Condition #4 is suboptimal in some cases, as it is conceivable that memory warnings could trigger clearing content that is inside the working range. However, enforcing the // preservation of this content could result in the app being killed, which is not likely preferable over briefly seeing placeholders in the event the user scrolls backwards. // Nonetheless, future changes to the implementation will likely eliminate this behavior to simplify debugging and extensibility of working range functionality. diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 9587bea933..d17929e7ae 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -78,7 +78,7 @@ return node; } -- (ASDisplayNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { NSString *text = [kind isEqualToString:UICollectionElementKindSectionHeader] ? @"Header" : @"Footer"; SupplementaryNode *node = [[SupplementaryNode alloc] initWithText:text]; From a9023dd400039f3eb9b2bb291b82b8e804a82a9d Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:56:39 -0700 Subject: [PATCH 063/127] Fix layout of example supplementary nodes --- examples/ASCollectionView/Sample/SupplementaryNode.h | 2 +- examples/ASCollectionView/Sample/SupplementaryNode.m | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/ASCollectionView/Sample/SupplementaryNode.h b/examples/ASCollectionView/Sample/SupplementaryNode.h index e64ac9e3f6..f75c929684 100644 --- a/examples/ASCollectionView/Sample/SupplementaryNode.h +++ b/examples/ASCollectionView/Sample/SupplementaryNode.h @@ -11,7 +11,7 @@ #import -@interface SupplementaryNode : ASDisplayNode +@interface SupplementaryNode : ASCellNode - (instancetype)initWithText:(NSString *)text; diff --git a/examples/ASCollectionView/Sample/SupplementaryNode.m b/examples/ASCollectionView/Sample/SupplementaryNode.m index baddef62c5..ca5579e9a6 100644 --- a/examples/ASCollectionView/Sample/SupplementaryNode.m +++ b/examples/ASCollectionView/Sample/SupplementaryNode.m @@ -36,6 +36,7 @@ static CGFloat kInsets = 15.0; - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { ASCenterLayoutSpec *center = [[ASCenterLayoutSpec alloc] init]; + center.centeringOptions = ASCenterLayoutSpecCenteringXY; center.child = _textNode; UIEdgeInsets insets = UIEdgeInsetsMake(kInsets, kInsets, kInsets, kInsets); return [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:center]; From ba72c737796690cacd661900a08d31028c4957a2 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 11:28:01 -0700 Subject: [PATCH 064/127] Clarify default value behavior of the layoutDelegate for flow layouts --- AsyncDisplayKit/ASCollectionView.mm | 6 +++--- .../ASCollectionViewFlowLayoutInspector.m | 5 +++-- AsyncDisplayKitTests/ASCollectionViewTests.m | 21 +++++++++++++++++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 94ff1104d7..385ca7faf7 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -228,7 +228,9 @@ static BOOL _isInterceptedSelector(SEL sel) // Register the default layout inspector delegate for flow layouts, custom layouts // will need to roll their own ASCollectionViewLayoutInspecting implementation. - _layoutDelegate = [self flowLayoutInspector]; + if ([layout asdk_isFlowLayout]) { + _layoutDelegate = [self flowLayoutInspector]; + } _registeredSupplementaryKinds = [NSMutableArray array]; @@ -249,8 +251,6 @@ static BOOL _isInterceptedSelector(SEL sel) /** * A layout inspector implementation specific for the sizing behavior of UICollectionViewFlowLayouts - * - * @discussion Will return nil if the current layout is not a flow layout */ - (ASCollectionViewFlowLayoutInspector *)flowLayoutInspector { diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index a34b6d66ca..efdb26bdaf 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -21,11 +21,12 @@ - (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout { + self = [super init]; + if (flowLayout == nil) { return nil; } - - self = [super init]; + if (self != nil) { self.collectionView = collectionView; _layout = flowLayout; diff --git a/AsyncDisplayKitTests/ASCollectionViewTests.m b/AsyncDisplayKitTests/ASCollectionViewTests.m index 3b4d661514..1993d0734b 100644 --- a/AsyncDisplayKitTests/ASCollectionViewTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewTests.m @@ -6,7 +6,8 @@ // #import -#import +#import "ASCollectionView.h" +#import "ASCollectionViewFlowLayoutInspector.h" @interface ASCollectionViewTestDelegate : NSObject @@ -79,7 +80,23 @@ @implementation ASCollectionViewTests -- (void)DISABLED_testCollectionViewController { +- (void)testThatItSetsALayoutInspectorForFlowLayouts +{ + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; + XCTAssert(collectionView.layoutDelegate != nil, @"should automatically set a layout delegate for flow layouts"); + XCTAssert([collectionView.layoutDelegate isKindOfClass:[ASCollectionViewFlowLayoutInspector class]], @"should have a flow layout inspector by default"); +} + +- (void)testThatItDoesNotSetALayoutInspectorForCustomLayouts +{ + UICollectionViewLayout *layout = [[UICollectionViewLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; + XCTAssert(collectionView.layoutDelegate == nil, @"should not set a layout delegate for custom layouts"); +} + +- (void)DISABLED_testCollectionViewController +{ ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil]; UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; From c8a79c5cfb72f13975b8fa81f625487cad3c4c72 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 11:29:54 -0700 Subject: [PATCH 065/127] Ignore coverage analysis files in master --- .gitignore | 2 ++ ObjectiveC.gcno | Bin 924 -> 0 bytes QuartzCore.gcno | Bin 924 -> 0 bytes 3 files changed, 2 insertions(+) delete mode 100644 ObjectiveC.gcno delete mode 100644 QuartzCore.gcno diff --git a/.gitignore b/.gitignore index aee909766f..84de6767f4 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ docs/.sass-cache *.lock *.gcov +*.gcno +*.gcda diff --git a/ObjectiveC.gcno b/ObjectiveC.gcno deleted file mode 100644 index 05bc771aa773306a2dac55e20902adbb6fb7cbe7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 924 zcmd1LOHS7^GB9a6bMz1c0|TQBkN|-zatj`?18MDO$Kc@PqWqkku*}qQg`C97)FS7c z#Ny&u1_lA3ynbkLYEiL%ZemexYEDsprM_cvWnQvNW^n;fsdr`xCLd~?Uw%reUM_Npb<{_;@1& jY+)6jl#`#F9iN$3mYNpb<{_;@1& jY+)6jl#`#F9iN$3mY Date: Sun, 11 Oct 2015 12:38:53 -0700 Subject: [PATCH 066/127] Homogenize cell node measuring --- AsyncDisplayKit/Details/ASDataController.mm | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 552bd1292a..1e67c329c1 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -103,6 +103,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Cell Layout +- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize +{ + [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); +} + /* * FIXME: Shouldn't this method, as well as `_layoutNodes:atIndexPaths:withAnimationOptions:` use the word "measure" instead? * @@ -116,8 +122,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASCellNode *node = nodes[idx]; if (node.isNodeLoaded) { ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + [self _layoutNode:node withConstrainedSize:constrainedSize]; } }]; } @@ -173,9 +178,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Only measure nodes whose views aren't loaded, since we're in the background. // We should already have measured loaded nodes before we left the main thread, using _layoutNodesWithMainThreadAffinity: if (!node.isNodeLoaded) { - ASSizeRange constrainedSize = nodeBoundSizes[k]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + [self _layoutNode:node withConstrainedSize:nodeBoundSizes[k]]; } } }); From 952a66a9243b8cbc221b73d9a60af65e4af5b332 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 10:28:39 -0700 Subject: [PATCH 067/127] Remove initial data loading subclass hook --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 5 ----- AsyncDisplayKit/Details/ASDataController+Subclasses.h | 2 -- AsyncDisplayKit/Details/ASDataController.mm | 11 ++--------- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 13bf7ae915..081d44585d 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -28,11 +28,6 @@ NSMutableDictionary *_pendingIndexPaths; } -- (void)willPerformInitialDataLoading -{ - // TODO: Implement -} - - (void)prepareForReloadData { _pendingNodes = [NSMutableDictionary dictionary]; diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 1c3a54a2c0..6f930ca23b 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -10,8 +10,6 @@ @interface ASDataController (Subclasses) -- (void)willPerformInitialDataLoading; - /** * An opportunity for a subclass to access the data source before entering into the editing queue */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 1e67c329c1..f3901a7ae5 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -110,9 +110,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } /* - * FIXME: Shouldn't this method, as well as `_layoutNodes:atIndexPaths:withAnimationOptions:` use the word "measure" instead? + * Perform measurement and layout of loaded nodes on the main thread, skipping unloaded nodes. * - * Once nodes have loaded their views, we can't layout in the background so this is a chance + * @discussion Once nodes have loaded their views, we can't layout in the background so this is a chance * to do so immediately on the main thread. */ - (void)_layoutNodesWithMainThreadAffinity:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths { @@ -337,8 +337,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); [self accessDataSourceWithBlock:^{ - [self willPerformInitialDataLoading]; - NSMutableArray *indexPaths = [NSMutableArray array]; NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self]; @@ -670,11 +668,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Backing store manipulation optional hooks (Subclass API) -- (void)willPerformInitialDataLoading -{ - // Optional template hook for subclasses (See ASDataController+Subclasses.h) -} - - (void)prepareForReloadData { // Optional template hook for subclasses (See ASDataController+Subclasses.h) From ee0cc2001a37a91884a79c5c2bce908438b51c41 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 16:06:52 -0700 Subject: [PATCH 068/127] Add support for loaded node layout for supplementary views --- AsyncDisplayKit/ASCollectionView.mm | 1 - .../Details/ASCollectionDataController.mm | 13 +++- .../Details/ASDataController+Subclasses.h | 8 +++ AsyncDisplayKit/Details/ASDataController.mm | 65 +++++++++---------- .../ASCollectionView/Sample/ViewController.m | 7 +- 5 files changed, 56 insertions(+), 38 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 385ca7faf7..c16715b4eb 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -735,7 +735,6 @@ static BOOL _isInterceptedSelector(SEL sel) { ASCellNode *node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; ASDisplayNodeAssert(node != nil, @"A node must be returned for a supplementary node"); - ASDisplayNodeAssert(!node.nodeLoaded, @"The supplementary node must not be loaded"); return node; } diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 081d44585d..0bbc2c502c 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -41,6 +41,9 @@ [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; _pendingNodes[kind] = nodes; _pendingIndexPaths[kind] = indexPaths; + + // Measure loaded nodes before leaving the main thread + [self layoutLoadedNodes:nodes ofKind:kind atIndexPaths:indexPaths]; }]; } @@ -66,8 +69,8 @@ [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:^(NSArray *nodes, NSArray *indexPaths) { [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; }]; - _pendingNodes[kind] = nil; - _pendingIndexPaths[kind] = nil; + [_pendingNodes removeObjectForKey:kind]; + [_pendingIndexPaths removeObjectForKey:kind]; }]; } @@ -81,6 +84,9 @@ [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths]; _pendingNodes[kind] = nodes; _pendingIndexPaths[kind] = indexPaths; + + // Measure loaded nodes before leaving the main thread + [self layoutLoadedNodes:nodes ofKind:kind atIndexPaths:indexPaths]; }]; } @@ -119,6 +125,9 @@ [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths]; _pendingNodes[kind] = nodes; _pendingIndexPaths[kind] = indexPaths; + + // Measure loaded nodes before leaving the main thread + [self layoutLoadedNodes:nodes ofKind:kind atIndexPaths:indexPaths]; }]; } diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 6f930ca23b..018e58348a 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -58,6 +58,14 @@ */ - (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +/* + * Perform measurement and layout of loaded nodes on the main thread, skipping unloaded nodes. + * + * @discussion Once nodes have loaded their views, we can't layout in the background so this is a chance + * to do so immediately on the main thread. + */ +- (void)layoutLoadedNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths; + /** * Provides the size range for a specific node during the layout process. */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index f3901a7ae5..24dcd68d2c 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -103,30 +103,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Cell Layout -- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize -{ - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); -} - -/* - * Perform measurement and layout of loaded nodes on the main thread, skipping unloaded nodes. - * - * @discussion Once nodes have loaded their views, we can't layout in the background so this is a chance - * to do so immediately on the main thread. - */ -- (void)_layoutNodesWithMainThreadAffinity:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths { - NSAssert(NSThread.isMainThread, @"Main thread layout must be on the main thread."); - - [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, __unused BOOL * stop) { - ASCellNode *node = nodes[idx]; - if (node.isNodeLoaded) { - ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath]; - [self _layoutNode:node withConstrainedSize:constrainedSize]; - } - }]; -} - - (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; @@ -141,6 +117,27 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } } +- (void)layoutLoadedNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths { + NSAssert(NSThread.isMainThread, @"Main thread layout must be on the main thread."); + + [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, __unused BOOL * stop) { + ASCellNode *node = nodes[idx]; + if (node.isNodeLoaded) { + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + [self _layoutNode:node withConstrainedSize:constrainedSize]; + } + }]; +} + +/** + * Measure and layout the given node with the constrained size range. + */ +- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize +{ + [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); +} + /** * Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store. */ @@ -176,7 +173,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger k = j; k < j + batchCount; k++) { ASCellNode *node = nodes[k]; // Only measure nodes whose views aren't loaded, since we're in the background. - // We should already have measured loaded nodes before we left the main thread, using _layoutNodesWithMainThreadAffinity: + // We should already have measured loaded nodes before we left the main thread, using layoutLoadedNodes:ofKind:atIndexPaths: if (!node.isNodeLoaded) { [self _layoutNode:node withConstrainedSize:nodeBoundSizes[k]]; } @@ -371,7 +368,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _populateFromEntireDataSourceWithMutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; // Measure nodes whose views are loaded before we leave the main thread - [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; + [self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths]; // Allow subclasses to perform setup before going into the edit transaction [self prepareForReloadData]; @@ -550,14 +547,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ - [self prepareForInsertSections:sections]; - NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; [self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; // Measure nodes whose views are loaded before we leave the main thread - [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; + [self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths]; + + [self prepareForInsertSections:sections]; [_editingTransactionQueue addOperationWithBlock:^{ [self willInsertSections:sections]; @@ -604,8 +601,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ - [self prepareForReloadSections:sections]; - NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; [self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; @@ -615,7 +610,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // at this time. Thus _editingNodes could be empty and crash in ASIndexPathsForMultidimensional[...] // Measure nodes whose views are loaded before we leave the main thread - [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; + [self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths]; + + [self prepareForReloadSections:sections]; [_editingTransactionQueue addOperationWithBlock:^{ [self willReloadSections:sections]; @@ -727,7 +724,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } // Measure nodes whose views are loaded before we leave the main thread - [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; + [self layoutLoadedNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ LOG(@"Edit Transaction - insertRows: %@", indexPaths); @@ -777,7 +774,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } // Measure nodes whose views are loaded before we leave the main thread - [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; + [self layoutLoadedNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ LOG(@"Edit Transaction - reloadRows: %@", indexPaths); diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index d17929e7ae..01350da1a3 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -92,7 +92,12 @@ - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return 300; + return 10; +} + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView +{ + return 100; } - (void)collectionViewLockDataSource:(ASCollectionView *)collectionView From 8ac02a6eaf1836623ea2c352fd0a0d19f887d534 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 17:27:53 -0700 Subject: [PATCH 069/127] Document public supplementary node methods --- AsyncDisplayKit/ASCollectionView.h | 20 ++++ .../Details/ASDataController+Subclasses.h | 111 ++++++++++++++---- 2 files changed, 107 insertions(+), 24 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 338792c3d8..1bd6344130 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -126,6 +126,16 @@ */ - (void)reloadData; +/** + * Registers the given kind of supplementary node for use in creating node-backed supplementary views. + * + * @param kind The kind of supplementary node that will be requested through the data source. + * + * @discussion Use this method to register support for the use of supplementary nodes in place of the default + * `registerClass:forSupplementaryViewOfKind:withReuseIdentifier:` and `registerNib:forSupplementaryViewOfKind:withReuseIdentifier:` + * methods. This method will register an internal backing view that will host the contents of the supplementary nodes + * returned from the data source. + */ - (void)registerSupplementaryNodeOfKind:(NSString *)elementKind; /** @@ -281,6 +291,13 @@ @optional +/** + * Asks the collection view to provide a supplementary node to display in the collection view. + * + * @param collectionView An object representing the collection view requesting this information. + * @param kind The kind of supplementary node to provide. + * @param indexPath The index path that specifies the location of the new supplementary node. + */ - (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; /** @@ -355,6 +372,9 @@ @end +/** + * Defines methods that let you coordinate with a `UICollectionViewFlowLayout` in combination with an `ASCollectionView`. + */ @protocol ASCollectionViewDelegateFlowLayout @optional diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 018e58348a..3e33449389 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -10,30 +10,6 @@ @interface ASDataController (Subclasses) -/** - * An opportunity for a subclass to access the data source before entering into the editing queue - */ -- (void)prepareForReloadData; - -/** - * Subclasses can override this to reload data after the abstract data controller deletes its old data and before it reloads the new. - * - * @discussion Invoked on the editing transaction queue. - */ -- (void)willReloadData; - -- (void)prepareForInsertSections:(NSIndexSet *)sections; - -- (void)willInsertSections:(NSIndexSet *)sections; - -- (void)willDeleteSections:(NSIndexSet *)sections; - -- (void)prepareForReloadSections:(NSIndexSet *)sections; - -- (void)willReloadSections:(NSIndexSet *)sections; - -- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection; - #pragma mark - Internal editing & completed store querying /** @@ -93,4 +69,91 @@ */ - (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock; +#pragma mark - Data Manipulation Hooks + +/** + * Notifies the subclass to perform any work needed before the data controller is reloaded entirely + * + * @discussion This method will be performed before the data controller enters its editing queue, usually on the main + * thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or + * data stores before entering into editing the backing store on a background thread. + */ + - (void)prepareForReloadData; + +/** + * Notifies the subclass that the data controller is about to reload its data entirely + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform new node creation like supplementary views + * or header/footer nodes. + */ +- (void)willReloadData; + +/** + * Notifies the subclass to perform setup before sections are inserted in the data controller + * + * @discussion This method will be performed before the data controller enters its editing queue, usually on the main + * thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or + * data stores before entering into editing the backing store on a background thread. + * + * @param sections Indices of sections to be inserted + */ +- (void)prepareForInsertSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass that the data controller will insert new sections at the given position + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform any additional transformations like supplementary views + * or header/footer nodes. + * + * @param sections Indices of sections to be inserted + */ +- (void)willInsertSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass that the data controller will delete sections at the given positions + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform any additional transformations like supplementary views + * or header/footer nodes. + * + * @param sections Indices of sections to be deleted + */ +- (void)willDeleteSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass to perform any work needed before the given sections will be reloaded. + * + * @discussion This method will be performed before the data controller enters its editing queue, usually on the main + * thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or + * data stores before entering into editing the backing store on a background thread. + * + * @param sections Indices of sections to be reloaded + */ +- (void)prepareForReloadSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass that the data controller will reload the sections in the given index set + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform any additional transformations like supplementary views + * or header/footer nodes. + * + * @param sections Indices of sections to be reloaded + */ +- (void)willReloadSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass that the data controller will move a section to a new position + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform any additional transformations like supplementary views + * or header/footer nodes. + * + * @param section Index of current section position + * @param newSection Index of new section position + */ +- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection; + @end From e2bbde2a4e8ba2fb299c377cb7826cbf5f56d1bc Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 18:19:40 -0700 Subject: [PATCH 070/127] Clean up usage of internal flow layout inspector --- AsyncDisplayKit/ASCollectionView.mm | 11 +++- .../ASCollectionViewFlowLayoutInspector.h | 3 +- .../ASCollectionViewFlowLayoutInspector.m | 56 ++++++++++--------- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index c16715b4eb..36d700a0c6 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -337,7 +337,16 @@ static BOOL _isInterceptedSelector(SEL sel) _asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); } - _flowLayoutInspector.collectionView = self; + [_flowLayoutInspector cacheSelectorsForCollectionView:self]; +} + +- (void)setCollectionViewLayout:(UICollectionViewLayout *)collectionViewLayout +{ + [super setCollectionViewLayout:collectionViewLayout]; + if ([collectionViewLayout asdk_isFlowLayout]) { + _flowLayoutInspector = nil; + _layoutDelegate = [self flowLayoutInspector]; + } } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 94d65e2117..4e4f266cfd 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -34,10 +34,11 @@ @interface ASCollectionViewFlowLayoutInspector : NSObject @property (nonatomic, weak) UICollectionViewFlowLayout *layout; -@property (nonatomic, weak) ASCollectionView *collectionView; - (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout; +- (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView; + - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; - (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index efdb26bdaf..f9ea2f8293 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -19,26 +19,30 @@ #pragma mark - Accessors -- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout +- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout; { - self = [super init]; + self = [super init]; - if (flowLayout == nil) { - return nil; - } - - if (self != nil) { - self.collectionView = collectionView; - _layout = flowLayout; - } - return self; + if (flowLayout == nil) { + return nil; + } + + if (self != nil) { + [self cacheSelectorForCollectionView:collectionView]; + _layout = flowLayout; + } + return self; } -- (void)setCollectionView:(ASCollectionView *)collectionView +- (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView { - _collectionView = collectionView; - _delegateImplementsReferenceSizeForHeader = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; - _delegateImplementsReferenceSizeForFooter = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; + if (collectionView == nil) { + _delegateImplementsReferenceSizeForHeader = nil; + _delegateImplementsReferenceSizeForFooter = nil; + } else { + _delegateImplementsReferenceSizeForHeader = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; + _delegateImplementsReferenceSizeForFooter = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; + } } #pragma mark - ASCollectionViewLayoutInspecting @@ -46,11 +50,11 @@ - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { CGSize constrainedSize; - CGSize supplementarySize = [self sizeForSupplementaryViewOfKind:kind inSection:indexPath.section]; + CGSize supplementarySize = [self sizeForSupplementaryViewOfKind:kind inSection:indexPath.section collectionView:collectionView]; if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) { - constrainedSize = CGSizeMake(_collectionView.bounds.size.width, supplementarySize.height); + constrainedSize = CGSizeMake(collectionView.bounds.size.width, supplementarySize.height); } else { - constrainedSize = CGSizeMake(supplementarySize.height, _collectionView.bounds.size.height); + constrainedSize = CGSizeMake(supplementarySize.height, collectionView.bounds.size.height); } return ASSizeRangeMake(CGSizeZero, constrainedSize); } @@ -66,22 +70,22 @@ - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { - return [self layoutHasSupplementaryViewOfKind:kind inSection:section] ? 1 : 0; + return [self layoutHasSupplementaryViewOfKind:kind inSection:section collectionView:collectionView] ? 1 : 0; } #pragma mark - Private helpers -- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section +- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section collectionView:(ASCollectionView *)collectionView { if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { if (_delegateImplementsReferenceSizeForHeader) { - return [[self layoutDelegate] collectionView:_collectionView layout:_layout referenceSizeForHeaderInSection:section]; + return [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForHeaderInSection:section]; } else { return [self.layout headerReferenceSize]; } } else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { if (_delegateImplementsReferenceSizeForFooter) { - return [[self layoutDelegate] collectionView:_collectionView layout:_layout referenceSizeForFooterInSection:section]; + return [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForFooterInSection:section]; } else { return [self.layout footerReferenceSize]; } @@ -90,9 +94,9 @@ } } -- (BOOL)layoutHasSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section +- (BOOL)layoutHasSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section collectionView:(ASCollectionView *)collectionView { - CGSize size = [self sizeForSupplementaryViewOfKind:kind inSection:section]; + CGSize size = [self sizeForSupplementaryViewOfKind:kind inSection:section collectionView:collectionView]; if ([self usedLayoutValueForSize:size] > 0) { return YES; } else { @@ -109,9 +113,9 @@ } } -- (id)layoutDelegate +- (id)delegateForCollectionView:(ASCollectionView *)collectionView { - return (id)_collectionView.asyncDelegate; + return (id)collectionView.asyncDelegate; } @end From 0446902c1760934c14900cf64a874543a3131a1c Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 18:23:13 -0700 Subject: [PATCH 071/127] Remove supplementary view assertion --- AsyncDisplayKit/ASCollectionView.mm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 36d700a0c6..b7456be836 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -306,10 +306,6 @@ static BOOL _isInterceptedSelector(SEL sel) _asyncDataSourceImplementsConstrainedSizeForNode = NO; } else { _asyncDataSource = asyncDataSource; - // TODO: Support supplementary views with ASCollectionView. - if ([_asyncDataSource respondsToSelector:@selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:)]) { - ASDisplayNodeAssert(NO, @"ASCollectionView is planned to support supplementary views by September 2015. You can work around this issue by using standard items."); - } _proxyDataSource = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; super.dataSource = (id)_proxyDataSource; _asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0); From 772e9b95a4ade69a36fc9bc4a59a133b3f41c292 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 18:41:05 -0700 Subject: [PATCH 072/127] Document layout delegate on ASCollectionView --- AsyncDisplayKit/ASCollectionView.h | 9 +++++++-- AsyncDisplayKit/ASCollectionView.mm | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 1bd6344130..14ec90f894 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -63,7 +63,7 @@ * * @param asyncDataFetchingEnabled Enable the data fetching in async mode. * - * @discussion If asyncDataFetching is enabled, the `AScollectionView` will fetch data through `collectionView:numberOfRowsInSection:` and + * @discussion If asyncDataFetching is enabled, the `ASCollectionView` will fetch data through `collectionView:numberOfRowsInSection:` and * `collectionView:nodeForRowAtIndexPath:` in async mode from background thread. Otherwise, the methods will be invoked synchronically * from calling thread. * Enabling asyncDataFetching could avoid blocking main thread for `ASCellNode` allocation, which is frequently reported issue for @@ -83,7 +83,12 @@ /** * Optional introspection object for the collection view's layout. * - * TODO: Discuss more about this delegate + * @discussion Since supplementary and decoration views are controlled by the collection view's layout, this object + * is used as a bridge to provide information to the internal data controller about the existence of these views and + * their associated index paths. For collection views using `UICollectionViewFlowLayout`, a default inspector + * implementation `ASCollectionViewFlowLayoutInspector` is created and set on this property by default. Custom + * collection view layout subclasses will need to provide their own implementation of an inspector object for their + * supplementary views to be compatible with `ASCollectionView`'s supplementary node support. */ @property (nonatomic, weak) id layoutDelegate; diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index b7456be836..ccb1765c91 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -226,8 +226,8 @@ static BOOL _isInterceptedSelector(SEL sel) // and should not trigger a relayout. _ignoreMaxSizeChange = CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, CGSizeZero); - // Register the default layout inspector delegate for flow layouts, custom layouts - // will need to roll their own ASCollectionViewLayoutInspecting implementation. + // Register the default layout inspector delegate for flow layouts only, custom layouts + // will need to roll their own ASCollectionViewLayoutInspecting implementation and set a layout delegate if ([layout asdk_isFlowLayout]) { _layoutDelegate = [self flowLayoutInspector]; } From 12194199cd028238e561568b6640ea71d317f5a6 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 18:48:30 -0700 Subject: [PATCH 073/127] Clean up layout delegate protocol API --- AsyncDisplayKit/ASCollectionView.mm | 8 ++++---- .../Details/ASCollectionDataController.h | 4 ++-- .../Details/ASCollectionDataController.mm | 8 ++++---- .../Details/ASCollectionViewFlowLayoutInspector.h | 12 +++--------- .../Details/ASCollectionViewFlowLayoutInspector.m | 6 +++--- .../ASCollectionViewFlowLayoutInspectorTests.m | 14 +++++++------- 6 files changed, 23 insertions(+), 29 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index ccb1765c91..b312b86c10 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -754,16 +754,16 @@ static BOOL _isInterceptedSelector(SEL sel) return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; } -- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section +- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section { ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); - return [_layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; + return [_layoutDelegate collectionView:self supplementaryNodesOfKind:kind inSection:section]; } -- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; +- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind; { ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); - return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; + return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryNodeOfKind:kind]; } #pragma mark - ASRangeControllerDelegate. diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h index 9c2aa46b68..54ddfbb810 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.h +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -26,9 +26,9 @@ - (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController; -- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; +- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind; -- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; +- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section; @end diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 0bbc2c502c..ca5a48a84c 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -59,7 +59,7 @@ [self deleteSectionsOfKind:kind atIndexSet:indexSet completion:nil]; // Insert each section - NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; + NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryNodeOfKind:kind]; NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount]; for (int i = 0; i < sectionCount; i++) { [sections addObject:[[NSMutableArray alloc] init]]; @@ -163,10 +163,10 @@ - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { - NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; + NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryNodeOfKind:kind]; for (NSUInteger i = 0; i < sectionCount; i++) { NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i]; - NSUInteger rowCount = [self.collectionDataSource dataController:self supplementaryViewsOfKind:kind inSection:i]; + NSUInteger rowCount = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:i]; for (NSUInteger j = 0; j < rowCount; j++) { NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j]; [indexPaths addObject:indexPath]; @@ -178,7 +178,7 @@ - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withSections:(NSIndexSet *)sections mutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { - NSUInteger rowNum = [self.collectionDataSource dataController:self supplementaryViewsOfKind:kind inSection:idx]; + NSUInteger rowNum = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:idx]; NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx]; for (NSUInteger i = 0; i < rowNum; i++) { NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i]; diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 4e4f266cfd..5aab03e012 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -15,19 +15,19 @@ @protocol ASCollectionViewLayoutInspecting /** - * Asks the inspector + * Asks the inspector to provide a constrained size range for the given supplementary node. */ - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; /** * Asks the inspector for the number of supplementary sections in the collection view for the given kind. */ -- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind; /** * Asks the inspector for the number of supplementary views for the given kind in the specified section. */ -- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section; @end @@ -39,10 +39,4 @@ - (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView; -- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; - -- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; - -- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; - @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index f9ea2f8293..e68020490d 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -28,7 +28,7 @@ } if (self != nil) { - [self cacheSelectorForCollectionView:collectionView]; + [self cacheSelectorsForCollectionView:collectionView]; _layout = flowLayout; } return self; @@ -59,7 +59,7 @@ return ASSizeRangeMake(CGSizeZero, constrainedSize); } -- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind { if ([collectionView.asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) { return [collectionView.asyncDataSource numberOfSectionsInCollectionView:collectionView]; @@ -68,7 +68,7 @@ } } -- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section { return [self layoutHasSupplementaryViewOfKind:kind inSection:section collectionView:collectionView] ? 1 : 0; } diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m index 78171d8c32..53c0956c36 100644 --- a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -259,14 +259,14 @@ XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a zero size"); } -#pragma mark - #collectionView:numberOfSectionsForSupplementaryKind: +#pragma mark - #collectionView:numberOfSectionsForSupplementaryNodeOfKind: - (void)testThatItRespondsWithTheDefaultNumberOfSections { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; - NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryKind:UICollectionElementKindSectionHeader]; + NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; XCTAssert(sections == 1, @"should return 1 by default"); } @@ -277,11 +277,11 @@ ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; collectionView.asyncDataSource = dataSource; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; - NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryKind:UICollectionElementKindSectionHeader]; + NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; XCTAssert(sections == 2, @"should return 2"); } -#pragma mark - #collectionView:supplementaryViewsOfKind:inSection: +#pragma mark - #collectionView:supplementaryNodesOfKind:inSection: - (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheDelegate { @@ -292,7 +292,7 @@ collectionView.asyncDataSource = dataSource; collectionView.asyncDelegate = delegate; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; - NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionHeader inSection:0]; + NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionHeader inSection:0]; XCTAssert(count == 1, @"should have a header supplementary view"); } @@ -306,7 +306,7 @@ collectionView.asyncDataSource = dataSource; collectionView.asyncDelegate = delegate; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; - NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionFooter inSection:0]; + NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionFooter inSection:0]; XCTAssert(count == 1, @"should have a footer supplementary view"); } @@ -319,7 +319,7 @@ collectionView.asyncDataSource = dataSource; collectionView.asyncDelegate = delegate; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; - NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionFooter inSection:0]; + NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionFooter inSection:0]; XCTAssert(count == 0, @"should not have a footer supplementary view"); } From 9a37538582457e25c27bfa31b627f9dfb4ca240d Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 18:59:27 -0700 Subject: [PATCH 074/127] Clean up and test registration of internal supplementary node kinds --- .../Details/ASCollectionDataController.mm | 20 +++++++++---------- AsyncDisplayKitTests/ASCollectionViewTests.m | 15 ++++++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index ca5a48a84c..45f1d3af37 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -33,8 +33,7 @@ _pendingNodes = [NSMutableDictionary dictionary]; _pendingIndexPaths = [NSMutableDictionary dictionary]; - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + [[self supplementaryKinds] enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { LOG(@"Populating elements of kind: %@", kind); NSMutableArray *indexPaths = [NSMutableArray array]; NSMutableArray *nodes = [NSMutableArray array]; @@ -76,8 +75,7 @@ - (void)prepareForInsertSections:(NSIndexSet *)sections { - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + [[self supplementaryKinds] enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { LOG(@"Populating elements of kind: %@, for sections: %@", kind, sections); NSMutableArray *nodes = [NSMutableArray array]; NSMutableArray *indexPaths = [NSMutableArray array]; @@ -107,8 +105,7 @@ - (void)willDeleteSections:(NSIndexSet *)sections { - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + [[self supplementaryKinds] enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], sections); [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; @@ -118,8 +115,7 @@ - (void)prepareForReloadSections:(NSIndexSet *)sections { - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + [[self supplementaryKinds] enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { NSMutableArray *nodes = [NSMutableArray array]; NSMutableArray *indexPaths = [NSMutableArray array]; [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths]; @@ -145,8 +141,7 @@ - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection { - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + [[self supplementaryKinds] enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], [NSIndexSet indexSetWithIndex:section]); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths([self editingNodesOfKind:kind], indexPaths); [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; @@ -209,6 +204,11 @@ #pragma mark - Private Helpers +- (NSArray *)supplementaryKinds +{ + return [self.collectionDataSource supplementaryNodeKindsInDataController:self]; +} + - (id)collectionDataSource { return (id)self.dataSource; diff --git a/AsyncDisplayKitTests/ASCollectionViewTests.m b/AsyncDisplayKitTests/ASCollectionViewTests.m index 1993d0734b..db993713b5 100644 --- a/AsyncDisplayKitTests/ASCollectionViewTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewTests.m @@ -7,6 +7,7 @@ #import #import "ASCollectionView.h" +#import "ASCollectionDataController.h" #import "ASCollectionViewFlowLayoutInspector.h" @interface ASCollectionViewTestDelegate : NSObject @@ -74,6 +75,12 @@ @end +@interface ASCollectionView (InternalTesting) + +- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController; + +@end + @interface ASCollectionViewTests : XCTestCase @end @@ -95,6 +102,14 @@ XCTAssert(collectionView.layoutDelegate == nil, @"should not set a layout delegate for custom layouts"); } +- (void)testThatRegisteringASupplementaryNodeStoresItForIntrospection +{ + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; + [collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; + XCTAssertEqualObjects([collectionView supplementaryNodeKindsInDataController:nil], @[UICollectionElementKindSectionHeader]); +} + - (void)DISABLED_testCollectionViewController { ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil]; From 31f184b32dadcab9e9363f9f77af7bdeafdade35 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 20:20:19 -0700 Subject: [PATCH 075/127] Clear collection view delegate/dataSource on test tear down --- ...ASCollectionViewFlowLayoutInspectorTests.m | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m index 53c0956c36..1230807f44 100644 --- a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -108,6 +108,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsAVerticalConstrainedSizeFromTheFooterDelegateImplementation @@ -127,6 +130,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } // Size implementation @@ -147,6 +153,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsAVerticalConstrainedSizeFromTheFooterProperty @@ -165,6 +174,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } // Horizontal @@ -186,6 +198,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsAHorizontalConstrainedSizeFromTheFooterDelegateImplementation @@ -205,6 +220,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } // Size implementation @@ -225,6 +243,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.width)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsAHorizontalConstrainedSizeFromTheFooterProperty @@ -243,6 +264,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsZeroSizeWhenNoReferenceSizeIsImplemented @@ -257,6 +281,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeZero); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a zero size"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } #pragma mark - #collectionView:numberOfSectionsForSupplementaryNodeOfKind: @@ -268,6 +295,9 @@ ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; XCTAssert(sections == 1, @"should return 1 by default"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItProvidesTheNumberOfSectionsInTheDataSource @@ -279,6 +309,9 @@ ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; XCTAssert(sections == 2, @"should return 2"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } #pragma mark - #collectionView:supplementaryNodesOfKind:inSection: @@ -294,6 +327,9 @@ ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionHeader inSection:0]; XCTAssert(count == 1, @"should have a header supplementary view"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheLayout @@ -308,6 +344,9 @@ ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionFooter inSection:0]; XCTAssert(count == 1, @"should have a footer supplementary view"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsNoneWhenNoReferenceSizeIsImplemented @@ -321,6 +360,9 @@ ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionFooter inSection:0]; XCTAssert(count == 0, @"should not have a footer supplementary view"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } @end From 55c1b8f6e74917c45cf3d8716dbf09b59d236c0e Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 23:33:06 -0700 Subject: [PATCH 076/127] Fix variable naming in debug log --- AsyncDisplayKit/Details/ASDataController.mm | 4 ++-- AsyncDisplayKit/Details/ASRangeController.mm | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 24dcd68d2c..400a1161fd 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -543,7 +543,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - insertSections: %@", indexSet); + LOG(@"Edit Command - insertSections: %@", sections); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ @@ -559,7 +559,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ [self willInsertSections:sections]; - LOG(@"Edit Transaction - insertSections: %@", indexSet); + LOG(@"Edit Transaction - insertSections: %@", sections); NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count]; for (NSUInteger i = 0; i < sections.count; i++) { [sectionArray addObject:[NSMutableArray array]]; diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 266fb1a855..146b85897f 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -75,7 +75,7 @@ // coalesce these events -- handling them multiple times per runloop is noisy and expensive _queuedRangeUpdate = YES; - + [self performSelector:@selector(updateVisibleNodeIndexPaths) withObject:nil afterDelay:0 From 8b7ac3c37dc44e6477680fd445edb4cadfc7dd95 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 13 Oct 2015 20:45:40 -0700 Subject: [PATCH 077/127] Ensure layout controller exposes only cell item index paths to range controller --- AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm index cbed7f2bb7..9619cba111 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm +++ b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm @@ -137,7 +137,9 @@ typedef struct ASRangeGeometry ASRangeGeometry; NSMutableSet *indexPathSet = [[NSMutableSet alloc] init]; NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds]; for (UICollectionViewLayoutAttributes *la in layoutAttributes) { - [indexPathSet addObject:la.indexPath]; + if (la.representedElementCategory == UICollectionElementCategoryCell) { + [indexPathSet addObject:la.indexPath]; + } } return indexPathSet; } From 3b983c5a5075ae7db3a906a303b0b050a15bf482 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 16 Oct 2015 16:15:29 -0700 Subject: [PATCH 078/127] Ascii art for layoutables --- AsyncDisplayKit/ASDisplayNode.h | 4 +- AsyncDisplayKit/ASDisplayNode.mm | 13 ++ AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h | 59 ++++++ AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m | 185 ++++++++++++++++++ AsyncDisplayKit/Layout/ASLayoutSpec.h | 9 +- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 33 +++- AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm | 7 + AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm | 7 + AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 7 + AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 7 + 10 files changed, 327 insertions(+), 4 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h create mode 100644 AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 11a138ea4f..bdd7ec6d3e 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -12,7 +12,7 @@ #import #import #import - +#import #import @class ASDisplayNode; @@ -48,7 +48,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); * */ -@interface ASDisplayNode : ASDealloc2MainObject +@interface ASDisplayNode : ASDealloc2MainObject /** @name Initializing a node object */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index c5beb9347b..0f02203fdb 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -2074,6 +2074,19 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, return self; } + +#pragma mark - ASLayoutableAsciiArtProtocol + +- (NSString *)asciiArtString +{ + return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]]; +} + +- (NSString *)asciiArtName +{ + return NSStringFromClass([self class]); +} + @end @implementation ASDisplayNode (Debugging) diff --git a/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h b/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h new file mode 100644 index 0000000000..3be6ed88de --- /dev/null +++ b/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +@protocol ASLayoutableAsciiArtProtocol +/** + * Returns an ascii-art representation of this object and its children. + * For example, an ASInsetSpec may return something like this: + * + * --ASInsetLayoutSpec-- + * | ASTextNode | + * --------------------- + */ +- (NSString *)asciiArtString; + +/** + * returns the name of this object that will display in the ascii art. Usually this can + * simply be NSStringFromClass([self class]). + */ +- (NSString *)asciiArtName; + +@end + +/** + * A that takes a parent and its children and renders as ascii art box. + */ +@interface ASAsciiArtBoxCreator : NSObject + +/** + * Renders an ascii art box with the children aligned horizontally + * Example: + * ------------ASStackLayoutSpec----------- + * | ASTextNode ASTextNode ASTextNode | + * ---------------------------------------- + */ ++ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent; + +/** + * Renders an ascii art box with the children aligned vertically. + * Example: + * --ASStackLayoutSpec-- + * | ASTextNode | + * | ASTextNode | + * | ASTextNode | + * --------------------- + */ ++ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent; + +@end + + diff --git a/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m b/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m new file mode 100644 index 0000000000..9ec7e213bf --- /dev/null +++ b/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +@import UIKit; +#import "ASAsciiArtBoxCreator.h" + +static const NSUInteger kDebugBoxPadding = 2; + +typedef NS_ENUM(NSUInteger, PIDebugBoxPaddingLocation) +{ + PIDebugBoxPaddingLocationFront, + PIDebugBoxPaddingLocationEnd, + PIDebugBoxPaddingLocationBoth +}; + +@interface NSString(PIDebugBox) + +@end + +@implementation NSString(PIDebugBox) + ++ (instancetype)debugbox_stringWithString:(NSString *)stringToRepeat repeatedCount:(NSUInteger)repeatCount +{ + NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[stringToRepeat length] * repeatCount]; + for (NSUInteger index = 0; index < repeatCount; index++) { + [string appendString:stringToRepeat]; + } + return [string copy]; +} + +- (NSString *)debugbox_stringByAddingPadding:(NSString *)padding count:(NSUInteger)count location:(PIDebugBoxPaddingLocation)location +{ + NSString *paddingString = [NSString debugbox_stringWithString:padding repeatedCount:count]; + switch (location) { + case PIDebugBoxPaddingLocationFront: + return [NSString stringWithFormat:@"%@%@", paddingString, self]; + case PIDebugBoxPaddingLocationEnd: + return [NSString stringWithFormat:@"%@%@", self, paddingString]; + case PIDebugBoxPaddingLocationBoth: + return [NSString stringWithFormat:@"%@%@%@", paddingString, self, paddingString]; + } + return [self copy]; +} + +@end + +@implementation ASAsciiArtBoxCreator + ++ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent +{ + if ([children count] == 0) { + return parent; + } + + NSMutableArray *> *childrenLines = [NSMutableArray array]; + + // split the children into lines + NSUInteger lineCountPerChild = 0; + for (NSString *child in children) { + NSArray *lines = [child componentsSeparatedByString:@"\n"]; + lineCountPerChild = MAX(lineCountPerChild, [lines count]); + } + + for (NSString *child in children) { + NSMutableArray *lines = [[child componentsSeparatedByString:@"\n"] mutableCopy]; + NSUInteger topPadding = ceilf((CGFloat)(lineCountPerChild - [lines count])/2.0); + NSUInteger bottomPadding = (lineCountPerChild - [lines count])/2.0; + NSUInteger lineLength = [lines[0] length]; + + for (NSUInteger index = 0; index < topPadding; index++) { + [lines insertObject:[NSString debugbox_stringWithString:@" " repeatedCount:lineLength] atIndex:0]; + } + for (NSUInteger index = 0; index < bottomPadding; index++) { + [lines addObject:[NSString debugbox_stringWithString:@" " repeatedCount:lineLength]]; + } + [childrenLines addObject:lines]; + } + + NSMutableArray *concatenatedLines = [NSMutableArray array]; + NSString *padding = [NSString debugbox_stringWithString:@" " repeatedCount:kDebugBoxPadding]; + for (NSUInteger index = 0; index < lineCountPerChild; index++) { + NSMutableString *line = [[NSMutableString alloc] init]; + [line appendFormat:@"|%@",padding]; + for (NSArray *childLines in childrenLines) { + [line appendFormat:@"%@%@", childLines[index], padding]; + } + [line appendString:@"|"]; + [concatenatedLines addObject:line]; + } + + // surround the lines in a box + NSUInteger totalLineLength = [concatenatedLines[0] length]; + if (totalLineLength < [parent length]) { + NSUInteger difference = [parent length] + (2 * kDebugBoxPadding) - totalLineLength; + NSUInteger leftPadding = ceilf((CGFloat)difference/2.0); + NSUInteger rightPadding = difference/2; + + NSString *leftString = [@"|" debugbox_stringByAddingPadding:@" " count:leftPadding location:PIDebugBoxPaddingLocationEnd]; + NSString *rightString = [@"|" debugbox_stringByAddingPadding:@" " count:rightPadding location:PIDebugBoxPaddingLocationFront]; + + NSMutableArray *paddedLines = [NSMutableArray array]; + for (NSString *line in concatenatedLines) { + NSString *paddedLine = [line stringByReplacingOccurrencesOfString:@"|" withString:leftString options:NSCaseInsensitiveSearch range:NSMakeRange(0, 1)]; + paddedLine = [paddedLine stringByReplacingOccurrencesOfString:@"|" withString:rightString options:NSCaseInsensitiveSearch range:NSMakeRange([paddedLine length] - 1, 1)]; + [paddedLines addObject:paddedLine]; + } + concatenatedLines = paddedLines; + totalLineLength += difference; + } + concatenatedLines = [self appendTopAndBottomToBoxString:concatenatedLines parent:parent]; + return [concatenatedLines componentsJoinedByString:@"\n"]; + +} + ++ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent +{ + if ([children count] == 0) { + return parent; + } + + NSMutableArray *childrenLines = [NSMutableArray array]; + + NSUInteger maxChildLength = 0; + for (NSString *child in children) { + NSArray *lines = [child componentsSeparatedByString:@"\n"]; + maxChildLength = MAX(maxChildLength, [lines[0] length]); + } + + NSUInteger rightPadding = 0; + NSUInteger leftPadding = 0; + + if (maxChildLength < [parent length]) { + NSUInteger difference = [parent length] + (2 * kDebugBoxPadding) - maxChildLength; + leftPadding = ceilf((CGFloat)difference/2.0); + rightPadding = difference/2; + } + + NSString *rightPaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:rightPadding + kDebugBoxPadding]; + NSString *leftPaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:leftPadding + kDebugBoxPadding]; + + for (NSString *child in children) { + NSMutableArray *lines = [[child componentsSeparatedByString:@"\n"] mutableCopy]; + + NSUInteger leftLinePadding = ceilf((CGFloat)(maxChildLength - [lines[0] length])/2.0); + NSUInteger rightLinePadding = (maxChildLength - [lines[0] length])/2.0; + + for (NSString *line in lines) { + NSString *rightLinePaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:rightLinePadding]; + rightLinePaddingString = [NSString stringWithFormat:@"%@%@|", rightLinePaddingString, rightPaddingString]; + + NSString *leftLinePaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:leftLinePadding]; + leftLinePaddingString = [NSString stringWithFormat:@"|%@%@", leftLinePaddingString, leftPaddingString]; + + NSString *paddingLine = [NSString stringWithFormat:@"%@%@%@", leftLinePaddingString, line, rightLinePaddingString]; + [childrenLines addObject:paddingLine]; + } + } + + childrenLines = [self appendTopAndBottomToBoxString:childrenLines parent:parent]; + return [childrenLines componentsJoinedByString:@"\n"]; +} + ++ (NSMutableArray *)appendTopAndBottomToBoxString:(NSMutableArray *)boxStrings parent:(NSString *)parent +{ + NSUInteger totalLineLength = [boxStrings[0] length]; + [boxStrings addObject:[NSString debugbox_stringWithString:@"-" repeatedCount:totalLineLength]]; + + NSUInteger leftPadding = ceilf(((CGFloat)(totalLineLength - [parent length]))/2.0); + NSUInteger rightPadding = (totalLineLength - [parent length])/2; + + NSString *topLine = [parent debugbox_stringByAddingPadding:@"-" count:leftPadding location:PIDebugBoxPaddingLocationFront]; + topLine = [topLine debugbox_stringByAddingPadding:@"-" count:rightPadding location:PIDebugBoxPaddingLocationEnd]; + [boxStrings insertObject:topLine atIndex:0]; + + return boxStrings; +} + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index 2dd00198ff..4d5a89f5e8 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -9,9 +9,10 @@ */ #import +#import /** A layout spec is an immutable object that describes a layout, loosely inspired by React. */ -@interface ASLayoutSpec : NSObject +@interface ASLayoutSpec : NSObject /** * Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a @@ -95,4 +96,10 @@ /** Returns all children added to this layout spec. */ - (NSArray *)children; +/** + * Used by other layout specs to create ascii art debug strings + */ ++ (NSString *)asciiArtStringForChildren:(NSArray> *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction; ++ (NSString *)asciiArtStringForChildren:(NSArray> *)children parentName:(NSString *)parentName; + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 3ba312709b..fc6db2e71f 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -122,5 +122,36 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[kDefaultChildrenKey]; } - +#pragma mark - ASLayoutableAsciiArtProtocol + ++ (NSString *)asciiArtStringForChildren:(NSArray> *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction +{ + NSMutableArray *childStrings = [NSMutableArray array]; + for (id layoutChild in children) { + NSString *childString = [layoutChild asciiArtString]; + if (childString) { + [childStrings addObject:childString]; + } + } + if (direction == ASStackLayoutDirectionHorizontal) { + return [ASAsciiArtBoxCreator horizontalBoxStringForChildren:childStrings parent:parentName]; + } + return [ASAsciiArtBoxCreator verticalBoxStringForChildren:childStrings parent:parentName]; +} + ++ (NSString *)asciiArtStringForChildren:(NSArray> *)children parentName:(NSString *)parentName +{ + return [self asciiArtStringForChildren:children parentName:parentName direction:ASStackLayoutDirectionHorizontal]; +} + +- (NSString *)asciiArtString +{ + return [ASLayoutSpec asciiArtStringForChildren:@[self.child] parentName:[self asciiArtName]]; +} + +- (NSString *)asciiArtName +{ + return NSStringFromClass([self class]); +} + @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index 1fdfd5fc28..31c38a1deb 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -72,4 +72,11 @@ static NSString * const kOverlayChildKey = @"kOverlayChildKey"; return nil; } +#pragma mark - ASLayoutableAsciiArtProtocol + +- (NSString *)debugBoxString +{ + return [ASLayoutSpec asciiArtStringForChildren:@[self.overlay, self.child] parentName:[self asciiArtName]]; +} + @end diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index eac7464aa3..236b27a308 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -86,4 +86,11 @@ return nil; } +#pragma mark - ASLayoutableAsciiArtProtocol + +- (NSString *)asciiArtName +{ + return [NSString stringWithFormat:@"%@ (%.1f)", NSStringFromClass([self class]), self.ratio]; +} + @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index a1d27b3c5e..d1476d1e6b 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -135,4 +135,11 @@ sublayouts:sublayouts]; } +#pragma mark - ASLayoutableAsciiArtProtocol + +- (NSString *)asciiArtString +{ + return [ASLayoutSpec asciiArtStringForChildren:self.children parentName:[self asciiArtName] direction:self.direction]; +} + @end diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 7652247f9b..300fb045c3 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -84,4 +84,11 @@ return nil; } +#pragma mark - ASLayoutableAsciiArtProtocol + +- (NSString *)debugBoxString +{ + return [ASLayoutSpec asciiArtStringForChildren:self.children parentName:[self asciiArtName]]; +} + @end From f164b9770022adadaf17994f5c01ac009c965f29 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Mon, 19 Oct 2015 08:32:14 -0700 Subject: [PATCH 079/127] Put all ascii box methods into a Debugging category. --- AsyncDisplayKit/ASDisplayNode.h | 5 +++- AsyncDisplayKit/ASDisplayNode.mm | 25 +++++++++---------- AsyncDisplayKit/Layout/ASLayoutSpec.h | 4 ++- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 4 +++ AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm | 4 +++ AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm | 4 +++ AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 4 +++ AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 4 +++ 8 files changed, 39 insertions(+), 15 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index bdd7ec6d3e..f7d9e8483e 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -48,7 +48,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); * */ -@interface ASDisplayNode : ASDealloc2MainObject +@interface ASDisplayNode : ASDealloc2MainObject /** @name Initializing a node object */ @@ -659,6 +659,9 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); - (void)addSubnode:(ASDisplayNode *)node; @end +@interface ASDisplayNode (Debugging) +@end + @interface ASDisplayNode (Deprecated) - (void)reclaimMemory ASDISPLAYNODE_DEPRECATED; diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 0f02203fdb..2c536006e0 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -2074,19 +2074,6 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, return self; } - -#pragma mark - ASLayoutableAsciiArtProtocol - -- (NSString *)asciiArtString -{ - return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]]; -} - -- (NSString *)asciiArtName -{ - return NSStringFromClass([self class]); -} - @end @implementation ASDisplayNode (Debugging) @@ -2150,6 +2137,18 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, return subtree; } +#pragma mark - ASLayoutableAsciiArtProtocol + +- (NSString *)asciiArtString +{ + return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]]; +} + +- (NSString *)asciiArtName +{ + return NSStringFromClass([self class]); +} + @end // We use associated objects as a last resort if our view is not a _ASDisplayView ie it doesn't have the _node ivar to write to diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index 4d5a89f5e8..cd177ce891 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -12,7 +12,7 @@ #import /** A layout spec is an immutable object that describes a layout, loosely inspired by React. */ -@interface ASLayoutSpec : NSObject +@interface ASLayoutSpec : NSObject /** * Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a @@ -95,7 +95,9 @@ /** Returns all children added to this layout spec. */ - (NSArray *)children; +@end +@interface ASLayoutSpec (Debugging) /** * Used by other layout specs to create ascii art debug strings */ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index fc6db2e71f..f14fdca6cc 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -122,6 +122,10 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[kDefaultChildrenKey]; } +@end + +@implementation ASLayoutSpec (Debugging) + #pragma mark - ASLayoutableAsciiArtProtocol + (NSString *)asciiArtStringForChildren:(NSArray> *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index 31c38a1deb..ba580d08b1 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -72,6 +72,10 @@ static NSString * const kOverlayChildKey = @"kOverlayChildKey"; return nil; } +@end + +@implementation ASOverlayLayoutSpec (Debugging) + #pragma mark - ASLayoutableAsciiArtProtocol - (NSString *)debugBoxString diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index 236b27a308..4e04d4ca05 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -86,6 +86,10 @@ return nil; } +@end + +@implementation ASRatioLayoutSpec (Debugging) + #pragma mark - ASLayoutableAsciiArtProtocol - (NSString *)asciiArtName diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index d1476d1e6b..2269ffffeb 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -135,6 +135,10 @@ sublayouts:sublayouts]; } +@end + +@implementation ASStackLayoutSpec (Debugging) + #pragma mark - ASLayoutableAsciiArtProtocol - (NSString *)asciiArtString diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 300fb045c3..f346017316 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -84,6 +84,10 @@ return nil; } +@end + +@implementation ASStaticLayoutSpec (Debugging) + #pragma mark - ASLayoutableAsciiArtProtocol - (NSString *)debugBoxString From 5a3b0215101f2b3dc117af5081615863847b8ed4 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Mon, 19 Oct 2015 08:57:17 -0700 Subject: [PATCH 080/127] fixed build --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 ++++++++++++ AsyncDisplayKit/ASDisplayNode.h | 5 +---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 1a58fabfe9..32a6695692 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -208,6 +208,10 @@ 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5586691BD549CB00B50E3A /* ASAsciiArtBoxCreator.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5586671BD549CB00B50E3A /* ASAsciiArtBoxCreator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C55866A1BD549CB00B50E3A /* ASAsciiArtBoxCreator.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5586681BD549CB00B50E3A /* ASAsciiArtBoxCreator.m */; settings = {ASSET_TAGS = (); }; }; + 9C55866B1BD54A1900B50E3A /* ASAsciiArtBoxCreator.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5586681BD549CB00B50E3A /* ASAsciiArtBoxCreator.m */; settings = {ASSET_TAGS = (); }; }; + 9C55866C1BD54A3000B50E3A /* ASAsciiArtBoxCreator.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5586671BD549CB00B50E3A /* ASAsciiArtBoxCreator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */; }; @@ -567,6 +571,8 @@ 4640521D1A3F83C40061C0BA /* ASLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutController.h; sourceTree = ""; }; 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AsyncDisplayKit.h; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; + 9C5586671BD549CB00B50E3A /* ASAsciiArtBoxCreator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASAsciiArtBoxCreator.h; path = AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h; sourceTree = ""; }; + 9C5586681BD549CB00B50E3A /* ASAsciiArtBoxCreator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASAsciiArtBoxCreator.m; path = AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m; sourceTree = ""; }; 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptions.h; path = AsyncDisplayKit/Layout/ASLayoutOptions.h; sourceTree = ""; }; 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptions.mm; path = AsyncDisplayKit/Layout/ASLayoutOptions.mm; sourceTree = ""; }; 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptionsPrivate.mm; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm; sourceTree = ""; }; @@ -976,6 +982,8 @@ AC6456051B0A333200CF11B8 /* Layout */ = { isa = PBXGroup; children = ( + 9C5586671BD549CB00B50E3A /* ASAsciiArtBoxCreator.h */, + 9C5586681BD549CB00B50E3A /* ASAsciiArtBoxCreator.m */, ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */, ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */, ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */, @@ -1053,6 +1061,7 @@ 058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */, 058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */, 058D0A74195D05F800B7D73C /* _ASPendingState.h in Headers */, + 9C5586691BD549CB00B50E3A /* ASAsciiArtBoxCreator.h in Headers */, 058D0A76195D05F900B7D73C /* _ASScopeTimer.h in Headers */, 205F0E191B37339C007741D0 /* ASAbstractLayoutController.h in Headers */, 058D0A82195D060300B7D73C /* ASAssert.h in Headers */, @@ -1156,6 +1165,7 @@ B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */, B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */, B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */, + 9C55866C1BD54A3000B50E3A /* ASAsciiArtBoxCreator.h in Headers */, B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */, 509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */, B35062571B010F070018CF92 /* ASAssert.h in Headers */, @@ -1444,6 +1454,7 @@ 058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, 058D0A19195D050800B7D73C /* _ASDisplayView.mm in Sources */, + 9C55866A1BD549CB00B50E3A /* ASAsciiArtBoxCreator.m in Sources */, 058D0A27195D050800B7D73C /* _ASPendingState.m in Sources */, 205F0E1A1B37339C007741D0 /* ASAbstractLayoutController.mm in Sources */, ACF6ED1B1B17843500DA7C62 /* ASBackgroundLayoutSpec.mm in Sources */, @@ -1554,6 +1565,7 @@ B350624A1B010EFD0018CF92 /* _ASCoreAnimationExtras.mm in Sources */, 2767E9421BB19BD600EA9B77 /* ASViewController.m in Sources */, B35062101B010EFD0018CF92 /* _ASDisplayLayer.mm in Sources */, + 9C55866B1BD54A1900B50E3A /* ASAsciiArtBoxCreator.m in Sources */, B35062121B010EFD0018CF92 /* _ASDisplayView.mm in Sources */, B350624C1B010EFD0018CF92 /* _ASPendingState.m in Sources */, 509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */, diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index f7d9e8483e..bb84512f43 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -519,7 +519,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); * Convenience methods for debugging. */ -@interface ASDisplayNode (Debugging) +@interface ASDisplayNode (Debugging) /** * @abstract Return a description of the node hierarchy. @@ -659,9 +659,6 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); - (void)addSubnode:(ASDisplayNode *)node; @end -@interface ASDisplayNode (Debugging) -@end - @interface ASDisplayNode (Deprecated) - (void)reclaimMemory ASDISPLAYNODE_DEPRECATED; From 050160e6bbaf21f4a9a31b2ef8312492dc36dee5 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Mon, 19 Oct 2015 10:04:49 -0700 Subject: [PATCH 081/127] fixed the build again (removed all generic collections) --- AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h | 4 ++-- AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m | 14 +++++++------- AsyncDisplayKit/Layout/ASLayoutSpec.h | 4 ++-- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h b/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h index 3be6ed88de..198e792d1e 100644 --- a/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h +++ b/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h @@ -41,7 +41,7 @@ * | ASTextNode ASTextNode ASTextNode | * ---------------------------------------- */ -+ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent; ++ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent; /** * Renders an ascii art box with the children aligned vertically. @@ -52,7 +52,7 @@ * | ASTextNode | * --------------------- */ -+ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent; ++ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent; @end diff --git a/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m b/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m index 9ec7e213bf..ea9ef9490c 100644 --- a/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m +++ b/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m @@ -53,13 +53,13 @@ typedef NS_ENUM(NSUInteger, PIDebugBoxPaddingLocation) @implementation ASAsciiArtBoxCreator -+ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent ++ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent { if ([children count] == 0) { return parent; } - NSMutableArray *> *childrenLines = [NSMutableArray array]; + NSMutableArray *childrenLines = [NSMutableArray array]; // split the children into lines NSUInteger lineCountPerChild = 0; @@ -83,12 +83,12 @@ typedef NS_ENUM(NSUInteger, PIDebugBoxPaddingLocation) [childrenLines addObject:lines]; } - NSMutableArray *concatenatedLines = [NSMutableArray array]; + NSMutableArray *concatenatedLines = [NSMutableArray array]; NSString *padding = [NSString debugbox_stringWithString:@" " repeatedCount:kDebugBoxPadding]; for (NSUInteger index = 0; index < lineCountPerChild; index++) { NSMutableString *line = [[NSMutableString alloc] init]; [line appendFormat:@"|%@",padding]; - for (NSArray *childLines in childrenLines) { + for (NSArray *childLines in childrenLines) { [line appendFormat:@"%@%@", childLines[index], padding]; } [line appendString:@"|"]; @@ -119,13 +119,13 @@ typedef NS_ENUM(NSUInteger, PIDebugBoxPaddingLocation) } -+ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent ++ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent { if ([children count] == 0) { return parent; } - NSMutableArray *childrenLines = [NSMutableArray array]; + NSMutableArray *childrenLines = [NSMutableArray array]; NSUInteger maxChildLength = 0; for (NSString *child in children) { @@ -167,7 +167,7 @@ typedef NS_ENUM(NSUInteger, PIDebugBoxPaddingLocation) return [childrenLines componentsJoinedByString:@"\n"]; } -+ (NSMutableArray *)appendTopAndBottomToBoxString:(NSMutableArray *)boxStrings parent:(NSString *)parent ++ (NSMutableArray *)appendTopAndBottomToBoxString:(NSMutableArray *)boxStrings parent:(NSString *)parent { NSUInteger totalLineLength = [boxStrings[0] length]; [boxStrings addObject:[NSString debugbox_stringWithString:@"-" repeatedCount:totalLineLength]]; diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index cd177ce891..6426317b91 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -101,7 +101,7 @@ /** * Used by other layout specs to create ascii art debug strings */ -+ (NSString *)asciiArtStringForChildren:(NSArray> *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction; -+ (NSString *)asciiArtStringForChildren:(NSArray> *)children parentName:(NSString *)parentName; ++ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction; ++ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName; @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index f14fdca6cc..35beca3161 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -128,7 +128,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; #pragma mark - ASLayoutableAsciiArtProtocol -+ (NSString *)asciiArtStringForChildren:(NSArray> *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction ++ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction { NSMutableArray *childStrings = [NSMutableArray array]; for (id layoutChild in children) { @@ -143,7 +143,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return [ASAsciiArtBoxCreator verticalBoxStringForChildren:childStrings parent:parentName]; } -+ (NSString *)asciiArtStringForChildren:(NSArray> *)children parentName:(NSString *)parentName ++ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName { return [self asciiArtStringForChildren:children parentName:parentName direction:ASStackLayoutDirectionHorizontal]; } From 938cd468b3ae4b8872a8a8d45d117a656a4a4345 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 19 Oct 2015 18:45:40 +0300 Subject: [PATCH 082/127] Add snapshot tests for ASStaticLayoutSpec. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 4 + .../ASStaticLayoutSpecSnapshotTests.m | 85 ++++++++++++++++++ ...testChildrenMeasuredWithAutoMaxSize@2x.png | Bin 0 -> 2555 bytes ...estSizingBehaviour_overflowChildren@2x.png | Bin 0 -> 834 bytes ...stSizingBehaviour_underflowChildren@2x.png | Bin 0 -> 4194 bytes ...testSizingBehaviour_wrappedChildren@2x.png | Bin 0 -> 2275 bytes 6 files changed, 89 insertions(+) create mode 100644 AsyncDisplayKitTests/ASStaticLayoutSpecSnapshotTests.m create mode 100644 AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testChildrenMeasuredWithAutoMaxSize@2x.png create mode 100644 AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testSizingBehaviour_overflowChildren@2x.png create mode 100644 AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testSizingBehaviour_underflowChildren@2x.png create mode 100644 AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testSizingBehaviour_wrappedChildren@2x.png diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index f096375ce4..ff78556ce4 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -233,6 +233,7 @@ 9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; + AC026B581BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A521A1139C100143C57 /* ASCollectionView.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A501A1139C100143C57 /* ASCollectionView.mm */; }; @@ -590,6 +591,7 @@ 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackBaselinePositionedLayout.mm; sourceTree = ""; }; 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutablePrivate.h; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; + AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASStaticLayoutSpecSnapshotTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; AC3C4A501A1139C100143C57 /* ASCollectionView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionView.mm; sourceTree = ""; }; @@ -819,6 +821,7 @@ ACF6ED591B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm */, ACF6ED5A1B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm */, ACF6ED5B1B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm */, + AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */, ACF6ED571B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.h */, ACF6ED581B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.m */, 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */, @@ -1552,6 +1555,7 @@ 058D0A3A195D057000B7D73C /* ASDisplayNodeTests.m in Sources */, 058D0A3B195D057000B7D73C /* ASDisplayNodeTestsHelper.m in Sources */, 056D21551ABCEF50001107EF /* ASImageNodeSnapshotTests.m in Sources */, + AC026B581BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m in Sources */, ACF6ED5E1B178DC700DA7C62 /* ASInsetLayoutSpecSnapshotTests.mm in Sources */, ACF6ED601B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.m in Sources */, CC7FD9E11BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m in Sources */, diff --git a/AsyncDisplayKitTests/ASStaticLayoutSpecSnapshotTests.m b/AsyncDisplayKitTests/ASStaticLayoutSpecSnapshotTests.m new file mode 100644 index 0000000000..36efb324a3 --- /dev/null +++ b/AsyncDisplayKitTests/ASStaticLayoutSpecSnapshotTests.m @@ -0,0 +1,85 @@ +// +// ASStaticLayoutSpecSnapshotTests.m +// AsyncDisplayKit +// +// Created by Huy Nguyen on 18/10/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASLayoutSpecSnapshotTestsHelper.h" + +#import "ASStaticLayoutSpec.h" +#import "ASBackgroundLayoutSpec.h" + +@interface ASStaticLayoutSpecSnapshotTests : ASLayoutSpecSnapshotTestCase +@end + +@implementation ASStaticLayoutSpecSnapshotTests + +- (void)setUp +{ + [super setUp]; + self.recordMode = NO; +} + +- (void)testSizingBehaviour +{ + [self testWithSizeRange:ASSizeRangeMake(CGSizeMake(150, 200), CGSizeMake(FLT_MAX, FLT_MAX)) + identifier:@"underflowChildren"]; + [self testWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(50, 100)) + identifier:@"overflowChildren"]; + // Expect the spec to wrap its content because children sizes are between constrained size + [self testWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(FLT_MAX / 2, FLT_MAX / 2)) + identifier:@"wrappedChildren"]; +} + +- (void)testChildrenMeasuredWithAutoMaxSize +{ + ASStaticSizeDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor]); + firstChild.layoutPosition = CGPointMake(0, 0); + firstChild.staticSize = CGSizeMake(50, 50); + + ASStaticSizeDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); + secondChild.layoutPosition = CGPointMake(10, 60); + secondChild.staticSize = CGSizeMake(100, 100); + + ASSizeRange sizeRange = ASSizeRangeMake(CGSizeMake(10, 10), CGSizeMake(110, 160)); + [self testWithChildren:@[firstChild, secondChild] sizeRange:sizeRange identifier:nil]; + + XCTAssertTrue(ASSizeRangeEqualToSizeRange(firstChild.constrainedSizeForCalculatedLayout, + ASSizeRangeMake(CGSizeZero, sizeRange.max))); + CGSize secondChildMaxSize = CGSizeMake(sizeRange.max.width - secondChild.layoutPosition.x, + sizeRange.max.height - secondChild.layoutPosition.y); + XCTAssertTrue(ASSizeRangeEqualToSizeRange(secondChild.constrainedSizeForCalculatedLayout, + ASSizeRangeMake(CGSizeZero, secondChildMaxSize))); +} + +- (void)testWithSizeRange:(ASSizeRange)sizeRange identifier:(NSString *)identifier +{ + ASDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor]); + firstChild.layoutPosition = CGPointMake(0, 0); + firstChild.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50)); + + + ASDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); + secondChild.layoutPosition = CGPointMake(0, 50); + secondChild.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(100, 100)); + + [self testWithChildren:@[firstChild, secondChild] sizeRange:sizeRange identifier:identifier]; +} + +- (void)testWithChildren:(NSArray *)children sizeRange:(ASSizeRange)sizeRange identifier:(NSString *)identifier +{ + ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor whiteColor]); + + NSMutableArray *subnodes = [NSMutableArray arrayWithArray:children]; + [subnodes insertObject:backgroundNode atIndex:0]; + + ASStaticLayoutSpec *staticLayoutSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:children]; + ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:staticLayoutSpec + background:backgroundNode]; + + [self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier]; +} + +@end diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testChildrenMeasuredWithAutoMaxSize@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testChildrenMeasuredWithAutoMaxSize@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2b60cf2bcf5af151123b4f90bb5c9d296b0bcc2f GIT binary patch literal 2555 zcmeAS@N?(olHy`uVBq!ia0vp^cNiEL9XQy4tm~aZyg-VvILO_JVcj{Imp~3@fk$L9 z1A~|<2s3&HseAwm%4E9uhX83NAO?X2K&%0!foztxXt51BKsw0N#WAFU@y#7azXuFF zEC>Jmzsy+ofH}*xO!2MmC7sDDe;oU4cmDPLcB_iNIe8Bn-#Xv;XIEDjX3lWuy565x zJAXcHVD4+$!1$nn`?(sI#RClvX$HO>1$m1`g-62#GeI<}pAlNx_?efXypD-WLz-b- zPV}{(Yz@}-jI2|58N#>ezOG?DFn4|f(fC&W9iN2*9Bwicq~6|i4wxW+a%wEdVc4-LH}bhO!@EB$A^~O$F*moZ z!C~DW>mBR=`pW&E|NZL0cB>!xcjMl*$Gv<1{L1ei5$p$Q-?Tbx`mM|;Q?M>!2jhc7 zn_}1By`KH4u95j%E4MjAOvJY*+%IO{4HvL@5VNCz`9N#x?zr9SuRr1FYb4;qRe9^z#X zpZ2!u?Y_9l-??Q9^kTqvs>eH=f3vUY0ORvRtxs@<#hUL0+dSxd-`xY=hZOUZvM>$cT~>x?C)P^?TLrmY5S9@D($-{3*1rbuD^S2TDc!? zXZ0u6FK6CqbHE*iFCg-m?SX~N|NmYS|8z1iuu6eSm$%n8@*Za3VRn4Ke~YnWkibFl zLy`rlD;EB!vVT8+{`1QC^*kjHyxISqk-i}#Bm3hj@Zivf>#H-~^+__aTeNXoGwd+P zS+*;ARq6BwW|_mSvJ5d2->O8<-ZfNyz;L_(Y~0z9a-Xg5gbEsxcWhv8;65C;e$h_r zG=y<8U-hlL7R$s}z$0D;jH(;2Lruz$_rQ%a3%+Z3b(i-6#=eJ#_!#tfs`qw1dpAQ6 zZd}?b>t#{poN(8!55IUv))--&PwDZH*LrZ*zWJ&(Yj?66+_>2*-_6Lns|t55*z;#N p;Kq%5el+Gsll_p*^RKNwFzzWo{QrZ6KB$?;;OXk;vd$@?2>?ccAQS)q literal 0 HcmV?d00001 diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testSizingBehaviour_overflowChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testSizingBehaviour_overflowChildren@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1687dc24f6128e3a5b53bfbec8223abb04f1dac3 GIT binary patch literal 834 zcmeAS@N?(olHy`uVBq!ia0vp^DL{OJgAGW^*|;18QjEnx?oJHr&dIz4aySb-B8wRq z#8g3;(KATp15i*V)5SjoNHYO32!M>xfYLxVz$T=`f zG(OoC$-tqoX^T$8?8S1M*##V)_-q<(Mr~-Q0fzVg|6e)g0V9Ml3>46BuP$UfV8C<4 z;r;%SO$U=3nO)ODm+asDy=G}WgU1_3CZ-OTbuLCNz@YUw!vqYdrKtyZNq(JS3k=cp zNurw>1RPQ_BRAx^*{1OWLv&`!Q5^;rfygMaHM0ZzZ!&Wz6sbiTas$JB+BDYJX;+fJ k0V8LQXV|bcD)?KC6w{h(H;cR<0)v{t)78&qol`;+02Tr$*Z=?k literal 0 HcmV?d00001 diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testSizingBehaviour_underflowChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testSizingBehaviour_underflowChildren@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..22338139793cdf73ca4428415a2d8a4445d6f5c3 GIT binary patch literal 4194 zcmeAS@N?(olHy`uVBq!ia0y~yVAKKP2^?%d5s}i^Y9Pf}9OUlAuKb;uuoF_~x2ppNgS? z)5Sgimu8!=XG{xIi1Xb0$~B4Se$D>(zkgakIe5OGS?0&-M*cN?2Vebv{O~v<-=CKZ z39->L>f@%?7c?+8utr23Xp~}S;NFmR$V&r6Z)spQ!gwg`C4kiZz`8^QS$NKX?h zSa6K-0MnXT8-z{)X&v1M4!t0YO%qk8j5=U61O`tC?8~lQ`uzLjgR=Ztvuu<^Hte&0 z%CM@s-0)E2tYjv!gvb&_W>a4|L9PhJHDc%Y9{v2w&&?vB*aw)VxwTUkEIX;}#Hii! z^=tOK>hEHAIoPxMI3pTT6I*$%m{huGB(!dbviUA{{)U^sn6sfbZejmFey@*B-lVXZE|NnbWUx;B~V0Q(zOx|8I zf|#{YaV@uH23SNwN9$~t{L^FBSMmejy5y=n^A~zzju)2;=2KB8))I?4z1T18j&Sk_v{+C9kX({uXcr)~pPk Lu6{1-oD!M<{9X%e literal 0 HcmV?d00001 diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testSizingBehaviour_wrappedChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStaticLayoutSpecSnapshotTests/testSizingBehaviour_wrappedChildren@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3d76853c01eb6ddcceedec3977ad267556d744c0 GIT binary patch literal 2275 zcmeAS@N?(olHy`uVBq!ia0vp^Cm0wQbvW37thQf&_5mrz;vjb?hIQv;UIICs1s;*b z3=CqbAk63)r1AkMD3j^p9|EMAfEWa(0kH;@2C`W=_RRXWi-Cdpil>WXNCo4YtA05pc*+Fk3K3BmM2H%JYB4d13tHNB8Z&kv*50se|_j z+Z5raYhsPx@BF`r5yszf4&UOfW*&%ouztZp5uv*Gwey=9*{3lxeRM2PkYP}NuqOWK-?>KxI~*(o z_!@)@bdJtl8j-In(1;|PIx0IF8l!0goKr^g0-=&2!RiC^uB5w+SDL#%1KS=9p00i_ I>zopr0G7EZ1poj5 literal 0 HcmV?d00001 From 0972c56f7367e98369383a2c10be4f9944cc20d9 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 20 Oct 2015 16:37:39 -0700 Subject: [PATCH 083/127] Call UIViewController's designated initializer in ASViewController ASViewController currently cannot be subclassed in Swift, as the original call to `[super init]` directs to calling the designated initializer of UIViewController `[self initWithNibName:bundle:]`. Because of Swift's initializer inheritance behavior, this designated initializer will not implicitly be available on the subclassed ASViewController without some extra boilerplate. Adding an explicit call to the designated initializer fixes this issue. --- AsyncDisplayKit/ASViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASViewController.m b/AsyncDisplayKit/ASViewController.m index ef89b905a6..15fe664323 100644 --- a/AsyncDisplayKit/ASViewController.m +++ b/AsyncDisplayKit/ASViewController.m @@ -20,7 +20,7 @@ - (instancetype)initWithNode:(ASDisplayNode *)node { - if (!(self = [super init])) { + if (!(self = [super initWithNibName:nil bundle:nil])) { return nil; } From 8f289d99771429d09573e9342d10a84123c93276 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 19 Oct 2015 22:37:23 -0700 Subject: [PATCH 084/127] Back internal kind collection with a set --- AsyncDisplayKit/ASCollectionView.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index b312b86c10..e124ecd203 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -153,7 +153,7 @@ static BOOL _isInterceptedSelector(SEL sel) CGSize _maxSizeForNodesConstrainedSize; BOOL _ignoreMaxSizeChange; - NSMutableArray *_registeredSupplementaryKinds; + NSMutableSet *_registeredSupplementaryKinds; /** * If YES, the `UICollectionView` will reload its data on next layout pass so we should not forward any updates to it. @@ -232,7 +232,7 @@ static BOOL _isInterceptedSelector(SEL sel) _layoutDelegate = [self flowLayoutInspector]; } - _registeredSupplementaryKinds = [NSMutableArray array]; + _registeredSupplementaryKinds = [NSMutableSet set]; self.backgroundColor = [UIColor whiteColor]; @@ -745,7 +745,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController { - return _registeredSupplementaryKinds; + return [_registeredSupplementaryKinds allObjects]; } - (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath From 11076b115e2c1fb49a599d44c92da773d557ec17 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 19 Oct 2015 22:45:30 -0700 Subject: [PATCH 085/127] Used cached layout value in node remeasurement --- AsyncDisplayKit/Details/ASDataController.mm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 400a1161fd..c8191b3a05 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -15,6 +15,7 @@ #import "ASDisplayNode.h" #import "ASMultidimensionalArrayUtils.h" #import "ASDisplayNodeInternal.h" +#import "ASLayout.h" //#define LOG(...) NSLog(__VA_ARGS__) #define LOG(...) @@ -818,8 +819,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + ASLayout *layout = [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, layout.size.width, layout.size.height); }]; }]; }]; From 9697edac33377302fa35267de777cced29a58f83 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 19 Oct 2015 22:54:48 -0700 Subject: [PATCH 086/127] Fix conditional block style --- AsyncDisplayKit/Details/ASDataController.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index c8191b3a05..c467716fbe 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -186,8 +186,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER); free(nodeBoundSizes); - if (completionBlock) + if (completionBlock) { completionBlock(nodes, indexPaths); + } } - (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath From 5ca14486b6de91d9008a545033b6e52cc0ffab94 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 21 Oct 2015 21:41:30 -0700 Subject: [PATCH 087/127] Make array instantiation consistent --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 45f1d3af37..1e3f7029d4 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -61,7 +61,7 @@ NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryNodeOfKind:kind]; NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount]; for (int i = 0; i < sectionCount; i++) { - [sections addObject:[[NSMutableArray alloc] init]]; + [sections addObject:[NSMutableArray array]]; } [self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] completion:nil]; From 413307a9738cf55646f3a4c693e76d356d09198f Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 21 Oct 2015 21:43:55 -0700 Subject: [PATCH 088/127] Assert flow layout inspector always initializes with a flow layout --- AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index e68020490d..b09f030c3c 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -11,6 +11,7 @@ #import "ASCollectionViewFlowLayoutInspector.h" #import "ASCollectionView.h" +#import "ASAssert.h" @implementation ASCollectionViewFlowLayoutInspector { BOOL _delegateImplementsReferenceSizeForHeader; @@ -24,7 +25,7 @@ self = [super init]; if (flowLayout == nil) { - return nil; + ASDisplayNodeAssert(NO, @"Should never create a layout inspector without a layout"); } if (self != nil) { From ad82c72eb47f2c2617c5af51fd45a4b55858343b Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 21 Oct 2015 21:49:38 -0700 Subject: [PATCH 089/127] Cache flow layout delegate in local variable --- .../Details/ASCollectionViewFlowLayoutInspector.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index b09f030c3c..0ef9f90370 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -41,8 +41,9 @@ _delegateImplementsReferenceSizeForHeader = nil; _delegateImplementsReferenceSizeForFooter = nil; } else { - _delegateImplementsReferenceSizeForHeader = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; - _delegateImplementsReferenceSizeForFooter = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; + id delegate = [self delegateForCollectionView:collectionView]; + _delegateImplementsReferenceSizeForHeader = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; + _delegateImplementsReferenceSizeForFooter = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; } } From 56457249840498409824d329526da05350f8779e Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 21 Oct 2015 22:16:42 -0700 Subject: [PATCH 090/127] Use pointer comparison helpers for string equality --- .../Details/ASCollectionViewFlowLayoutInspector.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index 0ef9f90370..beb654d0c0 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -12,6 +12,7 @@ #import "ASCollectionView.h" #import "ASAssert.h" +#import "ASEqualityHelpers.h" @implementation ASCollectionViewFlowLayoutInspector { BOOL _delegateImplementsReferenceSizeForHeader; @@ -79,13 +80,13 @@ - (CGSize)sizeForSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section collectionView:(ASCollectionView *)collectionView { - if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { + if (ASObjectIsEqual(kind, UICollectionElementKindSectionHeader)) { if (_delegateImplementsReferenceSizeForHeader) { return [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForHeaderInSection:section]; } else { return [self.layout headerReferenceSize]; } - } else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { + } else if (ASObjectIsEqual(kind, UICollectionElementKindSectionFooter)) { if (_delegateImplementsReferenceSizeForFooter) { return [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForFooterInSection:section]; } else { From 5736e7c2d24f490bcc4fe6b631a7e1ac31c5357d Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 21 Oct 2015 22:29:10 -0700 Subject: [PATCH 091/127] Clarify editing transaction documentation --- AsyncDisplayKit/Details/ASDataController+Subclasses.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 3e33449389..23c28e9aa4 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -74,8 +74,8 @@ /** * Notifies the subclass to perform any work needed before the data controller is reloaded entirely * - * @discussion This method will be performed before the data controller enters its editing queue, usually on the main - * thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or + * @discussion This method will be performed before the data controller enters its editing queue. + * The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or * data stores before entering into editing the backing store on a background thread. */ - (void)prepareForReloadData; @@ -92,8 +92,8 @@ /** * Notifies the subclass to perform setup before sections are inserted in the data controller * - * @discussion This method will be performed before the data controller enters its editing queue, usually on the main - * thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or + * @discussion This method will be performed before the data controller enters its editing queue. + * The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or * data stores before entering into editing the backing store on a background thread. * * @param sections Indices of sections to be inserted From 45ff74b6048c26be66b22cadc4a7bb7a04f89162 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 21 Oct 2015 22:38:41 -0700 Subject: [PATCH 092/127] Remove array fallback on editing node index paths --- AsyncDisplayKit/Details/ASDataController.mm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index c467716fbe..009d143273 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -220,8 +220,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { - if (indexPaths.count == 0) + if (indexPaths.count == 0) { return; + } + LOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForMultidimensionalArray(_editingNodes[kind])); NSMutableArray *editingNodes = _editingNodes[kind]; ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths); @@ -851,7 +853,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (NSArray *)indexPathsForEditingNodesOfKind:(NSString *)kind { - return _editingNodes[kind] != nil ? ASIndexPathsForMultidimensionalArray(_editingNodes[kind]) : [NSArray array]; + return _editingNodes[kind] != nil ? ASIndexPathsForMultidimensionalArray(_editingNodes[kind]) : nil; } - (NSMutableArray *)editingNodesOfKind:(NSString *)kind From db0a0326d70741b316a0cf06e3fe177e2d159e17 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 21 Oct 2015 22:59:10 -0700 Subject: [PATCH 093/127] Rename layoutDelegate to layoutInspector in ASCollectionView --- AsyncDisplayKit/ASCollectionView.h | 2 +- AsyncDisplayKit/ASCollectionView.mm | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 14ec90f894..2b8f902933 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -90,7 +90,7 @@ * collection view layout subclasses will need to provide their own implementation of an inspector object for their * supplementary views to be compatible with `ASCollectionView`'s supplementary node support. */ -@property (nonatomic, weak) id layoutDelegate; +@property (nonatomic, weak) id layoutInspector; /** * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index e124ecd203..31191d42d3 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -229,7 +229,7 @@ static BOOL _isInterceptedSelector(SEL sel) // Register the default layout inspector delegate for flow layouts only, custom layouts // will need to roll their own ASCollectionViewLayoutInspecting implementation and set a layout delegate if ([layout asdk_isFlowLayout]) { - _layoutDelegate = [self flowLayoutInspector]; + _layoutInspector = [self flowLayoutInspector]; } _registeredSupplementaryKinds = [NSMutableSet set]; @@ -341,7 +341,7 @@ static BOOL _isInterceptedSelector(SEL sel) [super setCollectionViewLayout:collectionViewLayout]; if ([collectionViewLayout asdk_isFlowLayout]) { _flowLayoutInspector = nil; - _layoutDelegate = [self flowLayoutInspector]; + _layoutInspector = [self flowLayoutInspector]; } } @@ -750,20 +750,20 @@ static BOOL _isInterceptedSelector(SEL sel) - (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); - return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + ASDisplayNodeAssert(_layoutInspector != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); + return [_layoutInspector collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; } - (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section { - ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); - return [_layoutDelegate collectionView:self supplementaryNodesOfKind:kind inSection:section]; + ASDisplayNodeAssert(_layoutInspector != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); + return [_layoutInspector collectionView:self supplementaryNodesOfKind:kind inSection:section]; } - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind; { - ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); - return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryNodeOfKind:kind]; + ASDisplayNodeAssert(_layoutInspector != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); + return [_layoutInspector collectionView:self numberOfSectionsForSupplementaryNodeOfKind:kind]; } #pragma mark - ASRangeControllerDelegate. From 084d60883bcdbda434ab9f3db8637de956eb97da Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 21 Oct 2015 23:04:21 -0700 Subject: [PATCH 094/127] Add assertion to flow layout inspector down casting --- AsyncDisplayKit/ASCollectionView.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 31191d42d3..08f4b43502 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -256,6 +256,7 @@ static BOOL _isInterceptedSelector(SEL sel) { if (_flowLayoutInspector == nil) { UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout; + ASDisplayNodeAssertNotNil(layout, @"Collection view layout must be a flow layout to use the built-in inspector"); _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:self flowLayout:layout]; } From e8ce7fcbb2ab6cc2c0b4825cd756dd7ee2a43103 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 21 Oct 2015 23:14:54 -0700 Subject: [PATCH 095/127] Clean up caching of collection view delegate selectors on the inspector --- AsyncDisplayKit/ASCollectionView.mm | 2 +- .../Details/ASCollectionViewFlowLayoutInspector.h | 10 ++++++++-- .../Details/ASCollectionViewFlowLayoutInspector.m | 7 +++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 08f4b43502..01b4927a7e 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -334,7 +334,7 @@ static BOOL _isInterceptedSelector(SEL sel) _asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); } - [_flowLayoutInspector cacheSelectorsForCollectionView:self]; + [_layoutInspector didChangeCollectionViewDelegate:asyncDelegate]; } - (void)setCollectionViewLayout:(UICollectionViewLayout *)collectionViewLayout diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 5aab03e012..179b880827 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -11,6 +11,7 @@ #import @class ASCollectionView; +@protocol ASCollectionViewDelegate; @protocol ASCollectionViewLayoutInspecting @@ -29,6 +30,13 @@ */ - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section; +/** + * Allow the inspector to respond to delegate changes. + * + * @discussion A great time to update perform selector caches! + */ +- (void)didChangeCollectionViewDelegate:(id)delegate; + @end @interface ASCollectionViewFlowLayoutInspector : NSObject @@ -37,6 +45,4 @@ - (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout; -- (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView; - @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index beb654d0c0..e90cd461d6 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -30,19 +30,18 @@ } if (self != nil) { - [self cacheSelectorsForCollectionView:collectionView]; + [self didChangeCollectionViewDelegate:collectionView.asyncDelegate]; _layout = flowLayout; } return self; } -- (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView +- (void)didChangeCollectionViewDelegate:(id)delegate; { - if (collectionView == nil) { + if (delegate == nil) { _delegateImplementsReferenceSizeForHeader = nil; _delegateImplementsReferenceSizeForFooter = nil; } else { - id delegate = [self delegateForCollectionView:collectionView]; _delegateImplementsReferenceSizeForHeader = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; _delegateImplementsReferenceSizeForFooter = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; } From e9708633bcb3f3ffe30c2e6a3611f7e7f8838d6f Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 21 Oct 2015 23:16:31 -0700 Subject: [PATCH 096/127] Remove stubbed collection view layout setter behavior --- AsyncDisplayKit/ASCollectionView.mm | 9 --------- 1 file changed, 9 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 01b4927a7e..bf761b04d7 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -337,15 +337,6 @@ static BOOL _isInterceptedSelector(SEL sel) [_layoutInspector didChangeCollectionViewDelegate:asyncDelegate]; } -- (void)setCollectionViewLayout:(UICollectionViewLayout *)collectionViewLayout -{ - [super setCollectionViewLayout:collectionViewLayout]; - if ([collectionViewLayout asdk_isFlowLayout]) { - _flowLayoutInspector = nil; - _layoutInspector = [self flowLayoutInspector]; - } -} - - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType { [_layoutController setTuningParameters:tuningParameters forRangeType:rangeType]; From d9ea1b52ea958a37303202cb8b9e1ecf664eb1b5 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Thu, 22 Oct 2015 15:27:48 -0700 Subject: [PATCH 097/127] Fix unit tests --- AsyncDisplayKitTests/ASCollectionViewTests.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKitTests/ASCollectionViewTests.m b/AsyncDisplayKitTests/ASCollectionViewTests.m index db993713b5..59a081ec21 100644 --- a/AsyncDisplayKitTests/ASCollectionViewTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewTests.m @@ -91,15 +91,15 @@ { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; - XCTAssert(collectionView.layoutDelegate != nil, @"should automatically set a layout delegate for flow layouts"); - XCTAssert([collectionView.layoutDelegate isKindOfClass:[ASCollectionViewFlowLayoutInspector class]], @"should have a flow layout inspector by default"); + XCTAssert(collectionView.layoutInspector != nil, @"should automatically set a layout delegate for flow layouts"); + XCTAssert([collectionView.layoutInspector isKindOfClass:[ASCollectionViewFlowLayoutInspector class]], @"should have a flow layout inspector by default"); } - (void)testThatItDoesNotSetALayoutInspectorForCustomLayouts { UICollectionViewLayout *layout = [[UICollectionViewLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; - XCTAssert(collectionView.layoutDelegate == nil, @"should not set a layout delegate for custom layouts"); + XCTAssert(collectionView.layoutInspector == nil, @"should not set a layout delegate for custom layouts"); } - (void)testThatRegisteringASupplementaryNodeStoresItForIntrospection From 6569893c239efd4b200adb6f5e27c8ec5a2c6a54 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Thu, 22 Oct 2015 18:54:07 -0700 Subject: [PATCH 098/127] Reset inspector selector implementation cache correctly --- AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index e90cd461d6..5ce3b9bfb6 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -39,8 +39,8 @@ - (void)didChangeCollectionViewDelegate:(id)delegate; { if (delegate == nil) { - _delegateImplementsReferenceSizeForHeader = nil; - _delegateImplementsReferenceSizeForFooter = nil; + _delegateImplementsReferenceSizeForHeader = NO; + _delegateImplementsReferenceSizeForFooter = NO; } else { _delegateImplementsReferenceSizeForHeader = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; _delegateImplementsReferenceSizeForFooter = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; From c33d6efa8a3be731fa4ffd1a196c976d631cf155 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Thu, 22 Oct 2015 08:53:24 -0700 Subject: [PATCH 099/127] WIP synchronous reload data on collection view --- AsyncDisplayKit/ASCollectionView.h | 2 + AsyncDisplayKit/ASCollectionView.mm | 6 + AsyncDisplayKit/Details/ASDataController.h | 2 + AsyncDisplayKit/Details/ASDataController.mm | 52 ++- .../SynchronousKittens/Default-568h@2x.png | Bin 0 -> 17520 bytes .../SynchronousKittens/Default-667h@2x.png | Bin 0 -> 18314 bytes .../SynchronousKittens/Default-736h@3x.png | Bin 0 -> 23380 bytes examples/SynchronousKittens/Podfile | 3 + .../Sample.xcodeproj/project.pbxproj | 361 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Sample.xcscheme | 88 +++++ .../contents.xcworkspacedata | 1 + .../SynchronousKittens/Sample/AppDelegate.h | 20 + .../SynchronousKittens/Sample/AppDelegate.m | 27 ++ .../SynchronousKittens/Sample/BlurbNode.h | 19 + .../SynchronousKittens/Sample/BlurbNode.m | 122 ++++++ examples/SynchronousKittens/Sample/Info.plist | 36 ++ .../SynchronousKittens/Sample/KittenNode.h | 24 ++ .../SynchronousKittens/Sample/KittenNode.mm | 197 ++++++++++ .../Sample/ViewController.h | 16 + .../Sample/ViewController.m | 214 +++++++++++ examples/SynchronousKittens/Sample/main.m | 20 + 22 files changed, 1216 insertions(+), 1 deletion(-) create mode 100644 examples/SynchronousKittens/Default-568h@2x.png create mode 100644 examples/SynchronousKittens/Default-667h@2x.png create mode 100644 examples/SynchronousKittens/Default-736h@3x.png create mode 100644 examples/SynchronousKittens/Podfile create mode 100644 examples/SynchronousKittens/Sample.xcodeproj/project.pbxproj create mode 100644 examples/SynchronousKittens/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 examples/SynchronousKittens/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme create mode 100644 examples/SynchronousKittens/Sample.xcworkspace/contents.xcworkspacedata create mode 100644 examples/SynchronousKittens/Sample/AppDelegate.h create mode 100644 examples/SynchronousKittens/Sample/AppDelegate.m create mode 100644 examples/SynchronousKittens/Sample/BlurbNode.h create mode 100644 examples/SynchronousKittens/Sample/BlurbNode.m create mode 100644 examples/SynchronousKittens/Sample/Info.plist create mode 100644 examples/SynchronousKittens/Sample/KittenNode.h create mode 100644 examples/SynchronousKittens/Sample/KittenNode.mm create mode 100644 examples/SynchronousKittens/Sample/ViewController.h create mode 100644 examples/SynchronousKittens/Sample/ViewController.m create mode 100644 examples/SynchronousKittens/Sample/main.m diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 14ec90f894..d3d08048e6 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -131,6 +131,8 @@ */ - (void)reloadData; +- (void)reloadDataAndWait; + /** * Registers the given kind of supplementary node for use in creating node-backed supplementary views. * diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index b312b86c10..e1535f4c2a 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -280,6 +280,12 @@ static BOOL _isInterceptedSelector(SEL sel) [self reloadDataWithCompletion:nil]; } +- (void)reloadDataAndWait +{ + [_dataController reloadDataAndWait]; + [super reloadData]; +} + - (void)setDataSource:(id)dataSource { // UIKit can internally generate a call to this method upon changing the asyncDataSource; only assert for non-nil. diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index e605aa86d6..0112a298ec 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -168,6 +168,8 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion; +- (void)reloadDataAndWait; + /** @name Data Querying */ - (NSUInteger)numberOfSections; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 400a1161fd..6314411dd2 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -404,6 +404,51 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +- (void)reloadDataAndWait +{ + [self performEditCommandWithBlock:^{ + ASDisplayNodeAssertMainThread(); + [_editingTransactionQueue waitUntilAllOperationsAreFinished]; + + [self accessDataSourceSynchronously:YES withBlock:^{ + NSUInteger sectionCount = [_dataSource numberOfSectionsInDataController:self]; + NSMutableArray *updatedNodes = [NSMutableArray array]; + NSMutableArray *updatedIndexPaths = [NSMutableArray array]; + [self _populateFromEntireDataSourceWithMutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; + + // Measure nodes whose views are loaded before we leave the main thread + [self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths]; + + // Allow subclasses to perform setup before going into the edit transaction + [self prepareForReloadData]; + + LOG(@"Edit Transaction - reloadData"); + + ASDataControllerAnimationOptions animationOptions = UITableViewRowAnimationNone; + + // Remove everything that existed before the reload, now that we're ready to insert replacements + NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind]); + [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; + + NSMutableArray *editingNodes = _editingNodes[ASDataControllerRowNodeKind]; + NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)]; + [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; + + [self willReloadData]; + + // Insert each section + NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount]; + for (int i = 0; i < sectionCount; i++) { + [sections addObject:[[NSMutableArray alloc] init]]; + } + + [self _insertSections:sections atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withAnimationOptions:animationOptions]; + + [self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions]; + }]; + }]; +} + #pragma mark - Data Source Access (Calling _dataSource) /** @@ -413,7 +458,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)accessDataSourceWithBlock:(dispatch_block_t)block { - if (_asyncDataFetchingEnabled) { + [self accessDataSourceSynchronously:NO withBlock:block]; +} + +- (void)accessDataSourceSynchronously:(BOOL)synchronously withBlock:(dispatch_block_t)block +{ + if (!synchronously && _asyncDataFetchingEnabled) { [_dataSource dataControllerLockDataSource]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ block(); diff --git a/examples/SynchronousKittens/Default-568h@2x.png b/examples/SynchronousKittens/Default-568h@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ee80b93937cd9dd79502629b14fe09aa03cacc2 GIT binary patch literal 17520 zcmeI3&2QsG7{;dyhuxq(aR70$vO)rco)4~Hqb-mA2=CIb8^QK*gwP8wCVy+_i!WbB=&euO z!=w19^{!?6gA#W9HYtq<0qu=Ybz>Z0`-H?on{-{TR{Zmu?}~!!)QWeFmfQ*&q~~s* zhveXV_s~8+u`5n-qh6?vEov|zF&4&yz86{JS~2yt=yB346@|1*d{QfJCIbpbtv#XP zheR++hG@%*E|`^)Vkml9c~ekjMU!MrQZ!LfExBSThA{aQ>jipL4V{j)-@H8;jz+a& zFOCCCl18IZX{43>uq!E*N=1@YNmWJKLyXS67>`9Sx|NwseVQb)LpO+B-xCsF-1diY ztyoM3ntdkMH3(({dC`O&r6`SYASoqTS|)PrnI;&9{q)ovTOxfjAYL3%ow8IH^!(V5 zdj5(bXX%v#(>ZCiW@9fs-@#z%&{4c~N)b$uE>%W{X91D+N#qYhn{1uZOS!e|>SMPv zpPUO$NoM7_ld-!(mSi$nx)ib*s?uw<8X>{4A0GOCzn-nKy(vPW(GXs1VcYc*q_0;c z*nd9Rb1TxsF{#tVsEdj$D*B-+TUy1EO;I*2Sj^#R z=5cV0FXfW&oAYsOtK)|Q9M|0e?h+~Rx>af3nCm%PQdYz7`yo9oQrD`|vgVvBU1rvf z7sc4K$xgFQ8%nP0Sc)olh@)wuzTR$&x`VM;Hf>YXY_n{bs#dn!Y6`K{%F7q5o4!3v zw#vlXq1KM0n~q5oQE_zYZ)g>1Jsbid6i*sMS$nsnP**iK4W z-A;A`ajMdV*7<48loOe|IDwa=ocZVEtH&7ih{xJcnN`|rwMpc6;t>wXW|yvs$8Pk@ z@}dTMSEZ!x_uck_9fO)qQO@GMQ+b+k+QN;k1G;mdod%W(l9?2zMP^8s0o3jkq<92c7p$Z}i&2s`As z*nB{i;{rg~A;-n$1F{?!0KyJAE;b*K<+uP4cF1wD`G73P1%R+aj*HC)WH~MXgdK8R zY(5~%aRDIgkmF+W0a=a<0AYt57n={ra$EoiJ7nT2%-^yl9(}cTMBkzP^v2h3(D!cz zdwaiy(D|zfJ@^QrzyG1%zali05&G>uLe}R9z2tv(@5kE+U4MV4xp_GL`Sg5w7{{k8fuN`-gg~6EtdKy$@q3ealPsm_(n_S1wutt$JFzFN)xMF-1^Yl zKS&PRZ`w}KFH<+@u=1!M^4^5hZ;wLioUladup`fJl>Yeo+mhtDjncbTTWyEy?AY5p zkJ#S%_P%p|;?&&I?dEcQWOIW)OQbw>80kV~ynhxlWtYXlAadBoDZiAPi>^NL zy0gi-;FM-AJ$E+pE|H~~T$U|`e1_`$TJ80S(IklWgP_;USJ}=4p|rj(z1*gb=chz*y}FfH5CiynoZ z(1ULtmnQT|F2%kDAJ?(FLDZ*7)9ceCriA`cU70l&dQO*=y&m*}h@Tc~8g*q+b3v6Y zGkeRA6Y4u`tJUNUWzTbMv)ZYeIx}Tvs=93I-HKc_OiS*t8m(1Toz=z=+wG!!&bk#i zgLJEmtzB+S4XSl5!;n{9oyw*`b-6>UrmUK^il*vDw??bk{BY}ne9ro<$m3;>_6mK{ zv;U_`>IE_XGxBbybA%AHk*$y&Essi=Cl+F`4c zIsUhEU>dfjO$yTgGzYWw>l{=6h`CK=a#@px$7$NGR{I`p>s+{xJnqw$@4<_ua8kkN zOJ_ZOc(8fd2=@U+V2j1fk5@J z8HQPu7E)trK3jz+=d5(*t^B#1|0GbRzX|55>h#WYod>gPx=vT%g@XVf;t+9(`G73q z0zkwe;u7-#S;Pf^h(p9B<^!^b3jh&^h)c`|WDyqtA`TIkm=DMzE&xOvA}%o>kVRYo zh&V)CVm=^?xBw7wh`7XjKo)TUAmR{liTQvm;sQX#A>tDA0a?TafQUoHCFTRNhzkG_ zhloqe2V@Z!03r?%mzWR8A}#<#93n0;ACN^{0Ejq5Tw*>Ti?{#~afrCYd_Wd)0U+WK zaf$hWEaCz{#3AAm^8s1J1%QY{#3kkfvWN=+5r;xt%d@v^na^LX9rAZ*rRRS9n7@B3 zIh(s}Le5_zCFIw8gxH@D@_g{o-5>7o_jw0ft+oBp&%b@Yw8WM7 zrH5boo3EvZ_(1|l00|%gB!C2v01`j~NZ=X>eD`35{4^j-PY%DimD+7>Y`4C6{oeh* E06EB-U;qFB literal 0 HcmV?d00001 diff --git a/examples/SynchronousKittens/Default-736h@3x.png b/examples/SynchronousKittens/Default-736h@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8949cae16a4217842bd8ad813422c6b02e7cafa GIT binary patch literal 23380 zcmeI3&2QsG7{;dvp`~agBn}`rU}c2_5{^Co*u*CBswBI#5^1Zpi0)~3Y)@Ki6WiGC zChY|TCvFHXE5u>%NL;wV0fEGo^J@PC5E5KCaDuU&4|kFdg zdhMfNZ$I1by=i;VuulBQrS}<*{ybMEgw+Y z?`=z+D4~*BH)T)7hSad?*u+K?zba`e))iG(ur6cGRxKNw(&STfR@qT2@%#2p_u6DQ z7PV`KSr*%hG8&EQBfTCa2MV?4Y7lsEkRh;JT_T6Zzgu6CWjm;?#Ukp#wUkVU{u-UaE@^ zqby1fqcet_rOzCg%}K8}8++;b4u?yJPP41G8G;GYrOI^gIHt-DO{1g4qgQXUOS!b{ z>a(CfpPW-pdFIS>r{mxZS)M6n#Zo9|sKu_;?j)3CQL-0B1E*YN+f#&6rz5@GBVG{Z zNMC6weE<1m&#h>eWYl4c(U7q!V`EQKZH#RestsFJD<)-6&Z8IkLH~G(hhf@Aqv}!V z$$PNP%ca`4;^TXEKT3uqbAll`ph_Gbw3K;crRQu(*_~(*CG51Qqqmf0%@tL# z%OtV!#5ekuMW}3fo+cYjqbZZ7=E~GCvFmv%we)@gvDd507p%LH zca(3HiM7wHU9)NG4c*OMb=lB-OLlervW%Ms#tqVRUEP{mSL6%UTS>sm92r#lFw}aGvc-8^S+s2F7KLn=zH_>DnivE{L5fL|(tNwMYt#KUt6;MNm1~M^YZEUo zWsaBc2I{wzQ?2vUnkgr;U~vM^N4fN`$j=^QbVx(dhAOR!UT2%6Q9m1zgsvU1HSw1l zy|g^7;k{c*UiSyVzc33ax&2^sKn+c6P*;_G^K!n@JzsX48kQ}r_EkzOqv5;LIsT_} zU}&~ED@gy*9L(3RcSynm>O0ExvZf7>(zKng_C46vIdva-)Tgc7gQrX3w1O{|&Q|{L zV6(EzN&qR!9d0QLZSw_F_TSIT=isR5-_TU{QE>i$BCV!*>25)XkQ{H}i_^U`z-5-GJRH)BFa2HG_>+sQA=U>Gio()6`~F zT1ic$<#bgZor~I8wz3Cv_M1SN{U}%{tFv3r!#tQ@)5CP-ykHOxh&TjXVm@3JaB)Dy zA>b18;j(~>10oIqmzWQi1za2uaR|7?e7G#&;(&-lz$NCxWdRolL>vMxF&{1qxHur< z5O9h4a9O~`0TG9QOU#GM0xk}SI0Rf`K3o=XaX`c&;1cuUvVe;NA`StUm=Bi)TpSQ_ z2)M+2xGdn}fQUoDCFa9r0T%~E90D#eA1({HI3VH>aEbYFS-`~s5r=?F%!kVYE)Iw| z1YBZ1To!O~K*S;767%7*fQthn4gr^#50?d891w9R#I-tq&6bAj-P#d*iT2)B=M(k< zuH>!n^bk6E38D8sKf00e*l5C8%|00;m9AOHk_01yBIKmZ5;0U!VbfB+Bx0zd!= l00AHX1c1Q*gn;t`y7MJkdHVCOto({Mu5Na}c>U)4e*&NtopJyG literal 0 HcmV?d00001 diff --git a/examples/SynchronousKittens/Podfile b/examples/SynchronousKittens/Podfile new file mode 100644 index 0000000000..6c012e3c04 --- /dev/null +++ b/examples/SynchronousKittens/Podfile @@ -0,0 +1,3 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +pod 'AsyncDisplayKit', :path => '../..' diff --git a/examples/SynchronousKittens/Sample.xcodeproj/project.pbxproj b/examples/SynchronousKittens/Sample.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..ddfd884074 --- /dev/null +++ b/examples/SynchronousKittens/Sample.xcodeproj/project.pbxproj @@ -0,0 +1,361 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 05561CFA19D4E77700CBA93C /* BlurbNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 05561CF919D4E77700CBA93C /* BlurbNode.m */; }; + 05561CFD19D4F94A00CBA93C /* KittenNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 05561CFC19D4F94A00CBA93C /* KittenNode.mm */; }; + 0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */; }; + 05E2128719D4DB510098F589 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128619D4DB510098F589 /* main.m */; }; + 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128919D4DB510098F589 /* AppDelegate.m */; }; + 05E2128D19D4DB510098F589 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128C19D4DB510098F589 /* ViewController.m */; }; + 3EC0CDCBA10D483D9F386E5E /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */; }; + 6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AA19EE274300767484 /* Default-667h@2x.png */; }; + 6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AB19EE274300767484 /* Default-736h@3x.png */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 05561CF819D4E77700CBA93C /* BlurbNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlurbNode.h; sourceTree = ""; }; + 05561CF919D4E77700CBA93C /* BlurbNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlurbNode.m; sourceTree = ""; }; + 05561CFB19D4F94A00CBA93C /* KittenNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KittenNode.h; sourceTree = ""; }; + 05561CFC19D4F94A00CBA93C /* KittenNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KittenNode.mm; sourceTree = ""; }; + 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; + 05E2128119D4DB510098F589 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 05E2128519D4DB510098F589 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 05E2128619D4DB510098F589 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 05E2128819D4DB510098F589 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 05E2128919D4DB510098F589 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 05E2128B19D4DB510098F589 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 05E2128C19D4DB510098F589 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 6C2C82AA19EE274300767484 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = SOURCE_ROOT; }; + 6C2C82AB19EE274300767484 /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = SOURCE_ROOT; }; + C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 05E2127E19D4DB510098F589 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3EC0CDCBA10D483D9F386E5E /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 05E2127819D4DB510098F589 = { + isa = PBXGroup; + children = ( + 05E2128319D4DB510098F589 /* Sample */, + 05E2128219D4DB510098F589 /* Products */, + 1A943BF0259746F18D6E423F /* Frameworks */, + 1AE410B73DA5C3BD087ACDD7 /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 05E2128219D4DB510098F589 /* Products */ = { + isa = PBXGroup; + children = ( + 05E2128119D4DB510098F589 /* Sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 05E2128319D4DB510098F589 /* Sample */ = { + isa = PBXGroup; + children = ( + 05E2128819D4DB510098F589 /* AppDelegate.h */, + 05E2128919D4DB510098F589 /* AppDelegate.m */, + 05E2128B19D4DB510098F589 /* ViewController.h */, + 05E2128C19D4DB510098F589 /* ViewController.m */, + 05561CFB19D4F94A00CBA93C /* KittenNode.h */, + 05561CFC19D4F94A00CBA93C /* KittenNode.mm */, + 05561CF819D4E77700CBA93C /* BlurbNode.h */, + 05561CF919D4E77700CBA93C /* BlurbNode.m */, + 05E2128419D4DB510098F589 /* Supporting Files */, + ); + path = Sample; + sourceTree = ""; + }; + 05E2128419D4DB510098F589 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */, + 6C2C82AA19EE274300767484 /* Default-667h@2x.png */, + 6C2C82AB19EE274300767484 /* Default-736h@3x.png */, + 05E2128519D4DB510098F589 /* Info.plist */, + 05E2128619D4DB510098F589 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 1A943BF0259746F18D6E423F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 1AE410B73DA5C3BD087ACDD7 /* Pods */ = { + isa = PBXGroup; + children = ( + C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */, + 088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 05E2128019D4DB510098F589 /* Sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */; + buildPhases = ( + E080B80F89C34A25B3488E26 /* Check Pods Manifest.lock */, + 05E2127D19D4DB510098F589 /* Sources */, + 05E2127E19D4DB510098F589 /* Frameworks */, + 05E2127F19D4DB510098F589 /* Resources */, + F012A6F39E0149F18F564F50 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Sample; + productName = Sample; + productReference = 05E2128119D4DB510098F589 /* Sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 05E2127919D4DB510098F589 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0600; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 05E2128019D4DB510098F589 = { + CreatedOnToolsVersion = 6.0.1; + }; + }; + }; + buildConfigurationList = 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 05E2127819D4DB510098F589; + productRefGroup = 05E2128219D4DB510098F589 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 05E2128019D4DB510098F589 /* Sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 05E2127F19D4DB510098F589 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */, + 6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */, + 6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + E080B80F89C34A25B3488E26 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + F012A6F39E0149F18F564F50 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 05E2127D19D4DB510098F589 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 05561CFD19D4F94A00CBA93C /* KittenNode.mm in Sources */, + 05E2128D19D4DB510098F589 /* ViewController.m in Sources */, + 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */, + 05561CFA19D4E77700CBA93C /* BlurbNode.m in Sources */, + 05E2128719D4DB510098F589 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 05E212A219D4DB510098F589 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 05E212A319D4DB510098F589 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 05E212A519D4DB510098F589 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Sample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 05E212A619D4DB510098F589 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Sample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05E212A219D4DB510098F589 /* Debug */, + 05E212A319D4DB510098F589 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05E212A519D4DB510098F589 /* Debug */, + 05E212A619D4DB510098F589 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 05E2127919D4DB510098F589 /* Project object */; +} diff --git a/examples/SynchronousKittens/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/SynchronousKittens/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..a80c038249 --- /dev/null +++ b/examples/SynchronousKittens/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/SynchronousKittens/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme b/examples/SynchronousKittens/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme new file mode 100644 index 0000000000..1e14aa0329 --- /dev/null +++ b/examples/SynchronousKittens/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/SynchronousKittens/Sample.xcworkspace/contents.xcworkspacedata b/examples/SynchronousKittens/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..d98549fd35 --- /dev/null +++ b/examples/SynchronousKittens/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/SynchronousKittens/Sample/AppDelegate.h b/examples/SynchronousKittens/Sample/AppDelegate.h new file mode 100644 index 0000000000..85855277e9 --- /dev/null +++ b/examples/SynchronousKittens/Sample/AppDelegate.h @@ -0,0 +1,20 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +#define UseAutomaticLayout 1 + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/examples/SynchronousKittens/Sample/AppDelegate.m b/examples/SynchronousKittens/Sample/AppDelegate.m new file mode 100644 index 0000000000..1dea563b77 --- /dev/null +++ b/examples/SynchronousKittens/Sample/AppDelegate.m @@ -0,0 +1,27 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "AppDelegate.h" + +#import "ViewController.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.window.backgroundColor = [UIColor whiteColor]; + self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]]; + [self.window makeKeyAndVisible]; + return YES; +} + +@end diff --git a/examples/SynchronousKittens/Sample/BlurbNode.h b/examples/SynchronousKittens/Sample/BlurbNode.h new file mode 100644 index 0000000000..57d8e30787 --- /dev/null +++ b/examples/SynchronousKittens/Sample/BlurbNode.h @@ -0,0 +1,19 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +/** + * Simple node that displays a placekitten.com attribution. + */ +@interface BlurbNode : ASCellNode + +@end diff --git a/examples/SynchronousKittens/Sample/BlurbNode.m b/examples/SynchronousKittens/Sample/BlurbNode.m new file mode 100644 index 0000000000..693ec0cd03 --- /dev/null +++ b/examples/SynchronousKittens/Sample/BlurbNode.m @@ -0,0 +1,122 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "BlurbNode.h" +#import "AppDelegate.h" + +#import +#import + +#import +#import + +static CGFloat kTextPadding = 10.0f; +static NSString *kLinkAttributeName = @"PlaceKittenNodeLinkAttributeName"; + +@interface BlurbNode () +{ + ASTextNode *_textNode; +} + +@end + + +@implementation BlurbNode + +#pragma mark - +#pragma mark ASCellNode. + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + // create a text node + _textNode = [[ASTextNode alloc] init]; + + // configure the node to support tappable links + _textNode.delegate = self; + _textNode.userInteractionEnabled = YES; + _textNode.linkAttributeNames = @[ kLinkAttributeName ]; + + // generate an attributed string using the custom link attribute specified above + NSString *blurb = @"kittens courtesy placekitten.com \U0001F638"; + NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:blurb]; + [string addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"HelveticaNeue-Light" size:16.0f] range:NSMakeRange(0, blurb.length)]; + [string addAttributes:@{ + kLinkAttributeName: [NSURL URLWithString:@"http://placekitten.com/"], + NSForegroundColorAttributeName: [UIColor grayColor], + NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot), + } + range:[blurb rangeOfString:@"placekitten.com"]]; + _textNode.attributedString = string; + + // add it as a subnode, and we're done + [self addSubnode:_textNode]; + + return self; +} + +- (void)didLoad +{ + // enable highlighting now that self.layer has loaded -- see ASHighlightOverlayLayer.h + self.layer.as_allowsHighlightDrawing = YES; + + [super didLoad]; +} + +#if UseAutomaticLayout +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + ASCenterLayoutSpec *centerSpec = [[ASCenterLayoutSpec alloc] init]; + centerSpec.centeringOptions = ASCenterLayoutSpecCenteringX; + centerSpec.sizingOptions = ASCenterLayoutSpecSizingOptionMinimumY; + centerSpec.child = _textNode; + + UIEdgeInsets padding =UIEdgeInsetsMake(kTextPadding, kTextPadding, kTextPadding, kTextPadding); + return [ASInsetLayoutSpec insetLayoutSpecWithInsets:padding child:centerSpec]; +} +#else +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + // called on a background thread. custom nodes must call -measure: on their subnodes in -calculateSizeThatFits: + CGSize measuredSize = [_textNode measure:CGSizeMake(constrainedSize.width - 2 * kTextPadding, + constrainedSize.height - 2 * kTextPadding)]; + return CGSizeMake(constrainedSize.width, measuredSize.height + 2 * kTextPadding); +} + +- (void)layout +{ + // called on the main thread. we'll use the stashed size from above, instead of blocking on text sizing + CGSize textNodeSize = _textNode.calculatedSize; + _textNode.frame = CGRectMake(roundf((self.calculatedSize.width - textNodeSize.width) / 2.0f), + kTextPadding, + textNodeSize.width, + textNodeSize.height); +} +#endif + +#pragma mark - +#pragma mark ASTextNodeDelegate methods. + +- (BOOL)textNode:(ASTextNode *)richTextNode shouldHighlightLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point +{ + // opt into link highlighting -- tap and hold the link to try it! must enable highlighting on a layer, see -didLoad + return YES; +} + +- (void)textNode:(ASTextNode *)richTextNode tappedLinkAttribute:(NSString *)attribute value:(NSURL *)URL atPoint:(CGPoint)point textRange:(NSRange)textRange +{ + // the node tapped a link, open it + [[UIApplication sharedApplication] openURL:URL]; +} + +@end diff --git a/examples/SynchronousKittens/Sample/Info.plist b/examples/SynchronousKittens/Sample/Info.plist new file mode 100644 index 0000000000..35d842827b --- /dev/null +++ b/examples/SynchronousKittens/Sample/Info.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples/SynchronousKittens/Sample/KittenNode.h b/examples/SynchronousKittens/Sample/KittenNode.h new file mode 100644 index 0000000000..3cc23d5a44 --- /dev/null +++ b/examples/SynchronousKittens/Sample/KittenNode.h @@ -0,0 +1,24 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +/** + * Social media-style node that displays a kitten picture and a random length + * of lorem ipsum text. Uses a placekitten.com kitten of the specified size. + */ +@interface KittenNode : ASCellNode + +- (instancetype)initWithKittenOfSize:(CGSize)size; + +- (void)toggleImageEnlargement; + +@end diff --git a/examples/SynchronousKittens/Sample/KittenNode.mm b/examples/SynchronousKittens/Sample/KittenNode.mm new file mode 100644 index 0000000000..847a2629c7 --- /dev/null +++ b/examples/SynchronousKittens/Sample/KittenNode.mm @@ -0,0 +1,197 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "KittenNode.h" +#import "AppDelegate.h" + +#import + +#import +#import + +static const CGFloat kImageSize = 80.0f; +static const CGFloat kOuterPadding = 16.0f; +static const CGFloat kInnerPadding = 10.0f; + + +@interface KittenNode () +{ + CGSize _kittenSize; + + ASNetworkImageNode *_imageNode; + ASTextNode *_textNode; + ASDisplayNode *_divider; + BOOL _isImageEnlarged; + BOOL _swappedTextAndImage; +} + +@end + + +@implementation KittenNode + +// lorem ipsum text courtesy https://kittyipsum.com/ <3 ++ (NSArray *)placeholders +{ + static NSArray *placeholders = nil; + + static dispatch_once_t once; + dispatch_once(&once, ^{ + placeholders = @[ + @"Kitty ipsum dolor sit amet, purr sleep on your face lay down in your way biting, sniff tincidunt a etiam fluffy fur judging you stuck in a tree kittens.", + @"Lick tincidunt a biting eat the grass, egestas enim ut lick leap puking climb the curtains lick.", + @"Lick quis nunc toss the mousie vel, tortor pellentesque sunbathe orci turpis non tail flick suscipit sleep in the sink.", + @"Orci turpis litter box et stuck in a tree, egestas ac tempus et aliquam elit.", + @"Hairball iaculis dolor dolor neque, nibh adipiscing vehicula egestas dolor aliquam.", + @"Sunbathe fluffy fur tortor faucibus pharetra jump, enim jump on the table I don't like that food catnip toss the mousie scratched.", + @"Quis nunc nam sleep in the sink quis nunc purr faucibus, chase the red dot consectetur bat sagittis.", + @"Lick tail flick jump on the table stretching purr amet, rhoncus scratched jump on the table run.", + @"Suspendisse aliquam vulputate feed me sleep on your keyboard, rip the couch faucibus sleep on your keyboard tristique give me fish dolor.", + @"Rip the couch hiss attack your ankles biting pellentesque puking, enim suspendisse enim mauris a.", + @"Sollicitudin iaculis vestibulum toss the mousie biting attack your ankles, puking nunc jump adipiscing in viverra.", + @"Nam zzz amet neque, bat tincidunt a iaculis sniff hiss bibendum leap nibh.", + @"Chase the red dot enim puking chuf, tristique et egestas sniff sollicitudin pharetra enim ut mauris a.", + @"Sagittis scratched et lick, hairball leap attack adipiscing catnip tail flick iaculis lick.", + @"Neque neque sleep in the sink neque sleep on your face, climb the curtains chuf tail flick sniff tortor non.", + @"Ac etiam kittens claw toss the mousie jump, pellentesque rhoncus litter box give me fish adipiscing mauris a.", + @"Pharetra egestas sunbathe faucibus ac fluffy fur, hiss feed me give me fish accumsan.", + @"Tortor leap tristique accumsan rutrum sleep in the sink, amet sollicitudin adipiscing dolor chase the red dot.", + @"Knock over the lamp pharetra vehicula sleep on your face rhoncus, jump elit cras nec quis quis nunc nam.", + @"Sollicitudin feed me et ac in viverra catnip, nunc eat I don't like that food iaculis give me fish.", + ]; + }); + + return placeholders; +} + +- (instancetype)initWithKittenOfSize:(CGSize)size +{ + if (!(self = [super init])) + return nil; + + _kittenSize = size; + + // kitten image, with a solid background colour serving as placeholder + _imageNode = [[ASNetworkImageNode alloc] init]; + _imageNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor(); + _imageNode.URL = [NSURL URLWithString:[NSString stringWithFormat:@"https://placekitten.com/%zd/%zd", + (NSInteger)roundl(_kittenSize.width), + (NSInteger)roundl(_kittenSize.height)]]; +// _imageNode.contentMode = UIViewContentModeCenter; + [_imageNode addTarget:self action:@selector(toggleNodesSwap) forControlEvents:ASControlNodeEventTouchUpInside]; + [self addSubnode:_imageNode]; + + // lorem ipsum text, plus some nice styling + _textNode = [[ASTextNode alloc] init]; + _textNode.attributedString = [[NSAttributedString alloc] initWithString:[self kittyIpsum] + attributes:[self textStyle]]; + [self addSubnode:_textNode]; + + // hairline cell separator + _divider = [[ASDisplayNode alloc] init]; + _divider.backgroundColor = [UIColor lightGrayColor]; + [self addSubnode:_divider]; + + return self; +} + +- (NSString *)kittyIpsum +{ + NSArray *placeholders = [KittenNode placeholders]; + u_int32_t ipsumCount = (u_int32_t)[placeholders count]; + u_int32_t location = arc4random_uniform(ipsumCount); + u_int32_t length = arc4random_uniform(ipsumCount - location); + + NSMutableString *string = [placeholders[location] mutableCopy]; + for (u_int32_t i = location + 1; i < location + length; i++) { + [string appendString:(i % 2 == 0) ? @"\n" : @" "]; + [string appendString:placeholders[i]]; + } + + return string; +} + +- (NSDictionary *)textStyle +{ + UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:12.0f]; + + NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + style.paragraphSpacing = 0.5 * font.lineHeight; + style.hyphenationFactor = 1.0; + + return @{ NSFontAttributeName: font, + NSParagraphStyleAttributeName: style }; +} + +#if UseAutomaticLayout +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + _imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); + _textNode.flexShrink = YES; + + ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; + stackSpec.direction = ASStackLayoutDirectionHorizontal; + stackSpec.spacing = kInnerPadding; + [stackSpec setChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]]; + + ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init]; + insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding); + insetSpec.child = stackSpec; + + return insetSpec; +} + +// With box model, you don't need to override this method, unless you want to add custom logic. +- (void)layout +{ + [super layout]; + + // Manually layout the divider. + CGFloat pixelHeight = 1.0f / [[UIScreen mainScreen] scale]; + _divider.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, pixelHeight); +} +#else +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + CGSize imageSize = CGSizeMake(kImageSize, kImageSize); + CGSize textSize = [_textNode measure:CGSizeMake(constrainedSize.width - kImageSize - 2 * kOuterPadding - kInnerPadding, + constrainedSize.height)]; + + // ensure there's room for the text + CGFloat requiredHeight = MAX(textSize.height, imageSize.height); + return CGSizeMake(constrainedSize.width, requiredHeight + 2 * kOuterPadding); +} + +- (void)layout +{ + CGFloat pixelHeight = 1.0f / [[UIScreen mainScreen] scale]; + _divider.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, pixelHeight); + + _imageNode.frame = CGRectMake(kOuterPadding, kOuterPadding, kImageSize, kImageSize); + + CGSize textSize = _textNode.calculatedSize; + _textNode.frame = CGRectMake(kOuterPadding + kImageSize + kInnerPadding, kOuterPadding, textSize.width, textSize.height); +} +#endif + +- (void)toggleImageEnlargement +{ + _isImageEnlarged = !_isImageEnlarged; + [self setNeedsLayout]; +} + +- (void)toggleNodesSwap +{ + _swappedTextAndImage = !_swappedTextAndImage; + [self setNeedsLayout]; +} + +@end diff --git a/examples/SynchronousKittens/Sample/ViewController.h b/examples/SynchronousKittens/Sample/ViewController.h new file mode 100644 index 0000000000..d0e9200d88 --- /dev/null +++ b/examples/SynchronousKittens/Sample/ViewController.h @@ -0,0 +1,16 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +@interface ViewController : UIViewController + +@end diff --git a/examples/SynchronousKittens/Sample/ViewController.m b/examples/SynchronousKittens/Sample/ViewController.m new file mode 100644 index 0000000000..7989fe15ce --- /dev/null +++ b/examples/SynchronousKittens/Sample/ViewController.m @@ -0,0 +1,214 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "ViewController.h" + +#import +#import + +#import "BlurbNode.h" +#import "KittenNode.h" + + +static const NSInteger kLitterSize = 20; // intial number of kitten cells in ASTableView +static const NSInteger kLitterBatchSize = 10; // number of kitten cells to add to ASTableView +static const NSInteger kMaxLitterSize = 100; // max number of kitten cells allowed in ASTableView + +@interface ViewController () +{ + ASTableView *_tableView; + + // array of boxed CGSizes corresponding to placekitten.com kittens + NSMutableArray *_kittenDataSource; + + BOOL _dataSourceLocked; + NSIndexPath *_blurbNodeIndexPath; +} + +@property (nonatomic, strong) NSMutableArray *kittenDataSource; +@property (atomic, assign) BOOL dataSourceLocked; + +@end + + +@implementation ViewController + +#pragma mark - +#pragma mark UIViewController. + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + _tableView = [[ASTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain asyncDataFetching:YES]; + _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // KittenNode has its own separator + _tableView.asyncDataSource = self; + _tableView.asyncDelegate = self; + + // populate our "data source" with some random kittens + _kittenDataSource = [self createLitterWithSize:kLitterSize]; + + _blurbNodeIndexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + + self.title = @"Kittens"; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit + target:self + action:@selector(toggleEditingMode)]; + + return self; +} + +- (NSMutableArray *)createLitterWithSize:(NSInteger)litterSize +{ + NSMutableArray *kittens = [NSMutableArray arrayWithCapacity:litterSize]; + for (NSInteger i = 0; i < litterSize; i++) { + + // placekitten.com will return the same kitten picture if the same pixel height & width are requested, + // so generate kittens with different width & height values. + u_int32_t deltaX = arc4random_uniform(10) - 5; + u_int32_t deltaY = arc4random_uniform(10) - 5; + CGSize size = CGSizeMake(350 + 2 * deltaX, 350 + 4 * deltaY); + + [kittens addObject:[NSValue valueWithCGSize:size]]; + } + return kittens; +} + +- (void)setKittenDataSource:(NSMutableArray *)kittenDataSource { + ASDisplayNodeAssert(!self.dataSourceLocked, @"Could not update data source when it is locked !"); + + _kittenDataSource = kittenDataSource; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + [self.view addSubview:_tableView]; +} + +- (void)viewWillLayoutSubviews +{ + _tableView.frame = self.view.bounds; +} + +- (BOOL)prefersStatusBarHidden +{ + return YES; +} + +- (void)toggleEditingMode +{ + [_tableView setEditing:!_tableView.editing animated:YES]; +} + + +#pragma mark - +#pragma mark ASTableView. + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + [_tableView deselectRowAtIndexPath:indexPath animated:YES]; + [_tableView beginUpdates]; + // Assume only kitten nodes are selectable (see -tableView:shouldHighlightRowAtIndexPath:). + KittenNode *node = (KittenNode *)[_tableView nodeForRowAtIndexPath:indexPath]; + [node toggleImageEnlargement]; + [_tableView endUpdates]; +} + +- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath +{ + // special-case the first row + if ([_blurbNodeIndexPath compare:indexPath] == NSOrderedSame) { + BlurbNode *node = [[BlurbNode alloc] init]; + return node; + } + + NSValue *size = _kittenDataSource[indexPath.row - 1]; + KittenNode *node = [[KittenNode alloc] initWithKittenOfSize:size.CGSizeValue]; + return node; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + // blurb node + kLitterSize kitties + return 1 + _kittenDataSource.count; +} + +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath +{ + // Enable selection for kitten nodes + return [_blurbNodeIndexPath compare:indexPath] != NSOrderedSame; +} + +- (void)tableViewLockDataSource:(ASTableView *)tableView +{ + self.dataSourceLocked = YES; +} + +- (void)tableViewUnlockDataSource:(ASTableView *)tableView +{ + self.dataSourceLocked = NO; +} + +- (BOOL)shouldBatchFetchForTableView:(UITableView *)tableView +{ + return _kittenDataSource.count < kMaxLitterSize; +} + +- (void)tableView:(UITableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context +{ + NSLog(@"adding kitties"); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + sleep(1); + dispatch_async(dispatch_get_main_queue(), ^{ + + // populate a new array of random-sized kittens + NSArray *moarKittens = [self createLitterWithSize:kLitterBatchSize]; + + NSMutableArray *indexPaths = [[NSMutableArray alloc] init]; + + // find number of kittens in the data source and create their indexPaths + NSInteger existingRows = _kittenDataSource.count + 1; + + for (NSInteger i = 0; i < moarKittens.count; i++) { + [indexPaths addObject:[NSIndexPath indexPathForRow:existingRows + i inSection:0]]; + } + + // add new kittens to the data source & notify table of new indexpaths + [_kittenDataSource addObjectsFromArray:moarKittens]; + [tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; + + [context completeBatchFetching:YES]; + + NSLog(@"kittens added"); + }); + }); +} + +- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath +{ + // Enable editing for Kitten nodes + return [_blurbNodeIndexPath compare:indexPath] != NSOrderedSame; +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (editingStyle == UITableViewCellEditingStyleDelete) { + // Assume only kitten nodes are editable (see -tableView:canEditRowAtIndexPath:). + [_kittenDataSource removeObjectAtIndex:indexPath.row - 1]; + [_tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + } +} + +@end diff --git a/examples/SynchronousKittens/Sample/main.m b/examples/SynchronousKittens/Sample/main.m new file mode 100644 index 0000000000..ae9488711c --- /dev/null +++ b/examples/SynchronousKittens/Sample/main.m @@ -0,0 +1,20 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} From 722b90f1cfae7b9da8475805a0d5389c591bfd86 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Thu, 22 Oct 2015 21:15:08 -0700 Subject: [PATCH 100/127] Implement basic immediate reloadData method on table and collection view --- AsyncDisplayKit/ASCollectionView.h | 8 ++- AsyncDisplayKit/ASCollectionView.mm | 5 +- AsyncDisplayKit/ASTableView.h | 8 +++ AsyncDisplayKit/ASTableView.mm | 7 ++ AsyncDisplayKit/Details/ASDataController.h | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 65 +++++-------------- .../Sample.xcodeproj/project.pbxproj | 16 +++++ .../Sample/ViewController.m | 2 + 8 files changed, 61 insertions(+), 52 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index d3d08048e6..17fef509f4 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -131,7 +131,13 @@ */ - (void)reloadData; -- (void)reloadDataAndWait; +/** + * Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes. + * + * @warning This method is substantially more expensive than UICollectionView's version and will block the main thread + * while all the cells load. + */ +- (void)reloadDataImmediately; /** * Registers the given kind of supplementary node for use in creating node-backed supplementary views. diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index e1535f4c2a..8ff35ef62d 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -280,9 +280,10 @@ static BOOL _isInterceptedSelector(SEL sel) [self reloadDataWithCompletion:nil]; } -- (void)reloadDataAndWait +- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [_dataController reloadDataAndWait]; + ASDisplayNodeAssertMainThread(); + [_dataController reloadDataImmediatelyWithAnimationOptions:animationOptions]; [super reloadData]; } diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index b7588f782b..d7ea79dc2d 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -93,6 +93,14 @@ */ - (void)reloadData; +/** + * Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes. + * + * @warning This method is substantially more expensive than UITableView's version and will block the main thread while + * all the cells load. + */ +- (void)reloadDataImmediately; + /** * begins a batch of insert, delete reload and move operations. This method must be called from the main thread. */ diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 314d7664c6..30f19e48cc 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -334,6 +334,13 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { [self reloadDataWithCompletion:nil]; } +- (void)reloadDataImmediately +{ + ASDisplayNodeAssertMainThread(); + [_dataController reloadDataImmediatelyWithAnimationOptions:UITableViewRowAnimationNone]; + [super reloadData]; +} + - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType { [_layoutController setTuningParameters:tuningParameters forRangeType:rangeType]; diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 0112a298ec..1dde3647b5 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -168,7 +168,7 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion; -- (void)reloadDataAndWait; +- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; /** @name Data Querying */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 6314411dd2..79436b3316 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -151,8 +151,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (void)_layoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { - ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); - if (!nodes.count) { return; } @@ -356,12 +354,22 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } - (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion +{ + [self _reloadDataWithAnimationOptions:animationOptions synchronously:NO completion:completion]; +} + +- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + [self _reloadDataWithAnimationOptions:animationOptions synchronously:YES completion:nil]; +} + +- (void)_reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions synchronously:(BOOL)synchronously completion:(void (^)())completion { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; - [self accessDataSourceWithBlock:^{ + [self accessDataSourceSynchronously:synchronously withBlock:^{ NSUInteger sectionCount = [_dataSource numberOfSectionsInDataController:self]; NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; @@ -373,7 +381,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Allow subclasses to perform setup before going into the edit transaction [self prepareForReloadData]; - [_editingTransactionQueue addOperationWithBlock:^{ + void (^transactionBlock)() = ^{ LOG(@"Edit Transaction - reloadData"); // Remove everything that existed before the reload, now that we're ready to insert replacements @@ -399,52 +407,13 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; if (completion) { dispatch_async(dispatch_get_main_queue(), completion); } - }]; - }]; - }]; -} - -- (void)reloadDataAndWait -{ - [self performEditCommandWithBlock:^{ - ASDisplayNodeAssertMainThread(); - [_editingTransactionQueue waitUntilAllOperationsAreFinished]; - - [self accessDataSourceSynchronously:YES withBlock:^{ - NSUInteger sectionCount = [_dataSource numberOfSectionsInDataController:self]; - NSMutableArray *updatedNodes = [NSMutableArray array]; - NSMutableArray *updatedIndexPaths = [NSMutableArray array]; - [self _populateFromEntireDataSourceWithMutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; + }; - // Measure nodes whose views are loaded before we leave the main thread - [self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths]; - - // Allow subclasses to perform setup before going into the edit transaction - [self prepareForReloadData]; - - LOG(@"Edit Transaction - reloadData"); - - ASDataControllerAnimationOptions animationOptions = UITableViewRowAnimationNone; - - // Remove everything that existed before the reload, now that we're ready to insert replacements - NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind]); - [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; - - NSMutableArray *editingNodes = _editingNodes[ASDataControllerRowNodeKind]; - NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)]; - [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; - - [self willReloadData]; - - // Insert each section - NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount]; - for (int i = 0; i < sectionCount; i++) { - [sections addObject:[[NSMutableArray alloc] init]]; + if (synchronously) { + transactionBlock(); + } else { + [_editingTransactionQueue addOperationWithBlock:transactionBlock]; } - - [self _insertSections:sections atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withAnimationOptions:animationOptions]; - - [self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions]; }]; }]; } diff --git a/examples/SynchronousKittens/Sample.xcodeproj/project.pbxproj b/examples/SynchronousKittens/Sample.xcodeproj/project.pbxproj index ddfd884074..a8ef05da55 100644 --- a/examples/SynchronousKittens/Sample.xcodeproj/project.pbxproj +++ b/examples/SynchronousKittens/Sample.xcodeproj/project.pbxproj @@ -128,6 +128,7 @@ 05E2127E19D4DB510098F589 /* Frameworks */, 05E2127F19D4DB510098F589 /* Resources */, F012A6F39E0149F18F564F50 /* Copy Pods Resources */, + ACCB3408566E7626721EF2D5 /* Embed Pods Frameworks */, ); buildRules = ( ); @@ -184,6 +185,21 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + ACCB3408566E7626721EF2D5 /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; E080B80F89C34A25B3488E26 /* Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/examples/SynchronousKittens/Sample/ViewController.m b/examples/SynchronousKittens/Sample/ViewController.m index 7989fe15ce..5d0b594879 100644 --- a/examples/SynchronousKittens/Sample/ViewController.m +++ b/examples/SynchronousKittens/Sample/ViewController.m @@ -94,6 +94,8 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell [super viewDidLoad]; [self.view addSubview:_tableView]; + + [_tableView reloadDataImmediately]; } - (void)viewWillLayoutSubviews From e0dfe8f0c4f5b56909c9f1789f28f204564f1134 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Fri, 23 Oct 2015 06:10:06 -0700 Subject: [PATCH 101/127] Fix method naming --- AsyncDisplayKit/ASCollectionView.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 8ff35ef62d..118586d61c 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -280,10 +280,10 @@ static BOOL _isInterceptedSelector(SEL sel) [self reloadDataWithCompletion:nil]; } -- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)reloadDataImmediately { ASDisplayNodeAssertMainThread(); - [_dataController reloadDataImmediatelyWithAnimationOptions:animationOptions]; + [_dataController reloadDataImmediatelyWithAnimationOptions:kASCollectionViewAnimationNone]; [super reloadData]; } From 5a8e7daace03cea6956209f9f007afd5b4728aef Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Sat, 24 Oct 2015 00:56:00 -0700 Subject: [PATCH 102/127] Implement ASDisplayNode.name separate from CALayer --- AsyncDisplayKit/ASDisplayNode.h | 5 ++++- AsyncDisplayKit/ASDisplayNode.mm | 13 +++++++++++++ .../Private/ASDisplayNode+UIViewBridge.mm | 12 ------------ AsyncDisplayKit/Private/_ASPendingState.m | 15 --------------- 4 files changed, 17 insertions(+), 28 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 11a138ea4f..f6ce365de9 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -108,6 +108,10 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); /** @name Properties */ +/** + * @abstract The name of this node, which will be displayed in `description`. The default value is nil. + */ +@property (atomic, copy) NSString *name; /** * @abstract Returns whether the node is synchronous. @@ -583,7 +587,6 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); @property (atomic, assign) CGFloat contentsScale; // default=1.0f. See @contentsScaleForDisplay for more info @property (atomic, assign) CATransform3D transform; // default=CATransform3DIdentity @property (atomic, assign) CATransform3D subnodeTransform; // default=CATransform3DIdentity -@property (atomic, copy) NSString *name; // default=nil. Use this to tag your layers in the server-recurse-description / pca or for your own purposes /** * @abstract The node view's background color. diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index c5beb9347b..7e8076ab82 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -47,6 +47,7 @@ // these dynamic properties all defined in ASLayoutOptionsPrivate.m @dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; +@synthesize name = _name; @synthesize preferredFrameSize = _preferredFrameSize; @synthesize isFinalLayoutable = _isFinalLayoutable; @@ -526,6 +527,18 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return (_view != nil || (_flags.layerBacked && _layer != nil)); } +- (NSString *)name +{ + ASDN::MutexLocker l(_propertyLock); + return _name; +} + +- (void)setName:(NSString *)name +{ + ASDN::MutexLocker l(_propertyLock); + _name = [name copy]; +} + - (BOOL)isSynchronous { return _flags.synchronous; diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index 4909abdd46..63ef4ed8da 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -496,18 +496,6 @@ _setToLayer(edgeAntialiasingMask, edgeAntialiasingMask); } -- (NSString *)name -{ - _bridge_prologue; - return _getFromLayer(asyncdisplaykit_name); -} - -- (void)setName:(NSString *)name -{ - _bridge_prologue; - _setToLayer(asyncdisplaykit_name, name); -} - - (BOOL)isAccessibilityElement { _bridge_prologue; diff --git a/AsyncDisplayKit/Private/_ASPendingState.m b/AsyncDisplayKit/Private/_ASPendingState.m index f8c2426971..b649275a87 100644 --- a/AsyncDisplayKit/Private/_ASPendingState.m +++ b/AsyncDisplayKit/Private/_ASPendingState.m @@ -37,7 +37,6 @@ CGFloat borderWidth; CGColorRef borderColor; BOOL asyncTransactionContainer; - NSString *name; BOOL isAccessibilityElement; NSString *accessibilityLabel; NSString *accessibilityHint; @@ -419,20 +418,6 @@ _flags.setAsyncTransactionContainer = YES; } -// This is named this way, since I'm not sure we can change the setter for the CA version -- (void)setAsyncdisplaykit_name:(NSString *)newName -{ - _flags.setName = YES; - if (name != newName) { - name = [newName copy]; - } -} - -- (NSString *)asyncdisplaykit_name -{ - return name; -} - - (BOOL)isAccessibilityElement { return isAccessibilityElement; From 86c6a4648d0cb51ae4c8fbedfb9a6f7123fa0567 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Sat, 24 Oct 2015 00:57:24 -0700 Subject: [PATCH 103/127] Update UIViewBridge documentation to state that bridged properties must be accessed from the main thread once the view or layer is loaded --- AsyncDisplayKit/ASDisplayNode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index f6ce365de9..fb131f9917 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -543,7 +543,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); * Using them will not cause the actual view/layer to be created, and will be applied when it is created (when the view * or layer property is accessed). * - * After the view is created, the properties pass through to the view directly as if called on the main thread. + * - NOTE: After the view or layer is created, the properties pass through to the view or layer directly and must be called on the main thread. * * See UIView and CALayer for documentation on these common properties. */ From 3bd54a3c5c4b1b7643885acead8b5cf514d5ba06 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Sat, 24 Oct 2015 01:03:56 -0700 Subject: [PATCH 104/127] Don't hold lock while copying ASDisplayNode.name --- AsyncDisplayKit/ASDisplayNode.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 7e8076ab82..302087343a 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -535,8 +535,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (void)setName:(NSString *)name { + NSString *copy = [name copy]; + ASDN::MutexLocker l(_propertyLock); - _name = [name copy]; + _name = copy; } - (BOOL)isSynchronous From 55e22d2e57adf4b453469d59bac4df3a8f3f6fa9 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Sat, 24 Oct 2015 01:09:31 -0700 Subject: [PATCH 105/127] Clean up more remnants of Old Ways --- AsyncDisplayKit.xcodeproj/project.pbxproj | 2 -- AsyncDisplayKit/Details/UIView+ASConvenience.h | 5 ----- AsyncDisplayKit/Details/UIView+ASConvenience.m | 13 ------------- AsyncDisplayKit/Private/_ASPendingState.m | 7 ------- 4 files changed, 27 deletions(-) delete mode 100644 AsyncDisplayKit/Details/UIView+ASConvenience.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index ff78556ce4..d50df2658a 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -1533,7 +1533,6 @@ 205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */, 058D0A21195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Sources */, 205F0E101B371875007741D0 /* UICollectionViewLayout+ASConvenience.m in Sources */, - 058D0A25195D050800B7D73C /* UIView+ASConvenience.m in Sources */, CC7FD9DF1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1648,7 +1647,6 @@ 34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, 044284FD1BAA365100D16268 /* UICollectionViewLayout+ASConvenience.m in Sources */, - B35062441B010EFD0018CF92 /* UIView+ASConvenience.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/AsyncDisplayKit/Details/UIView+ASConvenience.h b/AsyncDisplayKit/Details/UIView+ASConvenience.h index d1f0032469..908c99b2fc 100644 --- a/AsyncDisplayKit/Details/UIView+ASConvenience.h +++ b/AsyncDisplayKit/Details/UIView+ASConvenience.h @@ -31,7 +31,6 @@ @property (nonatomic, assign) CGFloat borderWidth; @property (nonatomic, assign, getter = isOpaque) BOOL opaque; @property (nonatomic, retain) __attribute__((NSObject)) CGColorRef borderColor; -@property (nonatomic, copy) NSString *asyncdisplaykit_name; @property (nonatomic, retain) __attribute__((NSObject)) CGColorRef backgroundColor; @property (nonatomic, assign) BOOL allowsEdgeAntialiasing; @property (nonatomic, assign) unsigned int edgeAntialiasingMask; @@ -79,7 +78,3 @@ @property (nonatomic, copy) NSString *accessibilityIdentifier; @end - -@interface CALayer (ASDisplayNodeLayer) -@property (atomic, copy) NSString *asyncdisplaykit_name; -@end diff --git a/AsyncDisplayKit/Details/UIView+ASConvenience.m b/AsyncDisplayKit/Details/UIView+ASConvenience.m deleted file mode 100644 index 6772024821..0000000000 --- a/AsyncDisplayKit/Details/UIView+ASConvenience.m +++ /dev/null @@ -1,13 +0,0 @@ -/* Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "UIView+ASConvenience.h" - -@implementation CALayer (ASDisplayNodeLayer) -@dynamic asyncdisplaykit_name; -@end diff --git a/AsyncDisplayKit/Private/_ASPendingState.m b/AsyncDisplayKit/Private/_ASPendingState.m index b649275a87..84fffd2387 100644 --- a/AsyncDisplayKit/Private/_ASPendingState.m +++ b/AsyncDisplayKit/Private/_ASPendingState.m @@ -132,7 +132,6 @@ @synthesize borderWidth=borderWidth; @synthesize borderColor=borderColor; @synthesize asyncdisplaykit_asyncTransactionContainer=asyncTransactionContainer; -@synthesize asyncdisplaykit_name=name; - (id)init { @@ -626,9 +625,6 @@ if (_flags.setAsyncTransactionContainer) layer.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer; - if (_flags.setName) - layer.asyncdisplaykit_name = name; - if (_flags.setOpaque) ASDisplayNodeAssert(layer.opaque == opaque, @"Didn't set opaque as desired"); } @@ -741,9 +737,6 @@ if (_flags.setAsyncTransactionContainer) view.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer; - if (_flags.setName) - layer.asyncdisplaykit_name = name; - if (_flags.setOpaque) ASDisplayNodeAssert(view.layer.opaque == opaque, @"Didn't set opaque as desired"); From 9b2a68781c044b8a64f82d1be93bbc88ecb3b5da Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Sat, 24 Oct 2015 01:18:24 -0700 Subject: [PATCH 106/127] Use ASObjectIsEqual to make ASDisplayNode.setName faster when setting the same name repeatedly --- AsyncDisplayKit/ASDisplayNode.mm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 302087343a..5d5e8b1d5b 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -19,6 +19,7 @@ #import "_ASDisplayView.h" #import "_ASScopeTimer.h" #import "ASDisplayNodeExtras.h" +#import "ASEqualityHelpers.h" #import "ASInternalHelpers.h" #import "ASLayout.h" @@ -535,10 +536,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (void)setName:(NSString *)name { - NSString *copy = [name copy]; - ASDN::MutexLocker l(_propertyLock); - _name = copy; + if (!ASObjectIsEqual(_name, name)) { + _name = [name copy]; + } } - (BOOL)isSynchronous From 0d53a829855c0ea3829f2cdfdc0d06338d84ac5b Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Sat, 24 Oct 2015 01:20:42 -0700 Subject: [PATCH 107/127] Remove dead flag --- AsyncDisplayKit/Private/_ASPendingState.m | 1 - 1 file changed, 1 deletion(-) diff --git a/AsyncDisplayKit/Private/_ASPendingState.m b/AsyncDisplayKit/Private/_ASPendingState.m index 84fffd2387..0ce85df83e 100644 --- a/AsyncDisplayKit/Private/_ASPendingState.m +++ b/AsyncDisplayKit/Private/_ASPendingState.m @@ -84,7 +84,6 @@ int setBorderWidth:1; int setBorderColor:1; int setAsyncTransactionContainer:1; - int setName:1; int setAllowsEdgeAntialiasing:1; int setEdgeAntialiasingMask:1; int setIsAccessibilityElement:1; From 99113388a810eac1023f912255a7a3018f090aef Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Sat, 24 Oct 2015 10:58:09 -0700 Subject: [PATCH 108/127] Make tests use CALayer.asyncdisplaykit_node.name rather than CALayer.asyncdisplaykit_name as needed --- AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m | 7 +++++-- AsyncDisplayKitTests/ASDisplayNodeTests.m | 13 ++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m b/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m index 02bb2c427e..8eedb0711d 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m @@ -57,8 +57,11 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C @end #define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n -#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; v.layer.asyncdisplaykit_name = @#v -#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; l.asyncdisplaykit_name = @#l + +// FIXME: It's goofy to use `setValue:forKey:` here but importing ASDisplayNodeInternal.h +// in this file causes build to fail (compiler chokes at ASDN::RecursiveMutex) +#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; [v.layer setValue:@#v forKey:@"asyncdisplaykit_node.name"] +#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; [l setValue:@#l forKey:@"asyncdisplaykit_node.name"] @implementation ASDisplayNodeAppearanceTests { diff --git a/AsyncDisplayKitTests/ASDisplayNodeTests.m b/AsyncDisplayKitTests/ASDisplayNodeTests.m index 4ce0c49bcf..1984308ca6 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeTests.m @@ -19,19 +19,22 @@ // Conveniences for making nodes named a certain way #define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n -#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; v.layer.asyncdisplaykit_name = @#v -#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; l.asyncdisplaykit_name = @#l + +// FIXME: It's goofy to use `setValue:forKey:` here but importing ASDisplayNodeInternal.h +// in this file causes build to fail (compiler chokes at ASDN::RecursiveMutex) +#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; [v.layer setValue:@#v forKey:@"asyncdisplaykit_node.name"] +#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; [l setValue:@#l forKey:@"asyncdisplaykit_node.name"] static NSString *orderStringFromSublayers(CALayer *l) { - return [[[l.sublayers valueForKey:@"asyncdisplaykit_name"] allObjects] componentsJoinedByString:@","]; + return [[l.sublayers valueForKey:@"asyncdisplaykit_node.name"] componentsJoinedByString:@","]; } static NSString *orderStringFromSubviews(UIView *v) { - return [[[v.subviews valueForKeyPath:@"layer.asyncdisplaykit_name"] allObjects] componentsJoinedByString:@","]; + return [[v.subviews valueForKeyPath:@"layer.asyncdisplaykit_node.name"] componentsJoinedByString:@","]; } static NSString *orderStringFromSubnodes(ASDisplayNode *n) { - return [[[n.subnodes valueForKey:@"name"] allObjects] componentsJoinedByString:@","]; + return [[n.subnodes valueForKey:@"name"] componentsJoinedByString:@","]; } // Asserts subnode, subview, sublayer order match what you provide here From ddf867b1065551a1f54195a6437fec9c1c641926 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Sat, 24 Oct 2015 10:58:25 -0700 Subject: [PATCH 109/127] Use NO rather than nil to fix build on Xcode 7.1 --- AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index e68020490d..052d088dd8 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -37,8 +37,8 @@ - (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView { if (collectionView == nil) { - _delegateImplementsReferenceSizeForHeader = nil; - _delegateImplementsReferenceSizeForFooter = nil; + _delegateImplementsReferenceSizeForHeader = NO; + _delegateImplementsReferenceSizeForFooter = NO; } else { _delegateImplementsReferenceSizeForHeader = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; _delegateImplementsReferenceSizeForFooter = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; From 52a259a4872e0c9872755549c0b58199f235a415 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 25 Oct 2015 18:30:51 +0200 Subject: [PATCH 110/127] Better names in ASTableView. --- AsyncDisplayKit/ASTableView.mm | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 314d7664c6..8fac3b6047 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -169,8 +169,8 @@ static BOOL _isInterceptedSelector(SEL sel) NSIndexPath *_contentOffsetAdjustmentTopVisibleRow; CGFloat _contentOffsetAdjustment; - CGFloat _maxWidthForNodesConstrainedSize; - BOOL _ignoreMaxWidthChange; + CGFloat _nodesConstrainedWidth; + BOOL _ignoreNodesConstrainedWidthChange; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -224,10 +224,10 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _automaticallyAdjustsContentOffset = NO; - _maxWidthForNodesConstrainedSize = self.bounds.size.width; + _nodesConstrainedWidth = self.bounds.size.width; // If the initial size is 0, expect a size change very soon which is part of the initial configuration // and should not trigger a relayout. - _ignoreMaxWidthChange = (_maxWidthForNodesConstrainedSize == 0); + _ignoreNodesConstrainedWidthChange = (_nodesConstrainedWidth == 0); } - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style @@ -396,13 +396,13 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)layoutSubviews { - if (_maxWidthForNodesConstrainedSize != self.bounds.size.width) { - _maxWidthForNodesConstrainedSize = self.bounds.size.width; + if (_nodesConstrainedWidth != self.bounds.size.width) { + _nodesConstrainedWidth = self.bounds.size.width; // First width change occurs during initial configuration. An expensive relayout pass is unnecessary at that time // and should be avoided, assuming that the initial data loading automatically runs shortly afterward. - if (_ignoreMaxWidthChange) { - _ignoreMaxWidthChange = NO; + if (_ignoreNodesConstrainedWidthChange) { + _ignoreNodesConstrainedWidthChange = NO; } else { [self beginUpdates]; [_dataController relayoutAllNodes]; @@ -828,8 +828,8 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { - return ASSizeRangeMake(CGSizeMake(_maxWidthForNodesConstrainedSize, 0), - CGSizeMake(_maxWidthForNodesConstrainedSize, FLT_MAX)); + return ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, 0), + CGSizeMake(_nodesConstrainedWidth, FLT_MAX)); } - (void)dataControllerLockDataSource From 75331189455af75b2a5c091d7df75380676a47cf Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 25 Oct 2015 18:32:39 +0200 Subject: [PATCH 111/127] Fix unnecessary relayout if table view width is zero initially - During initial configuration of a table view with a zero size, for each cell, there is a mismatch between the widths of its content view and constrained size. However, don't relayout the cell immediately because an initial data loading will be performed shortly afterward. --- AsyncDisplayKit/ASTableView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 8fac3b6047..1a9f470cfc 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -880,7 +880,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { // Normally the content view width equals to the constrained size width (which equals to the table view width). // If there is a mismatch between these values, for example after the table view entered or left editing mode, // content view width is preferred and used to re-measure the cell node. - if (contentViewWidth != constrainedSize.max.width) { + if (!_ignoreNodesConstrainedWidthChange && contentViewWidth != constrainedSize.max.width) { constrainedSize.min.width = contentViewWidth; constrainedSize.max.width = contentViewWidth; From 7b7146276365381d208e2f026bfc5177f708e11a Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 25 Oct 2015 18:41:28 +0200 Subject: [PATCH 112/127] Refactor ASTableViewTests - Add -triggerFirstLayoutMeasurementForTableView: - Move -triggerSizeChangeAndAssertRelayoutAllRowsForTableView:newSize to the end of the file. --- AsyncDisplayKitTests/ASTableViewTests.m | 121 +++++++++++------------- 1 file changed, 53 insertions(+), 68 deletions(-) diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index a0d2a413b7..fa3d826d12 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -234,9 +234,7 @@ tableView.asyncDelegate = dataSource; tableView.asyncDataSource = dataSource; - // Trigger layout measurement on all nodes - [tableView reloadData]; - + [self triggerFirstLayoutMeasurementForTableView:tableView]; [self triggerSizeChangeAndAssertRelayoutAllRowsForTableView:tableView newSize:tableViewFinalSize]; } @@ -264,36 +262,6 @@ [self triggerSizeChangeAndAssertRelayoutAllRowsForTableView:tableView newSize:tableViewFinalSize]; } -- (void)triggerSizeChangeAndAssertRelayoutAllRowsForTableView:(ASTableView *)tableView newSize:(CGSize)newSize -{ - XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSize"]; - - [tableView beginUpdates]; - - CGRect frame = tableView.frame; - frame.size = newSize; - tableView.frame = frame; - [tableView layoutIfNeeded]; - - [tableView endUpdatesAnimated:NO completion:^(BOOL completed) { - for (int section = 0; section < NumberOfSections; section++) { - for (int row = 0; row < NumberOfRowsPerSection; row++) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; - ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; - XCTAssertEqual(node.numberOfLayoutsOnMainThread, 1); - XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, newSize.width); - } - } - [nodesMeasuredUsingNewConstrainedSizeExpectation fulfill]; - }]; - - [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { - if (error) { - XCTFail(@"Expectation failed: %@", error); - } - }]; -} - - (void)testRelayoutVisibleRowsWhenEditingModeIsChanged { CGSize tableViewSize = CGSizeMake(100, 500); @@ -304,24 +272,8 @@ tableView.asyncDelegate = dataSource; tableView.asyncDataSource = dataSource; - - XCTestExpectation *reloadDataExpectation = [self expectationWithDescription:@"reloadData"]; - [tableView reloadDataWithCompletion:^{ - for (int section = 0; section < NumberOfSections; section++) { - for (int row = 0; row < NumberOfRowsPerSection; row++) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; - ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; - XCTAssertEqual(node.numberOfLayoutsOnMainThread, 0); - XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width); - } - } - [reloadDataExpectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { - if (error) { - XCTFail(@"Expectation failed: %@", error); - } - }]; + + [self triggerFirstLayoutMeasurementForTableView:tableView]; NSArray *visibleNodes = [tableView visibleNodes]; XCTAssertGreaterThan(visibleNodes.count, 0); @@ -390,23 +342,7 @@ tableView.asyncDelegate = dataSource; tableView.asyncDataSource = dataSource; - XCTestExpectation *reloadDataExpectation = [self expectationWithDescription:@"reloadData"]; - [tableView reloadDataWithCompletion:^{ - for (int section = 0; section < NumberOfSections; section++) { - for (int row = 0; row < NumberOfRowsPerSection; row++) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; - ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; - XCTAssertEqual(node.numberOfLayoutsOnMainThread, 0); - XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width); - } - } - [reloadDataExpectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { - if (error) { - XCTFail(@"Expectation failed: %@", error); - } - }]; + [self triggerFirstLayoutMeasurementForTableView:tableView]; // Cause table view to enter editing mode and then scroll to the bottom. // The last node should be re-measured on main thread with the new (smaller) content view width. @@ -451,4 +387,53 @@ }]; } +- (void)triggerFirstLayoutMeasurementForTableView:(ASTableView *)tableView{ + XCTestExpectation *reloadDataExpectation = [self expectationWithDescription:@"reloadData"]; + [tableView reloadDataWithCompletion:^{ + for (int section = 0; section < NumberOfSections; section++) { + for (int row = 0; row < NumberOfRowsPerSection; row++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; + ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; + XCTAssertEqual(node.numberOfLayoutsOnMainThread, 0); + XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableView.frame.size.width); + } + } + [reloadDataExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation failed: %@", error); + } + }]; +} + +- (void)triggerSizeChangeAndAssertRelayoutAllRowsForTableView:(ASTableView *)tableView newSize:(CGSize)newSize +{ + XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSize"]; + + [tableView beginUpdates]; + + CGRect frame = tableView.frame; + frame.size = newSize; + tableView.frame = frame; + [tableView layoutIfNeeded]; + + [tableView endUpdatesAnimated:NO completion:^(BOOL completed) { + for (int section = 0; section < NumberOfSections; section++) { + for (int row = 0; row < NumberOfRowsPerSection; row++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; + ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; + XCTAssertEqual(node.numberOfLayoutsOnMainThread, 1); + XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, newSize.width); + } + } + [nodesMeasuredUsingNewConstrainedSizeExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation failed: %@", error); + } + }]; +} + @end From 906d7c759b471eda67b95574199b6f91d46b6782 Mon Sep 17 00:00:00 2001 From: appleguy Date: Sun, 25 Oct 2015 19:40:47 -0700 Subject: [PATCH 113/127] Revert "Make ASDisplayNode.name Thread Safe" --- AsyncDisplayKit.xcodeproj/project.pbxproj | 2 ++ AsyncDisplayKit/ASDisplayNode.h | 7 ++---- AsyncDisplayKit/ASDisplayNode.mm | 16 ------------- .../ASCollectionViewFlowLayoutInspector.m | 4 ++-- .../Details/UIView+ASConvenience.h | 5 ++++ .../Details/UIView+ASConvenience.m | 13 +++++++++++ .../Private/ASDisplayNode+UIViewBridge.mm | 12 ++++++++++ AsyncDisplayKit/Private/_ASPendingState.m | 23 +++++++++++++++++++ .../ASDisplayNodeAppearanceTests.m | 7 ++---- AsyncDisplayKitTests/ASDisplayNodeTests.m | 13 ++++------- 10 files changed, 66 insertions(+), 36 deletions(-) create mode 100644 AsyncDisplayKit/Details/UIView+ASConvenience.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index d50df2658a..ff78556ce4 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -1533,6 +1533,7 @@ 205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */, 058D0A21195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Sources */, 205F0E101B371875007741D0 /* UICollectionViewLayout+ASConvenience.m in Sources */, + 058D0A25195D050800B7D73C /* UIView+ASConvenience.m in Sources */, CC7FD9DF1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1647,6 +1648,7 @@ 34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, 044284FD1BAA365100D16268 /* UICollectionViewLayout+ASConvenience.m in Sources */, + B35062441B010EFD0018CF92 /* UIView+ASConvenience.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index fb131f9917..11a138ea4f 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -108,10 +108,6 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); /** @name Properties */ -/** - * @abstract The name of this node, which will be displayed in `description`. The default value is nil. - */ -@property (atomic, copy) NSString *name; /** * @abstract Returns whether the node is synchronous. @@ -543,7 +539,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); * Using them will not cause the actual view/layer to be created, and will be applied when it is created (when the view * or layer property is accessed). * - * - NOTE: After the view or layer is created, the properties pass through to the view or layer directly and must be called on the main thread. + * After the view is created, the properties pass through to the view directly as if called on the main thread. * * See UIView and CALayer for documentation on these common properties. */ @@ -587,6 +583,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); @property (atomic, assign) CGFloat contentsScale; // default=1.0f. See @contentsScaleForDisplay for more info @property (atomic, assign) CATransform3D transform; // default=CATransform3DIdentity @property (atomic, assign) CATransform3D subnodeTransform; // default=CATransform3DIdentity +@property (atomic, copy) NSString *name; // default=nil. Use this to tag your layers in the server-recurse-description / pca or for your own purposes /** * @abstract The node view's background color. diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 5d5e8b1d5b..c5beb9347b 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -19,7 +19,6 @@ #import "_ASDisplayView.h" #import "_ASScopeTimer.h" #import "ASDisplayNodeExtras.h" -#import "ASEqualityHelpers.h" #import "ASInternalHelpers.h" #import "ASLayout.h" @@ -48,7 +47,6 @@ // these dynamic properties all defined in ASLayoutOptionsPrivate.m @dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; -@synthesize name = _name; @synthesize preferredFrameSize = _preferredFrameSize; @synthesize isFinalLayoutable = _isFinalLayoutable; @@ -528,20 +526,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return (_view != nil || (_flags.layerBacked && _layer != nil)); } -- (NSString *)name -{ - ASDN::MutexLocker l(_propertyLock); - return _name; -} - -- (void)setName:(NSString *)name -{ - ASDN::MutexLocker l(_propertyLock); - if (!ASObjectIsEqual(_name, name)) { - _name = [name copy]; - } -} - - (BOOL)isSynchronous { return _flags.synchronous; diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index 052d088dd8..e68020490d 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -37,8 +37,8 @@ - (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView { if (collectionView == nil) { - _delegateImplementsReferenceSizeForHeader = NO; - _delegateImplementsReferenceSizeForFooter = NO; + _delegateImplementsReferenceSizeForHeader = nil; + _delegateImplementsReferenceSizeForFooter = nil; } else { _delegateImplementsReferenceSizeForHeader = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; _delegateImplementsReferenceSizeForFooter = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; diff --git a/AsyncDisplayKit/Details/UIView+ASConvenience.h b/AsyncDisplayKit/Details/UIView+ASConvenience.h index 908c99b2fc..d1f0032469 100644 --- a/AsyncDisplayKit/Details/UIView+ASConvenience.h +++ b/AsyncDisplayKit/Details/UIView+ASConvenience.h @@ -31,6 +31,7 @@ @property (nonatomic, assign) CGFloat borderWidth; @property (nonatomic, assign, getter = isOpaque) BOOL opaque; @property (nonatomic, retain) __attribute__((NSObject)) CGColorRef borderColor; +@property (nonatomic, copy) NSString *asyncdisplaykit_name; @property (nonatomic, retain) __attribute__((NSObject)) CGColorRef backgroundColor; @property (nonatomic, assign) BOOL allowsEdgeAntialiasing; @property (nonatomic, assign) unsigned int edgeAntialiasingMask; @@ -78,3 +79,7 @@ @property (nonatomic, copy) NSString *accessibilityIdentifier; @end + +@interface CALayer (ASDisplayNodeLayer) +@property (atomic, copy) NSString *asyncdisplaykit_name; +@end diff --git a/AsyncDisplayKit/Details/UIView+ASConvenience.m b/AsyncDisplayKit/Details/UIView+ASConvenience.m new file mode 100644 index 0000000000..6772024821 --- /dev/null +++ b/AsyncDisplayKit/Details/UIView+ASConvenience.m @@ -0,0 +1,13 @@ +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "UIView+ASConvenience.h" + +@implementation CALayer (ASDisplayNodeLayer) +@dynamic asyncdisplaykit_name; +@end diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index 63ef4ed8da..4909abdd46 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -496,6 +496,18 @@ _setToLayer(edgeAntialiasingMask, edgeAntialiasingMask); } +- (NSString *)name +{ + _bridge_prologue; + return _getFromLayer(asyncdisplaykit_name); +} + +- (void)setName:(NSString *)name +{ + _bridge_prologue; + _setToLayer(asyncdisplaykit_name, name); +} + - (BOOL)isAccessibilityElement { _bridge_prologue; diff --git a/AsyncDisplayKit/Private/_ASPendingState.m b/AsyncDisplayKit/Private/_ASPendingState.m index 0ce85df83e..f8c2426971 100644 --- a/AsyncDisplayKit/Private/_ASPendingState.m +++ b/AsyncDisplayKit/Private/_ASPendingState.m @@ -37,6 +37,7 @@ CGFloat borderWidth; CGColorRef borderColor; BOOL asyncTransactionContainer; + NSString *name; BOOL isAccessibilityElement; NSString *accessibilityLabel; NSString *accessibilityHint; @@ -84,6 +85,7 @@ int setBorderWidth:1; int setBorderColor:1; int setAsyncTransactionContainer:1; + int setName:1; int setAllowsEdgeAntialiasing:1; int setEdgeAntialiasingMask:1; int setIsAccessibilityElement:1; @@ -131,6 +133,7 @@ @synthesize borderWidth=borderWidth; @synthesize borderColor=borderColor; @synthesize asyncdisplaykit_asyncTransactionContainer=asyncTransactionContainer; +@synthesize asyncdisplaykit_name=name; - (id)init { @@ -416,6 +419,20 @@ _flags.setAsyncTransactionContainer = YES; } +// This is named this way, since I'm not sure we can change the setter for the CA version +- (void)setAsyncdisplaykit_name:(NSString *)newName +{ + _flags.setName = YES; + if (name != newName) { + name = [newName copy]; + } +} + +- (NSString *)asyncdisplaykit_name +{ + return name; +} + - (BOOL)isAccessibilityElement { return isAccessibilityElement; @@ -624,6 +641,9 @@ if (_flags.setAsyncTransactionContainer) layer.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer; + if (_flags.setName) + layer.asyncdisplaykit_name = name; + if (_flags.setOpaque) ASDisplayNodeAssert(layer.opaque == opaque, @"Didn't set opaque as desired"); } @@ -736,6 +756,9 @@ if (_flags.setAsyncTransactionContainer) view.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer; + if (_flags.setName) + layer.asyncdisplaykit_name = name; + if (_flags.setOpaque) ASDisplayNodeAssert(view.layer.opaque == opaque, @"Didn't set opaque as desired"); diff --git a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m b/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m index 8eedb0711d..02bb2c427e 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m @@ -57,11 +57,8 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C @end #define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n - -// FIXME: It's goofy to use `setValue:forKey:` here but importing ASDisplayNodeInternal.h -// in this file causes build to fail (compiler chokes at ASDN::RecursiveMutex) -#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; [v.layer setValue:@#v forKey:@"asyncdisplaykit_node.name"] -#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; [l setValue:@#l forKey:@"asyncdisplaykit_node.name"] +#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; v.layer.asyncdisplaykit_name = @#v +#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; l.asyncdisplaykit_name = @#l @implementation ASDisplayNodeAppearanceTests { diff --git a/AsyncDisplayKitTests/ASDisplayNodeTests.m b/AsyncDisplayKitTests/ASDisplayNodeTests.m index 1984308ca6..4ce0c49bcf 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeTests.m @@ -19,22 +19,19 @@ // Conveniences for making nodes named a certain way #define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n - -// FIXME: It's goofy to use `setValue:forKey:` here but importing ASDisplayNodeInternal.h -// in this file causes build to fail (compiler chokes at ASDN::RecursiveMutex) -#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; [v.layer setValue:@#v forKey:@"asyncdisplaykit_node.name"] -#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; [l setValue:@#l forKey:@"asyncdisplaykit_node.name"] +#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; v.layer.asyncdisplaykit_name = @#v +#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; l.asyncdisplaykit_name = @#l static NSString *orderStringFromSublayers(CALayer *l) { - return [[l.sublayers valueForKey:@"asyncdisplaykit_node.name"] componentsJoinedByString:@","]; + return [[[l.sublayers valueForKey:@"asyncdisplaykit_name"] allObjects] componentsJoinedByString:@","]; } static NSString *orderStringFromSubviews(UIView *v) { - return [[v.subviews valueForKeyPath:@"layer.asyncdisplaykit_node.name"] componentsJoinedByString:@","]; + return [[[v.subviews valueForKeyPath:@"layer.asyncdisplaykit_name"] allObjects] componentsJoinedByString:@","]; } static NSString *orderStringFromSubnodes(ASDisplayNode *n) { - return [[n.subnodes valueForKey:@"name"] componentsJoinedByString:@","]; + return [[[n.subnodes valueForKey:@"name"] allObjects] componentsJoinedByString:@","]; } // Asserts subnode, subview, sublayer order match what you provide here From 5ea4b1b92deb053a6dcb6a968a646a245514c00d Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 19 Oct 2015 22:08:34 +0300 Subject: [PATCH 114/127] Implement ASChangeSetDataController. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 24 ++ AsyncDisplayKit/ASTableView.mm | 4 +- .../Details/ASChangeSetDataController.h | 19 + .../Details/ASChangeSetDataController.m | 179 ++++++++++ .../Details/ASCollectionDataController.h | 4 +- AsyncDisplayKit/Details/ASDataController.h | 2 +- AsyncDisplayKit/Private/ASInternalHelpers.h | 4 + AsyncDisplayKit/Private/ASInternalHelpers.mm | 9 + .../Private/_ASHierarchyChangeSet.h | 72 ++++ .../Private/_ASHierarchyChangeSet.m | 336 ++++++++++++++++++ 10 files changed, 648 insertions(+), 5 deletions(-) create mode 100644 AsyncDisplayKit/Details/ASChangeSetDataController.h create mode 100644 AsyncDisplayKit/Details/ASChangeSetDataController.m create mode 100644 AsyncDisplayKit/Private/_ASHierarchyChangeSet.h create mode 100644 AsyncDisplayKit/Private/_ASHierarchyChangeSet.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 545f9d05e7..c7da8e107d 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -237,6 +237,14 @@ 9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; + AC026B691BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AC026B6A1BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AC026B6B1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.m */; }; + AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.m */; }; + AC026B6F1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */; }; + AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */; }; + AC026B711BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.m */; }; + AC026B721BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.m */; }; AC026B581BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -597,6 +605,10 @@ 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackBaselinePositionedLayout.mm; sourceTree = ""; }; 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutablePrivate.h; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; + AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASChangeSetDataController.h; sourceTree = ""; }; + AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASChangeSetDataController.m; sourceTree = ""; }; + AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASHierarchyChangeSet.h; sourceTree = ""; }; + AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _ASHierarchyChangeSet.m; sourceTree = ""; }; AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASStaticLayoutSpecSnapshotTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; @@ -890,6 +902,8 @@ 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */, 464052191A3F83C40061C0BA /* ASDataController.h */, 4640521A1A3F83C40061C0BA /* ASDataController.mm */, + AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */, + AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.m */, 05A6D05819D0EB64002DD95E /* ASDealloc2MainObject.h */, 05A6D05919D0EB64002DD95E /* ASDealloc2MainObject.m */, 4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */, @@ -954,6 +968,8 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */, + AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.m */, 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */, 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */, 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */, @@ -1075,6 +1091,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + AC026B691BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */, 058D0A71195D05F800B7D73C /* _AS-objc-internal.h in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, @@ -1133,6 +1150,7 @@ 292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */, ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */, ACF6ED4D1B17847A00DA7C62 /* ASLayoutSpecUtilities.h in Headers */, + AC026B6F1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */, 0516FA3D1A15563400B4EBED /* ASLog.h in Headers */, 0442850D1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */, 0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */, @@ -1182,6 +1200,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + AC026B6A1BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */, B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */, @@ -1202,6 +1221,7 @@ B35062461B010EFD0018CF92 /* ASBasicImageDownloaderInternal.h in Headers */, B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */, 044285081BAA63FE00D16268 /* ASBatchFetching.h in Headers */, + AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */, B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, 34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */, 18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */, @@ -1478,6 +1498,7 @@ 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */, + AC026B711BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, 058D0A19195D050800B7D73C /* _ASDisplayView.mm in Sources */, 9C55866A1BD549CB00B50E3A /* ASAsciiArtBoxCreator.m in Sources */, @@ -1533,6 +1554,7 @@ ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */, ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, + AC026B6B1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */, 055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */, 058D0A17195D050800B7D73C /* ASTextNode.mm in Sources */, 058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */, @@ -1593,6 +1615,7 @@ 9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */, B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.m in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, + AC026B721BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */, B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */, B350624A1B010EFD0018CF92 /* _ASCoreAnimationExtras.mm in Sources */, 2767E9421BB19BD600EA9B77 /* ASViewController.m in Sources */, @@ -1648,6 +1671,7 @@ 34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */, 34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */, 34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */, + AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */, 34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */, B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */, B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */, diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 18695a714b..2433553d08 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -9,7 +9,7 @@ #import "ASTableView.h" #import "ASAssert.h" -#import "ASDataController.h" +#import "ASChangeSetDataController.h" #import "ASCollectionViewLayoutController.h" #import "ASLayoutController.h" #import "ASRangeController.h" @@ -210,7 +210,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _rangeController.layoutController = _layoutController; _rangeController.delegate = self; - _dataController = [[ASDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; + _dataController = [[ASChangeSetDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; _dataController.dataSource = self; _dataController.delegate = _rangeController; diff --git a/AsyncDisplayKit/Details/ASChangeSetDataController.h b/AsyncDisplayKit/Details/ASChangeSetDataController.h new file mode 100644 index 0000000000..26bc5780e5 --- /dev/null +++ b/AsyncDisplayKit/Details/ASChangeSetDataController.h @@ -0,0 +1,19 @@ +// +// ASChangeSetDataController.h +// AsyncDisplayKit +// +// Created by Huy Nguyen on 19/10/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import + +/** + * Subclass of ASDataController that enqueues and sorts edit commands during batch updating (using _ASHierarchyChangeSet). + * + * @see ASDataController + * @see _ASHierarchyChangeSet + */ +@interface ASChangeSetDataController : ASDataController + +@end diff --git a/AsyncDisplayKit/Details/ASChangeSetDataController.m b/AsyncDisplayKit/Details/ASChangeSetDataController.m new file mode 100644 index 0000000000..3e2c2ce118 --- /dev/null +++ b/AsyncDisplayKit/Details/ASChangeSetDataController.m @@ -0,0 +1,179 @@ +// +// ASChangeSetDataController.m +// AsyncDisplayKit +// +// Created by Huy Nguyen on 19/10/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASChangeSetDataController.h" +#import "ASInternalHelpers.h" +#import "_ASHierarchyChangeSet.h" +#import "ASAssert.h" + +@interface ASChangeSetDataController () + +@property (nonatomic, assign) NSUInteger batchUpdateCounter; +@property (nonatomic, strong) _ASHierarchyChangeSet *changeSet; + +@end + +@implementation ASChangeSetDataController + +- (instancetype)initWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled +{ + if (!(self = [super initWithAsyncDataFetching:asyncDataFetchingEnabled])) { + return nil; + } + + _batchUpdateCounter = 0; + + return self; +} + +#pragma mark - Batching (External API) + +- (void)beginUpdates +{ + ASDisplayNodeAssertMainThread(); + if (_batchUpdateCounter == 0) { + _changeSet = [_ASHierarchyChangeSet new]; + } + _batchUpdateCounter++; +} + +- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion +{ + ASDisplayNodeAssertMainThread(); + _batchUpdateCounter--; + + if (_batchUpdateCounter == 0) { + [_changeSet markCompleted]; + + [super beginUpdates]; + + for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) { + [super reloadSections:change.indexSet withAnimationOptions:change.animationOptions]; + } + + for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) { + [super reloadRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions]; + } + + for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeDelete]) { + [super deleteRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions]; + } + + for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) { + [super deleteSections:change.indexSet withAnimationOptions:change.animationOptions]; + } + + for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) { + [super insertSections:change.indexSet withAnimationOptions:change.animationOptions]; + } + + for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeInsert]) { + [super insertRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions]; + } + + [super endUpdatesAnimated:animated completion:completion]; + + _changeSet = nil; + } +} + +- (BOOL)batchUpdating +{ + BOOL batchUpdating = (_batchUpdateCounter != 0); + // _changeSet must be available during batch update + ASDisplayNodeAssertTrue(batchUpdating == (_changeSet != nil)); + return batchUpdating; +} + +#pragma mark - Section Editing (External API) + +- (void)insertSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + ASDisplayNodeAssertMainThread(); + if ([self batchUpdating]) { + [_changeSet insertSections:sections animationOptions:animationOptions]; + } else { + [super insertSections:sections withAnimationOptions:animationOptions]; + } +} + +- (void)deleteSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + ASDisplayNodeAssertMainThread(); + if ([self batchUpdating]) { + [_changeSet deleteSections:sections animationOptions:animationOptions]; + } else { + [super deleteSections:sections withAnimationOptions:animationOptions]; + } +} + +- (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + ASDisplayNodeAssertMainThread(); + if ([self batchUpdating]) { + [_changeSet reloadSections:sections animationOptions:animationOptions]; + } else { + [super reloadSections:sections withAnimationOptions:animationOptions]; + } +} + +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + ASDisplayNodeAssertMainThread(); + if ([self batchUpdating]) { + [_changeSet deleteSections:[NSIndexSet indexSetWithIndex:section] animationOptions:animationOptions]; + [_changeSet insertSections:[NSIndexSet indexSetWithIndex:newSection] animationOptions:animationOptions]; + } else { + [super moveSection:section toSection:newSection withAnimationOptions:animationOptions]; + } +} + +#pragma mark - Row Editing (External API) + +- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + ASDisplayNodeAssertMainThread(); + if ([self batchUpdating]) { + [_changeSet insertItems:indexPaths animationOptions:animationOptions]; + } else { + [super insertRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; + } +} + +- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + ASDisplayNodeAssertMainThread(); + if ([self batchUpdating]) { + [_changeSet deleteItems:indexPaths animationOptions:animationOptions]; + } else { + [super deleteRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; + } +} + +- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + ASDisplayNodeAssertMainThread(); + if ([self batchUpdating]) { + [_changeSet reloadItems:indexPaths animationOptions:animationOptions]; + } else { + [super reloadRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; + } +} + +- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + ASDisplayNodeAssertMainThread(); + if ([self batchUpdating]) { + [_changeSet deleteItems:@[indexPath] animationOptions:animationOptions]; + [_changeSet insertItems:@[newIndexPath] animationOptions:animationOptions]; + } else { + [super moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:animationOptions]; + } +} + +@end diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h index 54ddfbb810..7c66ff41fb 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.h +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -8,7 +8,7 @@ #import -#import +#import #import @class ASDisplayNode; @@ -32,7 +32,7 @@ @end -@interface ASCollectionDataController : ASDataController +@interface ASCollectionDataController : ASChangeSetDataController - (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 1dde3647b5..7847892408 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -97,7 +97,7 @@ typedef NSUInteger ASDataControllerAnimationOptions; * * All operations are asynchronous and thread safe. You can call it from background thread (it is recommendated) and the data * will be updated asynchronously. The dataSource must be updated to reflect the changes before these methods has been called. - * For each data updatin, the corresponding methods in delegate will be called. + * For each data updating, the corresponding methods in delegate will be called. */ @protocol ASFlowLayoutControllerDataSource; @interface ASDataController : ASDealloc2MainObject diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index 6a9d74e97e..b685a0408a 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -26,3 +26,7 @@ CGFloat ASCeilPixelValue(CGFloat f); CGFloat ASRoundPixelValue(CGFloat f); ASDISPLAYNODE_EXTERN_C_END + +@interface NSIndexPath (ASInverseComparison) +- (NSComparisonResult)as_inverseCompare:(NSIndexPath *)otherIndexPath; +@end diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.mm b/AsyncDisplayKit/Private/ASInternalHelpers.mm index f57450d39e..63003963b8 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.mm +++ b/AsyncDisplayKit/Private/ASInternalHelpers.mm @@ -70,3 +70,12 @@ CGFloat ASRoundPixelValue(CGFloat f) { return roundf(f * ASScreenScale()) / ASScreenScale(); } + +@implementation NSIndexPath (ASInverseComparison) + +- (NSComparisonResult)as_inverseCompare:(NSIndexPath *)otherIndexPath +{ + return [otherIndexPath compare:self]; +} + +@end diff --git a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.h b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.h new file mode 100644 index 0000000000..fa67235957 --- /dev/null +++ b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.h @@ -0,0 +1,72 @@ +// +// _ASHierarchyChangeSet.h +// AsyncDisplayKit +// +// Created by Adlai Holler on 9/29/15. +// Copyright © 2015 Facebook. All rights reserved. +// + +#import +#import "ASDataController.h" + +typedef NS_ENUM(NSInteger, _ASHierarchyChangeType) { + _ASHierarchyChangeTypeReload, + _ASHierarchyChangeTypeDelete, + _ASHierarchyChangeTypeInsert +}; + +@interface _ASHierarchySectionChange : NSObject + +// FIXME: Generalize this to `changeMetadata` dict? +@property (nonatomic, readonly) ASDataControllerAnimationOptions animationOptions; + +@property (nonatomic, strong, readonly) NSIndexSet *indexSet; +@property (nonatomic, readonly) _ASHierarchyChangeType changeType; +@end + +@interface _ASHierarchyItemChange : NSObject +@property (nonatomic, readonly) ASDataControllerAnimationOptions animationOptions; + +/// Index paths are sorted descending for changeType .Delete, ascending otherwise +@property (nonatomic, strong, readonly) NSArray *indexPaths; +@property (nonatomic, readonly) _ASHierarchyChangeType changeType; +@end + +@interface _ASHierarchyChangeSet : NSObject + +@property (nonatomic, strong, readonly) NSIndexSet *deletedSections; +@property (nonatomic, strong, readonly) NSIndexSet *insertedSections; +@property (nonatomic, strong, readonly) NSIndexSet *reloadedSections; +@property (nonatomic, strong, readonly) NSArray *insertedItems; +@property (nonatomic, strong, readonly) NSArray *deletedItems; +@property (nonatomic, strong, readonly) NSArray *reloadedItems; + +@property (nonatomic, readonly) BOOL completed; + +/// Call this once the change set has been constructed to prevent future modifications to the changeset. Calling this more than once is a programmer error. +- (void)markCompleted; + +/** + @abstract Return sorted changes of the given type, grouped by animation options. + + Items deleted from deleted sections are not reported. + Items inserted into inserted sections are not reported. + Items reloaded in reloaded sections are not reported. + + The safe order for processing change groups is: + - Reloaded sections & reloaded items + - Deleted items, descending order + - Deleted sections, descending order + - Inserted sections, ascending order + - Inserted items, ascending order + */ +- (NSArray /*<_ASHierarchySectionChange *>*/ *)sectionChangesOfType:(_ASHierarchyChangeType)changeType; +- (NSArray /*<_ASHierarchyItemChange *>*/ *)itemChangesOfType:(_ASHierarchyChangeType)changeType; + +- (void)deleteSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; +- (void)insertSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; +- (void)reloadSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; +- (void)insertItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; +- (void)deleteItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; +- (void)reloadItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; +@end diff --git a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m new file mode 100644 index 0000000000..ad2ee2fb01 --- /dev/null +++ b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m @@ -0,0 +1,336 @@ +// +// _ASHierarchyChangeSet.m +// AsyncDisplayKit +// +// Created by Adlai Holler on 9/29/15. +// Copyright © 2015 Facebook. All rights reserved. +// + +#import "_ASHierarchyChangeSet.h" +#import "ASInternalHelpers.h" + +@interface _ASHierarchySectionChange () +- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexSet:(NSIndexSet *)indexSet animationOptions:(ASDataControllerAnimationOptions)animationOptions; + +/** + On return `changes` is sorted according to the change type with changes coalesced by animationOptions + Assumes: `changes` is [_ASHierarchySectionChange] all with the same changeType + */ ++ (void)sortAndCoalesceChanges:(NSMutableArray *)changes; +@end + +@interface _ASHierarchyItemChange () +- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexPaths:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)animationOptions presorted:(BOOL)presorted; + +/** + On return `changes` is sorted according to the change type with changes coalesced by animationOptions + Assumes: `changes` is [_ASHierarchyItemChange] all with the same changeType + */ ++ (void)sortAndCoalesceChanges:(NSMutableArray *)changes ignoringChangesInSections:(NSIndexSet *)sections; +@end + +@interface _ASHierarchyChangeSet () + +@property (nonatomic, strong, readonly) NSMutableArray *insertItemChanges; +@property (nonatomic, strong, readonly) NSMutableArray *deleteItemChanges; +@property (nonatomic, strong, readonly) NSMutableArray *reloadItemChanges; +@property (nonatomic, strong, readonly) NSMutableArray *insertSectionChanges; +@property (nonatomic, strong, readonly) NSMutableArray *deleteSectionChanges; +@property (nonatomic, strong, readonly) NSMutableArray *reloadSectionChanges; + +@end + +@implementation _ASHierarchyChangeSet { + NSMutableIndexSet *_deletedSections; + NSMutableIndexSet *_insertedSections; + NSMutableIndexSet *_reloadedSections; + NSMutableArray *_insertedItems; + NSMutableArray *_deletedItems; + NSMutableArray *_reloadedItems; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _deletedSections = [NSMutableIndexSet new]; + _insertedSections = [NSMutableIndexSet new]; + _reloadedSections = [NSMutableIndexSet new]; + + _deletedItems = [NSMutableArray new]; + _insertedItems = [NSMutableArray new]; + _reloadedItems = [NSMutableArray new]; + + _insertItemChanges = [NSMutableArray new]; + _deleteItemChanges = [NSMutableArray new]; + _reloadItemChanges = [NSMutableArray new]; + _insertSectionChanges = [NSMutableArray new]; + _deleteSectionChanges = [NSMutableArray new]; + _reloadSectionChanges = [NSMutableArray new]; + } + return self; +} + +#pragma mark External API + +- (void)markCompleted +{ + NSAssert(!_completed, @"Attempt to mark already-completed changeset as completed."); + _completed = YES; + [self _sortAndCoalesceChangeArrays]; +} + +- (NSArray *)sectionChangesOfType:(_ASHierarchyChangeType)changeType +{ + [self _ensureCompleted]; + switch (changeType) { + case _ASHierarchyChangeTypeInsert: + return _insertSectionChanges; + case _ASHierarchyChangeTypeReload: + return _reloadSectionChanges; + case _ASHierarchyChangeTypeDelete: + return _deleteSectionChanges; + default: + NSAssert(NO, @"Request for section changes with invalid type: %lu", changeType); + } +} + +- (NSArray *)itemChangesOfType:(_ASHierarchyChangeType)changeType +{ + [self _ensureCompleted]; + switch (changeType) { + case _ASHierarchyChangeTypeInsert: + return _insertItemChanges; + case _ASHierarchyChangeTypeReload: + return _reloadItemChanges; + case _ASHierarchyChangeTypeDelete: + return _deleteItemChanges; + default: + NSAssert(NO, @"Request for item changes with invalid type: %lu", changeType); + } +} + +- (void)deleteItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeDelete indexPaths:indexPaths animationOptions:options presorted:NO]; + [_deleteItemChanges addObject:change]; +} + +- (void)deleteSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeDelete indexSet:sections animationOptions:options]; + [_deleteSectionChanges addObject:change]; +} + +- (void)insertItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeInsert indexPaths:indexPaths animationOptions:options presorted:NO]; + [_insertItemChanges addObject:change]; +} + +- (void)insertSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeInsert indexSet:sections animationOptions:options]; + [_insertSectionChanges addObject:change]; +} + +- (void)reloadItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeReload indexPaths:indexPaths animationOptions:options presorted:NO]; + [_reloadItemChanges addObject:change]; +} + +- (void)reloadSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeReload indexSet:sections animationOptions:options]; + [_reloadSectionChanges addObject:change]; +} + +#pragma mark Private + +- (BOOL)_ensureNotCompleted +{ + NSAssert(!_completed, @"Attempt to modify completed changeset %@", self); + return !_completed; +} + +- (BOOL)_ensureCompleted +{ + NSAssert(_completed, @"Attempt to process incomplete changeset %@", self); + return _completed; +} + +- (void)_sortAndCoalesceChangeArrays +{ + @autoreleasepool { + [_ASHierarchySectionChange sortAndCoalesceChanges:_deleteSectionChanges]; + [_ASHierarchySectionChange sortAndCoalesceChanges:_insertSectionChanges]; + [_ASHierarchySectionChange sortAndCoalesceChanges:_reloadSectionChanges]; + [_ASHierarchyItemChange sortAndCoalesceChanges:_deleteItemChanges ignoringChangesInSections:_deletedSections]; + [_ASHierarchyItemChange sortAndCoalesceChanges:_reloadItemChanges ignoringChangesInSections:_reloadedSections]; + [_ASHierarchyItemChange sortAndCoalesceChanges:_insertItemChanges ignoringChangesInSections:_insertedSections]; + } +} + +@end + +@implementation _ASHierarchySectionChange + +- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexSet:(NSIndexSet *)indexSet animationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + self = [super init]; + if (self) { + _changeType = changeType; + _indexSet = indexSet; + _animationOptions = animationOptions; + } + return self; +} + ++ (void)sortAndCoalesceChanges:(NSMutableArray *)changes +{ + if (changes.count < 1) { + return; + } + + _ASHierarchyChangeType type = [changes.firstObject changeType]; + + // Lookup table [Int: AnimationOptions] + NSMutableDictionary *animationOptions = [NSMutableDictionary new]; + + // All changed indexes, sorted + NSMutableIndexSet *allIndexes = [NSMutableIndexSet new]; + + for (_ASHierarchySectionChange *change in changes) { + [change.indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, __unused BOOL *stop) { + animationOptions[@(idx)] = @(change.animationOptions); + }]; + [allIndexes addIndexes:change.indexSet]; + } + + // Create new changes by grouping sorted changes by animation option + NSMutableArray *result = [NSMutableArray new]; + + __block ASDataControllerAnimationOptions currentOptions = 0; + __block NSMutableIndexSet *currentIndexes = nil; + + NSEnumerationOptions options = type == _ASHierarchyChangeTypeDelete ? NSEnumerationReverse : kNilOptions; + [allIndexes enumerateIndexesWithOptions:options usingBlock:^(NSUInteger idx, __unused BOOL * stop) { + + ASDataControllerAnimationOptions options = [animationOptions[@(idx)] integerValue]; + if (currentIndexes == nil) { + // Starting a new group + currentIndexes = [NSMutableIndexSet indexSetWithIndex:idx]; + currentOptions = options; + } else if (options == currentOptions) { + // Continuing the current group + [currentIndexes addIndex:idx]; + } else { + // Ending the current group + _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:type indexSet:currentIndexes animationOptions:currentOptions]; + [result addObject:change]; + currentOptions = 0; + currentIndexes = nil; + } + }]; + + if (currentIndexes != nil) { + // Ending the last group + _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:type indexSet:currentIndexes animationOptions:currentOptions]; + [result addObject:change]; + currentOptions = 0; + currentIndexes = nil; + } + [changes setArray:result]; +} + +@end + +@implementation _ASHierarchyItemChange + +- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexPaths:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)animationOptions presorted:(BOOL)presorted +{ + self = [super init]; + if (self) { + _changeType = changeType; + if (presorted) { + _indexPaths = indexPaths; + } else { + SEL sorting = changeType == _ASHierarchyChangeTypeDelete ? @selector(as_inverseCompare:) : @selector(compare:); + _indexPaths = [indexPaths sortedArrayUsingSelector:sorting]; + } + _animationOptions = animationOptions; + } + return self; +} + ++ (void)sortAndCoalesceChanges:(NSMutableArray *)changes ignoringChangesInSections:(NSIndexSet *)sections +{ + if (changes.count < 1) { + return; + } + + _ASHierarchyChangeType type = [changes.firstObject changeType]; + + // Lookup table [NSIndexPath: AnimationOptions] + NSMutableDictionary *animationOptions = [NSMutableDictionary new]; + + // All changed index paths, sorted + NSMutableArray *allIndexPaths = [NSMutableArray new]; + + NSPredicate *indexPathInValidSection = [NSPredicate predicateWithBlock:^BOOL(NSIndexPath *indexPath, __unused NSDictionary *_) { + return ![sections containsIndex:indexPath.section]; + }]; + for (_ASHierarchyItemChange *change in changes) { + for (NSIndexPath *indexPath in change.indexPaths) { + if ([indexPathInValidSection evaluateWithObject:indexPath]) { + animationOptions[indexPath] = @(change.animationOptions); + [allIndexPaths addObject:indexPath]; + } + } + } + + SEL sorting = type == _ASHierarchyChangeTypeDelete ? @selector(as_inverseCompare:) : @selector(compare:); + [allIndexPaths sortUsingSelector:sorting]; + + // Create new changes by grouping sorted changes by animation option + NSMutableArray *result = [NSMutableArray new]; + + ASDataControllerAnimationOptions currentOptions = 0; + NSMutableArray *currentIndexPaths = nil; + + for (NSIndexPath *indexPath in allIndexPaths) { + ASDataControllerAnimationOptions options = [animationOptions[indexPath] integerValue]; + if (currentIndexPaths == nil) { + // Starting a new group + currentIndexPaths = [NSMutableArray arrayWithObject:indexPath]; + currentOptions = options; + } else if (options == currentOptions) { + // Continuing the current group + [currentIndexPaths addObject:indexPath]; + } else { + // Ending the current group + _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:type indexPaths:currentIndexPaths animationOptions:currentOptions presorted:YES]; + [result addObject:change]; + currentOptions = 0; + currentIndexPaths = nil; + } + } + + if (currentIndexPaths != nil) { + // Ending the last group + _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:type indexPaths:currentIndexPaths animationOptions:currentOptions presorted:YES]; + [result addObject:change]; + currentOptions = 0; + currentIndexPaths = nil; + } + [changes setArray:result]; +} + +@end From 0315d628c55351a5152b9c254e2a24871948be43 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Tue, 20 Oct 2015 09:03:55 +0300 Subject: [PATCH 115/127] Rename as_inverseCompare to asdk_inverseCompare for prefix consistency --- AsyncDisplayKit/Private/ASInternalHelpers.h | 2 +- AsyncDisplayKit/Private/ASInternalHelpers.mm | 2 +- AsyncDisplayKit/Private/_ASHierarchyChangeSet.m | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index b685a0408a..00bed4b1d6 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -28,5 +28,5 @@ CGFloat ASRoundPixelValue(CGFloat f); ASDISPLAYNODE_EXTERN_C_END @interface NSIndexPath (ASInverseComparison) -- (NSComparisonResult)as_inverseCompare:(NSIndexPath *)otherIndexPath; +- (NSComparisonResult)asdk_inverseCompare:(NSIndexPath *)otherIndexPath; @end diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.mm b/AsyncDisplayKit/Private/ASInternalHelpers.mm index 63003963b8..d2337a44b8 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.mm +++ b/AsyncDisplayKit/Private/ASInternalHelpers.mm @@ -73,7 +73,7 @@ CGFloat ASRoundPixelValue(CGFloat f) @implementation NSIndexPath (ASInverseComparison) -- (NSComparisonResult)as_inverseCompare:(NSIndexPath *)otherIndexPath +- (NSComparisonResult)asdk_inverseCompare:(NSIndexPath *)otherIndexPath { return [otherIndexPath compare:self]; } diff --git a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m index ad2ee2fb01..f3172c6dde 100644 --- a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m +++ b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m @@ -262,7 +262,7 @@ if (presorted) { _indexPaths = indexPaths; } else { - SEL sorting = changeType == _ASHierarchyChangeTypeDelete ? @selector(as_inverseCompare:) : @selector(compare:); + SEL sorting = changeType == _ASHierarchyChangeTypeDelete ? @selector(asdk_inverseCompare:) : @selector(compare:); _indexPaths = [indexPaths sortedArrayUsingSelector:sorting]; } _animationOptions = animationOptions; @@ -296,7 +296,7 @@ } } - SEL sorting = type == _ASHierarchyChangeTypeDelete ? @selector(as_inverseCompare:) : @selector(compare:); + SEL sorting = type == _ASHierarchyChangeTypeDelete ? @selector(asdk_inverseCompare:) : @selector(compare:); [allIndexPaths sortUsingSelector:sorting]; // Create new changes by grouping sorted changes by animation option From 8745a478ec74d43a557f5673dc72c6607c9a4ddc Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 25 Oct 2015 19:33:34 +0200 Subject: [PATCH 116/127] Add documentation to ASChangeSetDataController. --- AsyncDisplayKit/Details/ASChangeSetDataController.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASChangeSetDataController.h b/AsyncDisplayKit/Details/ASChangeSetDataController.h index 26bc5780e5..cc385a148f 100644 --- a/AsyncDisplayKit/Details/ASChangeSetDataController.h +++ b/AsyncDisplayKit/Details/ASChangeSetDataController.h @@ -9,7 +9,11 @@ #import /** - * Subclass of ASDataController that enqueues and sorts edit commands during batch updating (using _ASHierarchyChangeSet). + * @abstract Subclass of ASDataController that simulates ordering of operations in batch updates defined in UITableView and UICollectionView. + * + * @discussion The ordering is achieved by using _ASHierarchyChangeSet to enqueue and sort operations. + * More information about the ordering and the index paths used for operations can be found here: + * https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TableView_iPhone/ManageInsertDeleteRow/ManageInsertDeleteRow.html#//apple_ref/doc/uid/TP40007451-CH10-SW17 * * @see ASDataController * @see _ASHierarchyChangeSet From ecc5e1ac831138fb133d6a7c0fb0d55178f20b3f Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 25 Oct 2015 19:40:24 +0200 Subject: [PATCH 117/127] Remove duplicate steps while coalescing changes in _ASHierarchyChangeSet --- .../Private/_ASHierarchyChangeSet.m | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m index f3172c6dde..d7c6ecab0b 100644 --- a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m +++ b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m @@ -219,11 +219,13 @@ __block ASDataControllerAnimationOptions currentOptions = 0; __block NSMutableIndexSet *currentIndexes = nil; + NSUInteger lastIndex = allIndexes.lastIndex; NSEnumerationOptions options = type == _ASHierarchyChangeTypeDelete ? NSEnumerationReverse : kNilOptions; [allIndexes enumerateIndexesWithOptions:options usingBlock:^(NSUInteger idx, __unused BOOL * stop) { - ASDataControllerAnimationOptions options = [animationOptions[@(idx)] integerValue]; + BOOL endingCurrentGroup = NO; + if (currentIndexes == nil) { // Starting a new group currentIndexes = [NSMutableIndexSet indexSetWithIndex:idx]; @@ -232,7 +234,12 @@ // Continuing the current group [currentIndexes addIndex:idx]; } else { - // Ending the current group + endingCurrentGroup = YES; + } + + BOOL endingLastGroup = (currentIndexes != nil && lastIndex == idx); + + if (endingCurrentGroup || endingLastGroup) { _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:type indexSet:currentIndexes animationOptions:currentOptions]; [result addObject:change]; currentOptions = 0; @@ -240,13 +247,6 @@ } }]; - if (currentIndexes != nil) { - // Ending the last group - _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:type indexSet:currentIndexes animationOptions:currentOptions]; - [result addObject:change]; - currentOptions = 0; - currentIndexes = nil; - } [changes setArray:result]; } @@ -304,9 +304,12 @@ ASDataControllerAnimationOptions currentOptions = 0; NSMutableArray *currentIndexPaths = nil; + NSIndexPath *lastIndexPath = allIndexPaths.lastObject; for (NSIndexPath *indexPath in allIndexPaths) { ASDataControllerAnimationOptions options = [animationOptions[indexPath] integerValue]; + BOOL endingCurrentGroup = NO; + if (currentIndexPaths == nil) { // Starting a new group currentIndexPaths = [NSMutableArray arrayWithObject:indexPath]; @@ -315,7 +318,12 @@ // Continuing the current group [currentIndexPaths addObject:indexPath]; } else { - // Ending the current group + endingCurrentGroup = YES; + } + + BOOL endingLastGroup = (currentIndexPaths != nil && (NSOrderedSame == [lastIndexPath compare:indexPath])); + + if (endingCurrentGroup || endingLastGroup) { _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:type indexPaths:currentIndexPaths animationOptions:currentOptions presorted:YES]; [result addObject:change]; currentOptions = 0; @@ -323,13 +331,6 @@ } } - if (currentIndexPaths != nil) { - // Ending the last group - _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:type indexPaths:currentIndexPaths animationOptions:currentOptions presorted:YES]; - [result addObject:change]; - currentOptions = 0; - currentIndexPaths = nil; - } [changes setArray:result]; } From fd0b3aaf5224f776b63274e06fb26f300622a159 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 26 Oct 2015 06:29:01 +0200 Subject: [PATCH 118/127] Revert "Revert "Make ASDisplayNode.name Thread Safe"" This reverts commit 906d7c759b471eda67b95574199b6f91d46b6782. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 2 -- AsyncDisplayKit/ASDisplayNode.h | 7 ++++-- AsyncDisplayKit/ASDisplayNode.mm | 16 +++++++++++++ .../Details/UIView+ASConvenience.h | 5 ---- .../Details/UIView+ASConvenience.m | 13 ----------- .../Private/ASDisplayNode+UIViewBridge.mm | 12 ---------- AsyncDisplayKit/Private/_ASPendingState.m | 23 ------------------- .../ASDisplayNodeAppearanceTests.m | 7 ++++-- AsyncDisplayKitTests/ASDisplayNodeTests.m | 13 +++++++---- 9 files changed, 34 insertions(+), 64 deletions(-) delete mode 100644 AsyncDisplayKit/Details/UIView+ASConvenience.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 545f9d05e7..fb4230ab72 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -1544,7 +1544,6 @@ 205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */, 058D0A21195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Sources */, 205F0E101B371875007741D0 /* UICollectionViewLayout+ASConvenience.m in Sources */, - 058D0A25195D050800B7D73C /* UIView+ASConvenience.m in Sources */, CC7FD9DF1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1660,7 +1659,6 @@ 34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, 044284FD1BAA365100D16268 /* UICollectionViewLayout+ASConvenience.m in Sources */, - B35062441B010EFD0018CF92 /* UIView+ASConvenience.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index bb84512f43..0f5c3a8c97 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -108,6 +108,10 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); /** @name Properties */ +/** + * @abstract The name of this node, which will be displayed in `description`. The default value is nil. + */ +@property (atomic, copy) NSString *name; /** * @abstract Returns whether the node is synchronous. @@ -539,7 +543,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); * Using them will not cause the actual view/layer to be created, and will be applied when it is created (when the view * or layer property is accessed). * - * After the view is created, the properties pass through to the view directly as if called on the main thread. + * - NOTE: After the view or layer is created, the properties pass through to the view or layer directly and must be called on the main thread. * * See UIView and CALayer for documentation on these common properties. */ @@ -583,7 +587,6 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); @property (atomic, assign) CGFloat contentsScale; // default=1.0f. See @contentsScaleForDisplay for more info @property (atomic, assign) CATransform3D transform; // default=CATransform3DIdentity @property (atomic, assign) CATransform3D subnodeTransform; // default=CATransform3DIdentity -@property (atomic, copy) NSString *name; // default=nil. Use this to tag your layers in the server-recurse-description / pca or for your own purposes /** * @abstract The node view's background color. diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 2c536006e0..9829ea5311 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -19,6 +19,7 @@ #import "_ASDisplayView.h" #import "_ASScopeTimer.h" #import "ASDisplayNodeExtras.h" +#import "ASEqualityHelpers.h" #import "ASInternalHelpers.h" #import "ASLayout.h" @@ -47,6 +48,7 @@ // these dynamic properties all defined in ASLayoutOptionsPrivate.m @dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; +@synthesize name = _name; @synthesize preferredFrameSize = _preferredFrameSize; @synthesize isFinalLayoutable = _isFinalLayoutable; @@ -526,6 +528,20 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return (_view != nil || (_flags.layerBacked && _layer != nil)); } +- (NSString *)name +{ + ASDN::MutexLocker l(_propertyLock); + return _name; +} + +- (void)setName:(NSString *)name +{ + ASDN::MutexLocker l(_propertyLock); + if (!ASObjectIsEqual(_name, name)) { + _name = [name copy]; + } +} + - (BOOL)isSynchronous { return _flags.synchronous; diff --git a/AsyncDisplayKit/Details/UIView+ASConvenience.h b/AsyncDisplayKit/Details/UIView+ASConvenience.h index d1f0032469..908c99b2fc 100644 --- a/AsyncDisplayKit/Details/UIView+ASConvenience.h +++ b/AsyncDisplayKit/Details/UIView+ASConvenience.h @@ -31,7 +31,6 @@ @property (nonatomic, assign) CGFloat borderWidth; @property (nonatomic, assign, getter = isOpaque) BOOL opaque; @property (nonatomic, retain) __attribute__((NSObject)) CGColorRef borderColor; -@property (nonatomic, copy) NSString *asyncdisplaykit_name; @property (nonatomic, retain) __attribute__((NSObject)) CGColorRef backgroundColor; @property (nonatomic, assign) BOOL allowsEdgeAntialiasing; @property (nonatomic, assign) unsigned int edgeAntialiasingMask; @@ -79,7 +78,3 @@ @property (nonatomic, copy) NSString *accessibilityIdentifier; @end - -@interface CALayer (ASDisplayNodeLayer) -@property (atomic, copy) NSString *asyncdisplaykit_name; -@end diff --git a/AsyncDisplayKit/Details/UIView+ASConvenience.m b/AsyncDisplayKit/Details/UIView+ASConvenience.m deleted file mode 100644 index 6772024821..0000000000 --- a/AsyncDisplayKit/Details/UIView+ASConvenience.m +++ /dev/null @@ -1,13 +0,0 @@ -/* Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "UIView+ASConvenience.h" - -@implementation CALayer (ASDisplayNodeLayer) -@dynamic asyncdisplaykit_name; -@end diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index 4909abdd46..63ef4ed8da 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -496,18 +496,6 @@ _setToLayer(edgeAntialiasingMask, edgeAntialiasingMask); } -- (NSString *)name -{ - _bridge_prologue; - return _getFromLayer(asyncdisplaykit_name); -} - -- (void)setName:(NSString *)name -{ - _bridge_prologue; - _setToLayer(asyncdisplaykit_name, name); -} - - (BOOL)isAccessibilityElement { _bridge_prologue; diff --git a/AsyncDisplayKit/Private/_ASPendingState.m b/AsyncDisplayKit/Private/_ASPendingState.m index f8c2426971..0ce85df83e 100644 --- a/AsyncDisplayKit/Private/_ASPendingState.m +++ b/AsyncDisplayKit/Private/_ASPendingState.m @@ -37,7 +37,6 @@ CGFloat borderWidth; CGColorRef borderColor; BOOL asyncTransactionContainer; - NSString *name; BOOL isAccessibilityElement; NSString *accessibilityLabel; NSString *accessibilityHint; @@ -85,7 +84,6 @@ int setBorderWidth:1; int setBorderColor:1; int setAsyncTransactionContainer:1; - int setName:1; int setAllowsEdgeAntialiasing:1; int setEdgeAntialiasingMask:1; int setIsAccessibilityElement:1; @@ -133,7 +131,6 @@ @synthesize borderWidth=borderWidth; @synthesize borderColor=borderColor; @synthesize asyncdisplaykit_asyncTransactionContainer=asyncTransactionContainer; -@synthesize asyncdisplaykit_name=name; - (id)init { @@ -419,20 +416,6 @@ _flags.setAsyncTransactionContainer = YES; } -// This is named this way, since I'm not sure we can change the setter for the CA version -- (void)setAsyncdisplaykit_name:(NSString *)newName -{ - _flags.setName = YES; - if (name != newName) { - name = [newName copy]; - } -} - -- (NSString *)asyncdisplaykit_name -{ - return name; -} - - (BOOL)isAccessibilityElement { return isAccessibilityElement; @@ -641,9 +624,6 @@ if (_flags.setAsyncTransactionContainer) layer.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer; - if (_flags.setName) - layer.asyncdisplaykit_name = name; - if (_flags.setOpaque) ASDisplayNodeAssert(layer.opaque == opaque, @"Didn't set opaque as desired"); } @@ -756,9 +736,6 @@ if (_flags.setAsyncTransactionContainer) view.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer; - if (_flags.setName) - layer.asyncdisplaykit_name = name; - if (_flags.setOpaque) ASDisplayNodeAssert(view.layer.opaque == opaque, @"Didn't set opaque as desired"); diff --git a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m b/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m index 02bb2c427e..8eedb0711d 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m @@ -57,8 +57,11 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C @end #define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n -#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; v.layer.asyncdisplaykit_name = @#v -#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; l.asyncdisplaykit_name = @#l + +// FIXME: It's goofy to use `setValue:forKey:` here but importing ASDisplayNodeInternal.h +// in this file causes build to fail (compiler chokes at ASDN::RecursiveMutex) +#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; [v.layer setValue:@#v forKey:@"asyncdisplaykit_node.name"] +#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; [l setValue:@#l forKey:@"asyncdisplaykit_node.name"] @implementation ASDisplayNodeAppearanceTests { diff --git a/AsyncDisplayKitTests/ASDisplayNodeTests.m b/AsyncDisplayKitTests/ASDisplayNodeTests.m index 4ce0c49bcf..1984308ca6 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeTests.m @@ -19,19 +19,22 @@ // Conveniences for making nodes named a certain way #define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n -#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; v.layer.asyncdisplaykit_name = @#v -#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; l.asyncdisplaykit_name = @#l + +// FIXME: It's goofy to use `setValue:forKey:` here but importing ASDisplayNodeInternal.h +// in this file causes build to fail (compiler chokes at ASDN::RecursiveMutex) +#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; [v.layer setValue:@#v forKey:@"asyncdisplaykit_node.name"] +#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; [l setValue:@#l forKey:@"asyncdisplaykit_node.name"] static NSString *orderStringFromSublayers(CALayer *l) { - return [[[l.sublayers valueForKey:@"asyncdisplaykit_name"] allObjects] componentsJoinedByString:@","]; + return [[l.sublayers valueForKey:@"asyncdisplaykit_node.name"] componentsJoinedByString:@","]; } static NSString *orderStringFromSubviews(UIView *v) { - return [[[v.subviews valueForKeyPath:@"layer.asyncdisplaykit_name"] allObjects] componentsJoinedByString:@","]; + return [[v.subviews valueForKeyPath:@"layer.asyncdisplaykit_node.name"] componentsJoinedByString:@","]; } static NSString *orderStringFromSubnodes(ASDisplayNode *n) { - return [[[n.subnodes valueForKey:@"name"] allObjects] componentsJoinedByString:@","]; + return [[n.subnodes valueForKey:@"name"] componentsJoinedByString:@","]; } // Asserts subnode, subview, sublayer order match what you provide here From 53a3b76d6d38f8ae28be6832260ece3d3d2f7622 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 26 Oct 2015 07:33:11 +0200 Subject: [PATCH 119/127] Add read-only names to UIView and CALayer - Backed by name of the underlying ASDisplayNode. - Remove goofy usages of `setValue:forKey:` in ASDisplayNodeTests and ASDisplayNodeAppearanceTests. --- AsyncDisplayKit/ASDisplayNode.h | 2 ++ AsyncDisplayKit/ASDisplayNode.mm | 10 ++++++++ .../ASDisplayNodeAppearanceTests.m | 11 +++++---- AsyncDisplayKitTests/ASDisplayNodeTests.m | 24 ++++++++++++------- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 0f5c3a8c97..b6769d7248 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -650,6 +650,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); * @param node The node to be added. */ - (void)addSubnode:(ASDisplayNode *)node; +- (NSString *)name; @end /** CALayer(AsyncDisplayKit) defines convenience method for adding sub-ASDisplayNode to a CALayer. */ @@ -660,6 +661,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); * @param node The node to be added. */ - (void)addSubnode:(ASDisplayNode *)node; +- (NSString *)name; @end @interface ASDisplayNode (Deprecated) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 9829ea5311..c47cc01840 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -2203,6 +2203,11 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode"; } } +- (NSString *)name +{ + return self.asyncdisplaykit_node.name; +} + @end @implementation CALayer (AsyncDisplayKit) @@ -2212,6 +2217,11 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode"; [self addSublayer:node.layer]; } +- (NSString *)name +{ + return self.asyncdisplaykit_node.name; +} + @end diff --git a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m b/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m index 8eedb0711d..e08f50d15f 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m @@ -56,12 +56,15 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C @interface ASDisplayNodeAppearanceTests : XCTestCase @end +// Conveniences for making nodes named a certain way #define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n +#define DeclareViewNamed(v) UIView *v = viewWithName(@#v) -// FIXME: It's goofy to use `setValue:forKey:` here but importing ASDisplayNodeInternal.h -// in this file causes build to fail (compiler chokes at ASDN::RecursiveMutex) -#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; [v.layer setValue:@#v forKey:@"asyncdisplaykit_node.name"] -#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; [l setValue:@#l forKey:@"asyncdisplaykit_node.name"] +static UIView *viewWithName(NSString *name) { + ASDisplayNode *n = [[ASDisplayNode alloc] init]; + n.name = name; + return n.view; +} @implementation ASDisplayNodeAppearanceTests { diff --git a/AsyncDisplayKitTests/ASDisplayNodeTests.m b/AsyncDisplayKitTests/ASDisplayNodeTests.m index 1984308ca6..44d63392e6 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeTests.m @@ -17,20 +17,29 @@ #import "UIView+ASConvenience.h" // Conveniences for making nodes named a certain way - #define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n +#define DeclareViewNamed(v) UIView *v = viewWithName(@#v) +#define DeclareLayerNamed(l) CALayer *l = layerWithName(@#l) -// FIXME: It's goofy to use `setValue:forKey:` here but importing ASDisplayNodeInternal.h -// in this file causes build to fail (compiler chokes at ASDN::RecursiveMutex) -#define DeclareViewNamed(v) UIView *v = [[UIView alloc] init]; [v.layer setValue:@#v forKey:@"asyncdisplaykit_node.name"] -#define DeclareLayerNamed(l) CALayer *l = [[CALayer alloc] init]; [l setValue:@#l forKey:@"asyncdisplaykit_node.name"] +static UIView *viewWithName(NSString *name) { + ASDisplayNode *n = [[ASDisplayNode alloc] init]; + n.name = name; + return n.view; +} + +static CALayer *layerWithName(NSString *name) { + ASDisplayNode *n = [[ASDisplayNode alloc] init]; + n.layerBacked = YES; + n.name = name; + return n.layer; +} static NSString *orderStringFromSublayers(CALayer *l) { - return [[l.sublayers valueForKey:@"asyncdisplaykit_node.name"] componentsJoinedByString:@","]; + return [[l.sublayers valueForKey:@"name"] componentsJoinedByString:@","]; } static NSString *orderStringFromSubviews(UIView *v) { - return [[v.subviews valueForKeyPath:@"layer.asyncdisplaykit_node.name"] componentsJoinedByString:@","]; + return [[v.subviews valueForKey:@"name"] componentsJoinedByString:@","]; } static NSString *orderStringFromSubnodes(ASDisplayNode *n) { @@ -1377,7 +1386,6 @@ static inline BOOL _CGPointEqualToPointWithEpsilon(CGPoint point1, CGPoint point DeclareNodeNamed(b); DeclareNodeNamed(c); DeclareViewNamed(d); - DeclareLayerNamed(e); [parent layer]; From b77522bf4fde2cf9fe7cb9951538812fd8cbd453 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 26 Oct 2015 07:40:54 +0200 Subject: [PATCH 120/127] Remove debugging names of ASDisplayNode and _ASDisplayLayer - They are neither considered internally nor in tests. And when they are accidently considered, they cause tests to fail. - Developers can easily set debugging names themselves whenever needed. --- AsyncDisplayKit/ASDisplayNode.mm | 4 +--- AsyncDisplayKit/Details/_ASDisplayLayer.mm | 5 ----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index c47cc01840..c299cb0d9c 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -460,9 +460,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) _layer = _view.layer; } _layer.asyncdisplaykit_node = self; -#if DEBUG - _layer.name = self.description; -#endif + self.asyncLayer.asyncDelegate = self; { diff --git a/AsyncDisplayKit/Details/_ASDisplayLayer.mm b/AsyncDisplayKit/Details/_ASDisplayLayer.mm index 1d9b023983..99e4bf68e8 100644 --- a/AsyncDisplayKit/Details/_ASDisplayLayer.mm +++ b/AsyncDisplayKit/Details/_ASDisplayLayer.mm @@ -36,11 +36,6 @@ _displaySentinel = [[ASSentinel alloc] init]; self.opaque = YES; - -#if DEBUG - // This is too expensive to do in production on all layers. - self.name = [NSString stringWithFormat:@"%@ (%p)", NSStringFromClass([self class]), self]; -#endif } return self; } From 1b500e407fe7f43a2588460fcde8a90cbb1007e8 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 26 Oct 2015 08:37:17 +0200 Subject: [PATCH 121/127] Fix comment in ASTableView --- AsyncDisplayKit/ASTableView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 2433553d08..9dded57ba4 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -417,7 +417,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } } - // To ensure _maxWidthForNodesConstrainedSize is up-to-date for every usage, this call to super must be done last + // To ensure _nodesConstrainedWidth is up-to-date for every usage, this call to super must be done last [super layoutSubviews]; } From 048566aed5836bdaf18f8ba42a250ab14881a4bf Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 26 Oct 2015 13:11:54 +0200 Subject: [PATCH 122/127] Fix unstable tests related to relayoutAllNodes in ASTableViewTests - After node constrained sizes were changed (because the table view width was changed, for example) and before relayoutAllNodes is executed (on main), if there is any layout tasks being executed on background, the new sizes will be used immediately. Therefore, by the time relayoutAllNodes is performed, some nodes already have an up-to-date layout and don't need to remeasure. - As a result, after relayoutAllNodes is completed, the number of layout passes performed on main thread for each node increased between 0 and 1 (i.e some nodes were remeasured, others weren't). ASTableViewTests is updated to assert this behaviour. --- AsyncDisplayKitTests/ASTableViewTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index fa3d826d12..e492379a62 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -423,7 +423,7 @@ for (int row = 0; row < NumberOfRowsPerSection; row++) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; - XCTAssertEqual(node.numberOfLayoutsOnMainThread, 1); + XCTAssertLessThanOrEqual(node.numberOfLayoutsOnMainThread, 1); XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, newSize.width); } } From 72fca5395fcd7d8db5b9768ac53aa37ead282679 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 26 Oct 2015 13:33:58 +0200 Subject: [PATCH 123/127] Inject an ASDataController class to ASTableView, for testing purposes - Injection can be done via a new internal initializer. The class will be used by ASTableView to create (and configure) a new data controller. - ASTableViewTests now injects its own type of ASDataController. This facilitates new ways for testing ASTableView-specific behaviours. The first application is counting the number of times relayoutAllNodes is called on the data controller. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 6 ++++ AsyncDisplayKit/ASTableView.mm | 27 +++++++++----- AsyncDisplayKit/ASTableViewInternal.h | 31 ++++++++++++++++ AsyncDisplayKitTests/ASTableViewTests.m | 44 +++++++++++++++++++---- 4 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 AsyncDisplayKit/ASTableViewInternal.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index c7da8e107d..4248e98a99 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -254,6 +254,8 @@ AC47D9451B3BB41900AAEE9D /* ASRelativeSize.h in Headers */ = {isa = PBXBuildFile; fileRef = AC47D9431B3BB41900AAEE9D /* ASRelativeSize.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC47D9461B3BB41900AAEE9D /* ASRelativeSize.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */; }; AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = AC6456071B0A335000CF11B8 /* ASCellNode.m */; }; + AC7A2C171BDE11DF0093FE1A /* ASTableViewInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = AC7A2C161BDE11DF0093FE1A /* ASTableViewInternal.h */; }; + AC7A2C181BDE11DF0093FE1A /* ASTableViewInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = AC7A2C161BDE11DF0093FE1A /* ASTableViewInternal.h */; }; ACC945A91BA9E7A0005E1FB8 /* ASViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; ACC945AB1BA9E7C1005E1FB8 /* ASViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */; }; ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -617,6 +619,7 @@ AC47D9431B3BB41900AAEE9D /* ASRelativeSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRelativeSize.h; path = AsyncDisplayKit/Layout/ASRelativeSize.h; sourceTree = ""; }; AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRelativeSize.mm; path = AsyncDisplayKit/Layout/ASRelativeSize.mm; sourceTree = ""; }; AC6456071B0A335000CF11B8 /* ASCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCellNode.m; sourceTree = ""; }; + AC7A2C161BDE11DF0093FE1A /* ASTableViewInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableViewInternal.h; sourceTree = ""; }; ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASViewController.h; sourceTree = ""; }; ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASViewController.m; sourceTree = ""; }; ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBackgroundLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h; sourceTree = ""; }; @@ -803,6 +806,7 @@ D785F6611A74327E00291744 /* ASScrollNode.m */, 055F1A3219ABD3E3004DAFF1 /* ASTableView.h */, 055F1A3319ABD3E3004DAFF1 /* ASTableView.mm */, + AC7A2C161BDE11DF0093FE1A /* ASTableViewInternal.h */, 0574D5E119C110610097DC25 /* ASTableViewProtocols.h */, 058D09DF195D050800B7D73C /* ASTextNode.h */, 058D09E0195D050800B7D73C /* ASTextNode.mm */, @@ -1128,6 +1132,7 @@ 058D0A4C195D05CB00B7D73C /* ASDisplayNode+Subclasses.h in Headers */, 058D0A4A195D05CB00B7D73C /* ASDisplayNode.h in Headers */, 058D0A84195D060300B7D73C /* ASDisplayNodeExtraIvars.h in Headers */, + AC7A2C171BDE11DF0093FE1A /* ASTableViewInternal.h in Headers */, 058D0A4D195D05CB00B7D73C /* ASDisplayNodeExtras.h in Headers */, 058D0A7B195D05F900B7D73C /* ASDisplayNodeInternal.h in Headers */, 0587F9BD1A7309ED00AFF0BA /* ASEditableTextNode.h in Headers */, @@ -1243,6 +1248,7 @@ B350625B1B010F070018CF92 /* ASEqualityHelpers.h in Headers */, B350621B1B010EFD0018CF92 /* ASFlowLayoutController.h in Headers */, B350621D1B010EFD0018CF92 /* ASHighlightOverlayLayer.h in Headers */, + AC7A2C181BDE11DF0093FE1A /* ASTableViewInternal.h in Headers */, B35062531B010EFD0018CF92 /* ASImageNode+CGExtras.h in Headers */, B35062021B010EFD0018CF92 /* ASImageNode.h in Headers */, B350621F1B010EFD0018CF92 /* ASImageProtocols.h in Headers */, diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 9dded57ba4..f1d4d3d5fb 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -7,6 +7,7 @@ */ #import "ASTableView.h" +#import "ASTableViewInternal.h" #import "ASAssert.h" #import "ASChangeSetDataController.h" @@ -155,7 +156,6 @@ static BOOL _isInterceptedSelector(SEL sel) _ASTableViewProxy *_proxyDataSource; _ASTableViewProxy *_proxyDelegate; - ASDataController *_dataController; ASFlowLayoutController *_layoutController; ASRangeController *_rangeController; @@ -174,6 +174,7 @@ static BOOL _isInterceptedSelector(SEL sel) } @property (atomic, assign) BOOL asyncDataSourceLocked; +@property (nonatomic, retain, readwrite) ASDataController *dataController; @end @@ -199,24 +200,29 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } } ++ (Class)dataControllerClass +{ + return [ASChangeSetDataController class]; +} + #pragma mark - #pragma mark Lifecycle -- (void)configureWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled +- (void)configureWithDataControllerClass:(Class)dataControllerClass asyncDataFetching:(BOOL)asyncDataFetching { _layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:ASFlowLayoutDirectionVertical]; _rangeController = [[ASRangeController alloc] init]; _rangeController.layoutController = _layoutController; _rangeController.delegate = self; - - _dataController = [[ASChangeSetDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; + + _dataController = [[dataControllerClass alloc] initWithAsyncDataFetching:asyncDataFetching]; _dataController.dataSource = self; _dataController.delegate = _rangeController; _layoutController.dataSource = _dataController; - _asyncDataFetchingEnabled = asyncDataFetchingEnabled; + _asyncDataFetchingEnabled = asyncDataFetching; _asyncDataSourceLocked = NO; _leadingScreensForBatching = 1.0; @@ -236,6 +242,11 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style asyncDataFetching:(BOOL)asyncDataFetchingEnabled +{ + return [self initWithFrame:frame style:style dataControllerClass:[self.class dataControllerClass] asyncDataFetching:asyncDataFetchingEnabled]; +} + +- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass asyncDataFetching:(BOOL)asyncDataFetchingEnabled { if (!(self = [super initWithFrame:frame style:style])) return nil; @@ -244,8 +255,8 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { // https://github.com/facebook/AsyncDisplayKit/issues/385 asyncDataFetchingEnabled = NO; - [self configureWithAsyncDataFetching:asyncDataFetchingEnabled]; - + [self configureWithDataControllerClass:dataControllerClass asyncDataFetching:asyncDataFetchingEnabled]; + return self; } @@ -254,7 +265,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { if (!(self = [super initWithCoder:aDecoder])) return nil; - [self configureWithAsyncDataFetching:NO]; + [self configureWithDataControllerClass:[self.class dataControllerClass] asyncDataFetching:NO]; return self; } diff --git a/AsyncDisplayKit/ASTableViewInternal.h b/AsyncDisplayKit/ASTableViewInternal.h new file mode 100644 index 0000000000..af940837e2 --- /dev/null +++ b/AsyncDisplayKit/ASTableViewInternal.h @@ -0,0 +1,31 @@ +// +// ASTableViewInternal.h +// AsyncDisplayKit +// +// Created by Huy Nguyen on 26/10/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASTableView.h" + +@class ASDataController; + +@interface ASTableView (Internal) + +@property (nonatomic, retain, readonly) ASDataController *dataController; + +/** + * Initializer. + * + * @param frame A rectangle specifying the initial location and size of the table view in its superview’€™s coordinates. + * The frame of the table view changes as table cells are added and deleted. + * + * @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants. + * + * @param dataControllerClass A controller class injected to and used to create a data controller for the table view. + * + * @param asyncDataFetchingEnabled This option is reserved for future use, and currently a no-op. + */ +- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass asyncDataFetching:(BOOL)asyncDataFetchingEnabled; + +@end diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index e492379a62..b4e137bcd7 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -9,18 +9,44 @@ #import #import "ASTableView.h" +#import "ASTableViewInternal.h" #import "ASDisplayNode+Subclasses.h" +#import "ASChangeSetDataController.h" #define NumberOfSections 10 #define NumberOfRowsPerSection 20 #define NumberOfReloadIterations 50 +@interface ASTestDataController : ASChangeSetDataController +@property (atomic) int numberOfAllNodesRelayouts; +@end + +@implementation ASTestDataController + +- (void)relayoutAllNodes +{ + _numberOfAllNodesRelayouts++; + [super relayoutAllNodes]; +} + +@end + @interface ASTestTableView : ASTableView @property (atomic, copy) void (^willDeallocBlock)(ASTableView *tableView); @end @implementation ASTestTableView +- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style asyncDataFetching:(BOOL)asyncDataFetchingEnabled +{ + return [super initWithFrame:frame style:style dataControllerClass:[ASTestDataController class] asyncDataFetching:asyncDataFetchingEnabled]; +} + +- (ASTestDataController *)testDataController +{ + return (ASTestDataController *)self.dataController; +} + - (void)dealloc { if (_willDeallocBlock) { @@ -219,7 +245,7 @@ } } -- (void)testRelayoutAllRowsWithNonZeroSizeInitially +- (void)testRelayoutAllNodesWithNonZeroSizeInitially { // Initial width of the table view is non-zero and all nodes are measured with this size. // Any subsequence size change must trigger a relayout. @@ -233,12 +259,14 @@ tableView.asyncDelegate = dataSource; tableView.asyncDataSource = dataSource; + + [tableView layoutIfNeeded]; - [self triggerFirstLayoutMeasurementForTableView:tableView]; - [self triggerSizeChangeAndAssertRelayoutAllRowsForTableView:tableView newSize:tableViewFinalSize]; + XCTAssertEqual(tableView.testDataController.numberOfAllNodesRelayouts, 0); + [self triggerSizeChangeAndAssertRelayoutAllNodesForTableView:tableView newSize:tableViewFinalSize]; } -- (void)testRelayoutAllRowsWithZeroSizeInitially +- (void)testRelayoutAllNodesWithZeroSizeInitially { // Initial width of the table view is 0. The first size change is part of the initial config. // Any subsequence size change after that must trigger a relayout. @@ -256,10 +284,10 @@ [superview addSubview:tableView]; // Width and height are swapped so that a later size change will simulate a rotation tableView.frame = CGRectMake(0, 0, tableViewFinalSize.height, tableViewFinalSize.width); - // Trigger layout measurement on all nodes [tableView layoutIfNeeded]; - [self triggerSizeChangeAndAssertRelayoutAllRowsForTableView:tableView newSize:tableViewFinalSize]; + XCTAssertEqual(tableView.testDataController.numberOfAllNodesRelayouts, 0); + [self triggerSizeChangeAndAssertRelayoutAllNodesForTableView:tableView newSize:tableViewFinalSize]; } - (void)testRelayoutVisibleRowsWhenEditingModeIsChanged @@ -407,7 +435,7 @@ }]; } -- (void)triggerSizeChangeAndAssertRelayoutAllRowsForTableView:(ASTableView *)tableView newSize:(CGSize)newSize +- (void)triggerSizeChangeAndAssertRelayoutAllNodesForTableView:(ASTestTableView *)tableView newSize:(CGSize)newSize { XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSize"]; @@ -419,6 +447,8 @@ [tableView layoutIfNeeded]; [tableView endUpdatesAnimated:NO completion:^(BOOL completed) { + XCTAssertEqual(tableView.testDataController.numberOfAllNodesRelayouts, 1); + for (int section = 0; section < NumberOfSections; section++) { for (int row = 0; row < NumberOfRowsPerSection; row++) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; From 6644621c04e1b853b6d70df86ac933cfc2372141 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 26 Oct 2015 14:28:28 +0200 Subject: [PATCH 124/127] Revert "Fix unnecessary relayout if table view width is zero initially" This reverts commit 75331189455af75b2a5c091d7df75380676a47cf. --- AsyncDisplayKit/ASTableView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index f1d4d3d5fb..5ecd82b7ae 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -898,7 +898,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { // Normally the content view width equals to the constrained size width (which equals to the table view width). // If there is a mismatch between these values, for example after the table view entered or left editing mode, // content view width is preferred and used to re-measure the cell node. - if (!_ignoreNodesConstrainedWidthChange && contentViewWidth != constrainedSize.max.width) { + if (contentViewWidth != constrainedSize.max.width) { constrainedSize.min.width = contentViewWidth; constrainedSize.max.width = contentViewWidth; From 88b74a4a00ee70d533c14ec7caad3484e7aed930 Mon Sep 17 00:00:00 2001 From: Vitaly Baev Date: Wed, 28 Oct 2015 11:11:36 +0300 Subject: [PATCH 125/127] Link to NSSpain talk --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 926873e09a..85cd1a648b 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ to implement node hierarchies or custom drawing. * Read the [Getting Started guide](http://asyncdisplaykit.org/guide/) * Get the [sample projects](https://github.com/facebook/AsyncDisplayKit/tree/master/examples) * Browse the [API reference](http://asyncdisplaykit.org/appledoc/) -* Watch the [NSLondon talk](http://vimeo.com/103589245) +* Watch the [NSLondon talk](http://vimeo.com/103589245) or the [NSSpain talk](http://vimeo.com/140409084) ## Testing From 681f20939cc90296f68e43595b9560716bb9179d Mon Sep 17 00:00:00 2001 From: Vitaly Baev Date: Wed, 28 Oct 2015 13:39:25 +0300 Subject: [PATCH 126/127] Fix link to NSSpain talk --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 85cd1a648b..2a249299a1 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ to implement node hierarchies or custom drawing. * Read the [Getting Started guide](http://asyncdisplaykit.org/guide/) * Get the [sample projects](https://github.com/facebook/AsyncDisplayKit/tree/master/examples) * Browse the [API reference](http://asyncdisplaykit.org/appledoc/) -* Watch the [NSLondon talk](http://vimeo.com/103589245) or the [NSSpain talk](http://vimeo.com/140409084) +* Watch the [NSLondon talk](http://vimeo.com/103589245) or the [NSSpain talk](https://www.youtube.com/watch?v=RY_X7l1g79Q) ## Testing From a2bc2074cc7fdca7717be7873ae34c1933a5038e Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Wed, 28 Oct 2015 11:01:01 -0700 Subject: [PATCH 127/127] release any CGColor's set while the node is in a pending state --- AsyncDisplayKit/Private/_ASPendingState.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/AsyncDisplayKit/Private/_ASPendingState.m b/AsyncDisplayKit/Private/_ASPendingState.m index 0ce85df83e..4d05431408 100644 --- a/AsyncDisplayKit/Private/_ASPendingState.m +++ b/AsyncDisplayKit/Private/_ASPendingState.m @@ -981,4 +981,11 @@ return pendingState; } +- (void)dealloc +{ + CGColorRelease(backgroundColor); + CGColorRelease(shadowColor); + CGColorRelease(borderColor); +} + @end