diff --git a/AsyncDisplayKit/ASCollectionNode.m b/AsyncDisplayKit/ASCollectionNode.m index cd0b7367..eb0ddd30 100644 --- a/AsyncDisplayKit/ASCollectionNode.m +++ b/AsyncDisplayKit/ASCollectionNode.m @@ -9,6 +9,10 @@ #import "ASCollectionNode.h" #import "ASDisplayNode+Subclasses.h" +@interface ASCollectionView (Internal) +- (ASCollectionView *)_initWithCollectionViewLayout:(UICollectionViewLayout *)layout; +@end + @implementation ASCollectionNode - (instancetype)init @@ -20,7 +24,7 @@ - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout { - if (self = [super initWithViewBlock:^UIView *{ return [[ASCollectionView alloc] initWithCollectionViewLayout:layout]; }]) { + if (self = [super initWithViewBlock:^UIView *{ return [[ASCollectionView alloc] _initWithCollectionViewLayout:layout]; }]) { return self; } return nil; @@ -31,6 +35,11 @@ return (ASCollectionView *)[super view]; } +- (void)visibilityDidChange:(BOOL)isVisible +{ + +} + - (void)clearContents { [super clearContents]; diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 31b3722d..8d715e3e 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -12,7 +12,7 @@ #import #import #import - +#import @class ASCellNode; @protocol ASCollectionViewDataSource; @@ -27,6 +27,11 @@ */ @interface ASCollectionView : UICollectionView +/** + * Initializer. + * + * @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil. + */ - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout; @property (nonatomic, weak) id asyncDataSource; @@ -52,27 +57,6 @@ */ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; -/** - * Initializer. - * - * @param frame The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview - * in which you plan to add it. This frame is passed to the superclass during initialization. - * - * @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. - * Must not be nil. - * - * @param asyncDataFetchingEnabled Enable the data fetching in async mode. - * - * @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 - * large scale data. On another hand, the application code need take the responsibility to avoid data inconsistence. Specifically, - * we will lock the data source through `collectionViewLockDataSource`, and unlock it by `collectionViewUnlockDataSource` after the data fetching. - * The application should not update the data source while the data source is locked, to keep data consistence. - */ -- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled; - /** * The number of screens left to scroll before the delegate -collectionView:beginBatchFetchingWithContext: is called. * @@ -429,4 +413,10 @@ */ - (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section; -@end \ No newline at end of file +@end + +@interface ASCollectionView (Deprecated) + +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled ASDISPLAYNODE_DEPRECATED; + +@end diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index ffd1c660..0aeefb27 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -9,6 +9,7 @@ #import "ASAssert.h" #import "ASBatchFetching.h" #import "ASCollectionView.h" +#import "ASCollectionNode.h" #import "ASCollectionDataController.h" #import "ASCollectionViewLayoutController.h" #import "ASCollectionViewFlowLayoutInspector.h" @@ -31,7 +32,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; */ static BOOL _isInterceptedSelector(SEL sel) { - return ( + return ( // handled by ASCollectionView node<->cell machinery sel == @selector(collectionView:cellForItemAtIndexPath:) || sel == @selector(collectionView:layout:sizeForItemAtIndexPath:) || @@ -44,7 +45,7 @@ static BOOL _isInterceptedSelector(SEL sel) // used for ASRangeController visibility updates sel == @selector(collectionView:willDisplayCell:forItemAtIndexPath:) || sel == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) || - + // used for batch fetching API sel == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) ); @@ -70,7 +71,7 @@ static BOOL _isInterceptedSelector(SEL sel) if (!self) { return nil; } - + ASDisplayNodeAssert(target, @"target must not be nil"); ASDisplayNodeAssert(interceptor, @"interceptor must not be nil"); @@ -84,7 +85,7 @@ static BOOL _isInterceptedSelector(SEL sel) { ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil"); - + return (_isInterceptedSelector(aSelector) || [_target respondsToSelector:aSelector]); } @@ -92,7 +93,7 @@ static BOOL _isInterceptedSelector(SEL sel) { ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil"); - + if (_isInterceptedSelector(aSelector)) { return _interceptor; } @@ -140,21 +141,21 @@ static BOOL _isInterceptedSelector(SEL sel) @interface ASCollectionView () { _ASCollectionViewProxy *_proxyDataSource; _ASCollectionViewProxy *_proxyDelegate; - + ASCollectionDataController *_dataController; ASRangeController *_rangeController; ASCollectionViewLayoutController *_layoutController; ASCollectionViewFlowLayoutInspector *_flowLayoutInspector; - + BOOL _performingBatchUpdates; NSMutableArray *_batchUpdateBlocks; - + BOOL _asyncDataFetchingEnabled; BOOL _asyncDelegateImplementsInsetSection; BOOL _collectionViewLayoutImplementsInsetSection; BOOL _asyncDataSourceImplementsConstrainedSizeForNode; BOOL _queuedNodeSizeUpdate; - + ASBatchContext *_batchContext; CGSize _maxSizeForNodesConstrainedSize; @@ -172,7 +173,7 @@ static BOOL _isInterceptedSelector(SEL sel) * You will get an assertion failure saying `Invalid number of items in section 0. * The number of items after the update (1) must be equal to the number of items before the update (1) plus or minus the items added and removed (1 added, 0 removed).` * The collection view never queried your data source before the update to see that it actually had 0 items. - */ + */ BOOL _superIsPendingDataLoad; } @@ -187,48 +188,53 @@ static BOOL _isInterceptedSelector(SEL sel) - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout { - return [self initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + return [self initWithFrame:CGRectZero collectionViewLayout:layout]; } - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { - return [self initWithFrame:frame collectionViewLayout:layout asyncDataFetching:NO]; +// ASCollectionNode *collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout]; +// collectionNode.frame = frame; +// return collectionNode.view; + return [self _initWithFrame:frame collectionViewLayout:layout]; } +// FIXME: This method is deprecated and will probably be removed in or shortly after 2.0. - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled +{ + return [self initWithFrame:frame collectionViewLayout:layout]; +} + +- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { if (!(self = [super initWithFrame:frame collectionViewLayout:layout])) return nil; - // FIXME: asyncDataFetching is currently unreliable for some use cases. - // https://github.com/facebook/AsyncDisplayKit/issues/385 - asyncDataFetchingEnabled = NO; - _layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self]; - + _rangeController = [[ASRangeController alloc] init]; _rangeController.dataSource = self; _rangeController.delegate = self; _rangeController.layoutController = _layoutController; - - _dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; + + _dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:NO]; _dataController.delegate = _rangeController; _dataController.dataSource = self; _batchContext = [[ASBatchContext alloc] init]; - + _leadingScreensForBatching = 1.0; - - _asyncDataFetchingEnabled = asyncDataFetchingEnabled; + + _asyncDataFetchingEnabled = NO; _asyncDataSourceLocked = NO; - + _performingBatchUpdates = NO; _batchUpdateBlocks = [NSMutableArray array]; - + _superIsPendingDataLoad = YES; _collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)]; - + _maxSizeForNodesConstrainedSize = self.bounds.size; // 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. @@ -262,13 +268,13 @@ static BOOL _isInterceptedSelector(SEL sel) */ - (ASCollectionViewFlowLayoutInspector *)flowLayoutInspector { - 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]; - } - return _flowLayoutInspector; + 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]; + } + return _flowLayoutInspector; } #pragma mark - @@ -314,7 +320,7 @@ static BOOL _isInterceptedSelector(SEL sel) // the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource // will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out // super.dataSource in this case because calls to _ASTableViewProxy will start failing and cause crashes. - + if (asyncDataSource == nil) { super.dataSource = nil; _asyncDataSource = nil; @@ -334,7 +340,7 @@ static BOOL _isInterceptedSelector(SEL sel) // the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate // will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out // super.delegate in this case because calls to _ASTableViewProxy will start failing and cause crashes. - + if (asyncDelegate == nil) { // order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes // in UIScrollViewAccessibility. @@ -348,7 +354,7 @@ static BOOL _isInterceptedSelector(SEL sel) super.delegate = (id)_proxyDelegate; _asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); } - + [_layoutInspector didChangeCollectionViewDelegate:asyncDelegate]; } @@ -409,7 +415,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion { ASDisplayNodeAssertMainThread(); - + [_dataController beginUpdates]; updates(); [_dataController endUpdatesAnimated:animated completion:completion]; @@ -487,7 +493,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { _ASCollectionViewCell *cell = [self dequeueReusableCellWithReuseIdentifier:kCellReuseIdentifier forIndexPath:indexPath]; - + ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; cell.node = node; [_rangeController configureContentView:cell.contentView forCellNode:node]; @@ -524,7 +530,7 @@ static BOOL _isInterceptedSelector(SEL sel) CGPoint scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview]; return [self scrollDirectionForVelocity:scrollVelocity]; } - + - (ASScrollDirection)scrollDirectionForVelocity:(CGPoint)scrollVelocity { ASScrollDirection direction = ASScrollDirectionNone; @@ -544,7 +550,7 @@ static BOOL _isInterceptedSelector(SEL sel) direction |= ASScrollDirectionUp; } } - + return direction; } @@ -623,7 +629,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { [self handleBatchFetchScrollingToOffset:*targetContentOffset]; - + if ([_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) { [_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset]; } @@ -643,11 +649,11 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)handleBatchFetchScrollingToOffset:(CGPoint)targetOffset { ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist"); - + if (![self shouldBatchFetch]) { return; } - + if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollDirection], self.bounds, self.contentSize, targetOffset, _leadingScreensForBatching)) { [_batchContext beginBatchFetching]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @@ -695,7 +701,7 @@ static BOOL _isInterceptedSelector(SEL sel) } constrainedSize = ASSizeRangeMake(CGSizeZero, maxSize); } - + UIEdgeInsets sectionInset = UIEdgeInsetsZero; if (_collectionViewLayoutImplementsInsetSection) { sectionInset = [(UICollectionViewFlowLayout *)self.collectionViewLayout sectionInset]; @@ -704,7 +710,7 @@ static BOOL _isInterceptedSelector(SEL sel) if (_asyncDelegateImplementsInsetSection) { sectionInset = [(id)_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section]; } - + if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) { constrainedSize.min.width = MAX(0, constrainedSize.min.width - sectionInset.left - sectionInset.right); //ignore insets for FLT_MAX so FLT_MAX can be compared against @@ -718,7 +724,7 @@ static BOOL _isInterceptedSelector(SEL sel) constrainedSize.max.height = MAX(0, constrainedSize.max.height - sectionInset.top - sectionInset.bottom); } } - + return constrainedSize; } @@ -738,7 +744,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)dataControllerLockDataSource { ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has already been locked"); - + self.asyncDataSourceLocked = YES; if ([_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSource:)]) { [_asyncDataSource collectionViewLockDataSource:self]; @@ -748,7 +754,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)dataControllerUnlockDataSource { ASDisplayNodeAssert(self.asyncDataSourceLocked, @"The data source has already been unlocked"); - + self.asyncDataSourceLocked = NO; if ([_asyncDataSource respondsToSelector:@selector(collectionViewUnlockDataSource:)]) { [_asyncDataSource collectionViewUnlockDataSource:self]; @@ -817,7 +823,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)rangeController:(ASRangeController *)rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion { ASDisplayNodeAssertMainThread(); - + if (!self.asyncDataSource || _superIsPendingDataLoad) { if (completion) { completion(NO); @@ -832,7 +838,7 @@ static BOOL _isInterceptedSelector(SEL sel) } } completion:completion]; }); - + [_batchUpdateBlocks removeAllObjects]; _performingBatchUpdates = NO; } @@ -840,11 +846,11 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - + if (!self.asyncDataSource || _superIsPendingDataLoad) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } - + if (_performingBatchUpdates) { [_batchUpdateBlocks addObject:^{ [super insertItemsAtIndexPaths:indexPaths]; @@ -859,11 +865,11 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - + if (!self.asyncDataSource || _superIsPendingDataLoad) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } - + if (_performingBatchUpdates) { [_batchUpdateBlocks addObject:^{ [super deleteItemsAtIndexPaths:indexPaths]; @@ -878,11 +884,11 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - + if (!self.asyncDataSource || _superIsPendingDataLoad) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } - + if (_performingBatchUpdates) { [_batchUpdateBlocks addObject:^{ [super insertSections:indexSet]; @@ -897,11 +903,11 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - + if (!self.asyncDataSource || _superIsPendingDataLoad) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } - + if (_performingBatchUpdates) { [_batchUpdateBlocks addObject:^{ [super deleteSections:indexSet]; @@ -918,11 +924,11 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged { ASDisplayNodeAssertMainThread(); - + if (!sizeChanged || _queuedNodeSizeUpdate) { return; } - + _queuedNodeSizeUpdate = YES; [self performSelector:@selector(requeryNodeSizes) withObject:nil @@ -934,7 +940,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)requeryNodeSizes { _queuedNodeSizeUpdate = NO; - + [super performBatchUpdates:^{} completion:nil]; } @@ -944,7 +950,7 @@ static BOOL _isInterceptedSelector(SEL sel) { for (NSArray *section in [_dataController completedNodes]) { for (ASDisplayNode *node in section) { - [node recursivelyClearContents]; + [node exitInterfaceState:ASInterfaceStateDisplay]; } } } @@ -953,7 +959,7 @@ static BOOL _isInterceptedSelector(SEL sel) { for (NSArray *section in [_dataController completedNodes]) { for (ASDisplayNode *node in section) { - [node recursivelyClearFetchedData]; + [node exitInterfaceState:ASInterfaceStateFetchData]; } } } diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index b0bb3b0e..108dbdd3 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -37,35 +37,8 @@ @interface ASDisplayNode (Subclassing) - -/** @name View Configuration */ - - -/** - * @return The view class to use when creating a new display node instance. Defaults to _ASDisplayView. - */ -+ (Class)viewClass; - - /** @name Properties */ - -/** - * @abstract The scale factor to apply to the rendering. - * - * @discussion Use setNeedsDisplayAtScale: to set a value and then after display, the display node will set the layer's - * contentsScale. This is to prevent jumps when re-rasterizing at a different contentsScale. - * Read this property if you need to know the future contentsScale of your layer, eg in drawParameters. - * - * @see setNeedsDisplayAtScale: - */ -@property (nonatomic, assign, readonly) CGFloat contentsScaleForDisplay; - -/** - * @abstract Whether the view or layer of this display node is currently in a window - */ -@property (nonatomic, readonly, assign, getter=isInHierarchy) BOOL inHierarchy; - /** * @abstract Return the calculated layout. * @@ -190,10 +163,9 @@ * * @note Called on the display queue and/or main queue (MUST BE THREAD SAFE) */ -+ (void)drawRect:(CGRect)bounds - withParameters:(id)parameters - isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock - isRasterizing:(BOOL)isRasterizing; ++ (void)drawRect:(CGRect)bounds withParameters:(id)parameters + isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock + isRasterizing:(BOOL)isRasterizing; /** * @summary Delegate override to provide new layer contents as a UIImage. @@ -236,6 +208,33 @@ */ - (void)displayDidFinish ASDISPLAYNODE_REQUIRES_SUPER; +/** @name Observing node-related changes */ + +/** + * @abstract Called whenever any bit in the ASInterfaceState bitfield is changed. + * + * @discussion Subclasses may use this to monitor when they become visible, should free cached data, and much more. + * @see ASInterfaceState + */ +- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState; + +- (void)visibilityDidChange:(BOOL)isVisible; + +/** + * Called just before the view is added to a window. + */ +- (void)willEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * Called after the view is removed from the window. + */ +- (void)didExitHierarchy ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Whether the view or layer of this display node is currently in a window + */ +@property (nonatomic, readonly, assign, getter=isInHierarchy) BOOL inHierarchy; + /** * @abstract Indicates that the node should fetch any external data, such as images. * @@ -245,6 +244,23 @@ */ - (void)fetchData ASDISPLAYNODE_REQUIRES_SUPER; +/** + * Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node. + * + * @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearFetchedData or + * selectively clear fetched data. + */ +- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers + * on the current node. + * + * @discussion Called by -recursivelyClearContents. Base class implements self.contents = nil, clearing any backing + * store, for asynchronous regeneration when needed. + */ +- (void)clearContents ASDISPLAYNODE_REQUIRES_SUPER; + /** * @abstract Indicates that the receiver is about to display its subnodes. This method is not called if there are no * subnodes present. @@ -267,7 +283,6 @@ */ - (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode ASDISPLAYNODE_REQUIRES_SUPER; - /** * @abstract Marks the receiver's bounds as needing to be redrawn, with a scale value. * @@ -295,6 +310,17 @@ */ - (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale; +/** + * @abstract The scale factor to apply to the rendering. + * + * @discussion Use setNeedsDisplayAtScale: to set a value and then after display, the display node will set the layer's + * contentsScale. This is to prevent jumps when re-rasterizing at a different contentsScale. + * Read this property if you need to know the future contentsScale of your layer, eg in drawParameters. + * + * @see setNeedsDisplayAtScale: + */ +@property (nonatomic, assign, readonly) CGFloat contentsScaleForDisplay; + /** @name Touch handling */ @@ -361,38 +387,6 @@ */ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; - -/** @name Observing node-related changes */ - - -/** - * Called just before the view is added to a window. - */ -- (void)willEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER; - -/** - * Called after the view is removed from the window. - */ -- (void)didExitHierarchy ASDISPLAYNODE_REQUIRES_SUPER; - -/** - * Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers - * on the current node. - * - * @discussion Called by -recursivelyClearContents. Base class implements self.contents = nil, clearing any backing - * store, for asynchronous regeneration when needed. - */ -- (void)clearContents ASDISPLAYNODE_REQUIRES_SUPER; - -/** - * Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node. - * - * @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearFetchedData or - * selectively clear fetched data. - */ -- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER; - - /** @name Placeholders */ /** @@ -412,6 +406,7 @@ */ - (UIImage *)placeholderImage; + /** @name Description */ /** diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 2d92c188..5fdcb4f7 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1722,6 +1722,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) [self clearFetchedData]; } +- (void)visibilityDidChange:(BOOL)isVisible +{ +} + /** * We currently only set interface state on nodes in table/collection views. For other nodes, if they are * in the hierarchy we enable all ASInterfaceState types with `ASInterfaceStateInHierarchy`, otherwise `None`. @@ -1776,11 +1780,17 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) // Entered or exited data loading state. if ((newState & ASInterfaceStateVisible) != (oldState & ASInterfaceStateVisible)) { if (newState & ASInterfaceStateVisible) { - // Consider providing a -didBecomeVisible. + [self visibilityDidChange:YES]; } else { - // Consider providing a -didBecomeInvisible. + [self visibilityDidChange:NO]; } } + + [self interfaceStateDidChange:newState fromState:oldState]; +} + +- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState +{ } - (void)enterInterfaceState:(ASInterfaceState)interfaceState diff --git a/AsyncDisplayKit/ASViewController.h b/AsyncDisplayKit/ASViewController.h index 281a5d0e..7ef1fdf7 100644 --- a/AsyncDisplayKit/ASViewController.h +++ b/AsyncDisplayKit/ASViewController.h @@ -11,14 +11,23 @@ @interface ASViewController : UIViewController +- (instancetype)initWithNode:(ASDisplayNode *)node NS_DESIGNATED_INITIALIZER; + @property (nonatomic, strong, readonly) ASDisplayNode *node; +/** + * @abstract Passthrough property to the the .interfaceState of the node. + * @return The current ASInterfaceState of the node, indicating whether it is visible and other situational properties. + * @see ASInterfaceState + */ +@property (nonatomic, readonly) ASInterfaceState interfaceState; + + // AsyncDisplayKit 2.0 BETA: This property is still being tested, but it allows // blocking as a view controller becomes visible to ensure no placeholders flash onscreen. // Refer to examples/SynchronousConcurrency, AsyncViewController.m @property (nonatomic, assign) BOOL neverShowPlaceholders; -- (instancetype)initWithNode:(ASDisplayNode *)node; /** * The constrained size used to measure the backing node. diff --git a/AsyncDisplayKit/ASViewController.m b/AsyncDisplayKit/ASViewController.m index ab2c6741..3b22a929 100644 --- a/AsyncDisplayKit/ASViewController.m +++ b/AsyncDisplayKit/ASViewController.m @@ -65,4 +65,9 @@ return ASSizeRangeMake(viewSize, viewSize); } +- (ASInterfaceState)interfaceState +{ + return _node.interfaceState; +} + @end diff --git a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h b/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h index 701be894..978032d9 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h +++ b/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h @@ -57,6 +57,9 @@ typedef NS_OPTIONS(NSUInteger, ASHierarchyState) ASHierarchyState _hierarchyState; } +// The view class to use when creating a new display node instance. Defaults to _ASDisplayView. ++ (Class)viewClass; + // These methods are recursive, and either union or remove the provided interfaceState to all sub-elements. - (void)enterInterfaceState:(ASInterfaceState)interfaceState; - (void)exitInterfaceState:(ASInterfaceState)interfaceState; diff --git a/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj b/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj index 57ec7f62..88ca8858 100644 --- a/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj @@ -130,7 +130,6 @@ AC3C4A5B1A11F47200143C57 /* Frameworks */, AC3C4A5C1A11F47200143C57 /* Resources */, A6902C454C7661D0D277AC62 /* Copy Pods Resources */, - EC37EEC9933F5786936BFE7C /* Embed Pods Frameworks */, ); buildRules = ( ); @@ -201,21 +200,6 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; showEnvVarsInLog = 0; }; - EC37EEC9933F5786936BFE7C /* 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; - }; F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647;