diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index fb818eb7e6..ec88645ea7 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -288,6 +288,16 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)clearFetchedData; +/** + * Forces the .contentInset to be UIEdgeInsetsZero. + * + * @discussion By default, UIKit sets the top inset to the navigation bar height, even for horizontally + * scrolling views. This can only be disabled by setting a property on the containing UIViewController, + * automaticallyAdjustsScrollViewInsets, which may not be accessible. ASPagerNode uses this to ensure + * its flow layout behaves predictably and does not log undefined layout warnings. + */ +@property (nonatomic) BOOL zeroContentInsets; + @end diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 2e7c17c017..754fd761fb 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -17,6 +17,7 @@ #import "ASInternalHelpers.h" #import "ASRangeController.h" #import "UICollectionViewLayout+ASConvenience.h" +#import "_ASDisplayLayer.h" static const NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimationNone; static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero}; @@ -113,6 +114,12 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; @implementation ASCollectionView +// Using _ASDisplayLayer ensures things like -layout are properly forwarded to ASCollectionNode. ++ (Class)layerClass +{ + return [_ASDisplayLayer class]; +} + #pragma mark - #pragma mark Lifecycle. @@ -221,7 +228,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)reloadDataWithCompletion:(void (^)())completion { - ASDisplayNodeAssert(self.asyncDelegate, @"ASCollectionView's asyncDelegate property must be set."); ASPerformBlockOnMainThread(^{ _superIsPendingDataLoad = YES; [super reloadData]; @@ -557,6 +563,10 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)layoutSubviews { + if (_zeroContentInsets) { + self.contentInset = UIEdgeInsetsZero; + } + if (! CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, self.bounds.size)) { _maxSizeForNodesConstrainedSize = self.bounds.size; diff --git a/AsyncDisplayKit/ASPagerNode.h b/AsyncDisplayKit/ASPagerNode.h index 6396d4b332..e715e48bda 100644 --- a/AsyncDisplayKit/ASPagerNode.h +++ b/AsyncDisplayKit/ASPagerNode.h @@ -17,6 +17,8 @@ - (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index; @end +// WARNING: ASPagerNode is new in AsyncDisplayKit 1.9.4 and not yet widely tested. +// Details of its API or behavior may change in future releases @interface ASPagerNode : ASCollectionNode // Configures a default horizontal, paging flow layout with 0 inter-item spacing. diff --git a/AsyncDisplayKit/ASPagerNode.m b/AsyncDisplayKit/ASPagerNode.m index 8a7c7fce63..4bb338439c 100644 --- a/AsyncDisplayKit/ASPagerNode.m +++ b/AsyncDisplayKit/ASPagerNode.m @@ -10,7 +10,8 @@ #import "ASDelegateProxy.h" #import "ASDisplayNode+Subclasses.h" -@interface ASPagerNode () { +@interface ASPagerNode () +{ UICollectionViewFlowLayout *_flowLayout; ASPagerNodeProxy *_proxy; id _pagerDataSource; @@ -46,25 +47,6 @@ return self; } -- (void)setDataSource:(id )pagerDataSource -{ - if (pagerDataSource != _pagerDataSource) { - _pagerDataSource = pagerDataSource; - _proxy = pagerDataSource ? [[ASPagerNodeProxy alloc] initWithTarget:pagerDataSource interceptor:self] : nil; - super.dataSource = (id )_proxy; - } -} - -- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy -{ - [self setDataSource:nil]; -} - -- (id )dataSource -{ - return _pagerDataSource; -} - - (void)didLoad { [super didLoad]; @@ -77,6 +59,11 @@ cv.showsHorizontalScrollIndicator = NO; cv.scrollsToTop = NO; + // Zeroing contentInset is important, as UIKit will set the top inset for the navigation bar even though + // our view is only horizontally scrollable. This causes UICollectionViewFlowLayout to log a warning. + // From here we cannot disable this directly (UIViewController's automaticallyAdjustsScrollViewInsets). + cv.zeroContentInsets = YES; + ASRangeTuningParameters preloadParams = { .leadingBufferScreenfuls = 2.0, .trailingBufferScreenfuls = 2.0 }; ASRangeTuningParameters renderParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 }; [self setTuningParameters:preloadParams forRangeType:ASLayoutRangeTypePreload]; @@ -95,13 +82,14 @@ - (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath { - ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load paging nodes"); - return [_pagerDataSource pagerNode:self nodeAtIndex:indexPath.item]; + ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load nodes to display"); + ASCellNode *pageNode = [_pagerDataSource pagerNode:self nodeAtIndex:indexPath.item]; + return pageNode; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load paging nodes"); + ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load nodes to display"); return [_pagerDataSource numberOfPagesInPagerNode:self]; } @@ -110,4 +98,25 @@ return ASSizeRangeMake(CGSizeZero, self.view.bounds.size); } +#pragma mark - Data Source Proxy + +- (id )dataSource +{ + return _pagerDataSource; +} + +- (void)setDataSource:(id )pagerDataSource +{ + if (pagerDataSource != _pagerDataSource) { + _pagerDataSource = pagerDataSource; + _proxy = pagerDataSource ? [[ASPagerNodeProxy alloc] initWithTarget:pagerDataSource interceptor:self] : nil; + super.dataSource = (id )_proxy; + } +} + +- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy +{ + [self setDataSource:nil]; +} + @end diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 36c990b993..71e0c93cbf 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -18,6 +18,7 @@ #import "ASLayout.h" #import "ASLayoutController.h" #import "ASRangeController.h" +#import "_ASDisplayLayer.h" #import @@ -120,6 +121,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; @implementation ASTableView +// Using _ASDisplayLayer ensures things like -layout are properly forwarded to ASTableNode. ++ (Class)layerClass +{ + return [_ASDisplayLayer class]; +} + + (Class)dataControllerClass { return [ASChangeSetDataController class]; @@ -280,7 +287,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)reloadDataWithCompletion:(void (^)())completion { - ASDisplayNodeAssert(self.asyncDelegate, @"ASTableView's asyncDelegate property must be set."); ASPerformBlockOnMainThread(^{ [super reloadData]; });