Merge branch 'master' into update-objc

Conflicts:
	AsyncDisplayKit/ASCollectionView.h
	AsyncDisplayKit/ASTableView.h
	AsyncDisplayKit/Details/ASLayoutController.h
	AsyncDisplayKit/Details/ASRangeController.h
This commit is contained in:
Adlai Holler
2015-11-24 16:40:03 -08:00
12 changed files with 200 additions and 244 deletions

View File

@@ -433,10 +433,4 @@ NS_ASSUME_NONNULL_BEGIN
@end @end
@interface ASCollectionView (Deprecated)
@property (nonatomic, assign) ASRangeTuningParameters rangeTuningParameters ASDISPLAYNODE_DEPRECATED;
@end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -130,7 +130,7 @@ static BOOL _isInterceptedSelector(SEL sel)
#pragma mark - #pragma mark -
#pragma mark ASCollectionView. #pragma mark ASCollectionView.
@interface ASCollectionView () <ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate> { @interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate> {
_ASCollectionViewProxy *_proxyDataSource; _ASCollectionViewProxy *_proxyDataSource;
_ASCollectionViewProxy *_proxyDelegate; _ASCollectionViewProxy *_proxyDelegate;
@@ -199,6 +199,7 @@ static BOOL _isInterceptedSelector(SEL sel)
_layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self]; _layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self];
_rangeController = [[ASRangeController alloc] init]; _rangeController = [[ASRangeController alloc] init];
_rangeController.dataSource = self;
_rangeController.delegate = self; _rangeController.delegate = self;
_rangeController.layoutController = _layoutController; _rangeController.layoutController = _layoutController;
@@ -767,14 +768,35 @@ static BOOL _isInterceptedSelector(SEL sel)
return [_layoutInspector collectionView:self numberOfSectionsForSupplementaryNodeOfKind:kind]; return [_layoutInspector collectionView:self numberOfSectionsForSupplementaryNodeOfKind:kind];
} }
#pragma mark - ASRangeControllerDelegate. #pragma mark - ASRangeControllerDataSource
- (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController { - (NSArray *)visibleNodeIndexPathsForRangeController:(ASRangeController *)rangeController
{
ASDisplayNodeAssertMainThread();
return [self indexPathsForVisibleItems];
}
- (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController
{
ASDisplayNodeAssertMainThread();
return self.bounds.size;
}
- (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths
{
return [_dataController nodesAtIndexPaths:indexPaths];
}
#pragma mark - ASRangeControllerDelegate
- (void)didBeginUpdatesInRangeController:(ASRangeController *)rangeController
{
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
_performingBatchUpdates = YES; _performingBatchUpdates = YES;
} }
- (void)rangeController:(ASRangeController *)rangeController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion { - (void)rangeController:(ASRangeController *)rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
{
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (!self.asyncDataSource || _superIsPendingDataLoad) { if (!self.asyncDataSource || _superIsPendingDataLoad) {
@@ -796,23 +818,6 @@ static BOOL _isInterceptedSelector(SEL sel)
_performingBatchUpdates = NO; _performingBatchUpdates = NO;
} }
- (NSArray *)rangeControllerVisibleNodeIndexPaths:(ASRangeController *)rangeController
{
ASDisplayNodeAssertMainThread();
return [self indexPathsForVisibleItems];
}
- (CGSize)rangeControllerViewportSize:(ASRangeController *)rangeController
{
ASDisplayNodeAssertMainThread();
return self.bounds.size;
}
- (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths
{
return [_dataController nodesAtIndexPaths:indexPaths];
}
- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions - (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();

View File

@@ -1644,7 +1644,8 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
- (void)clearContents - (void)clearContents
{ {
self.layer.contents = nil; // No-op if these haven't been created yet, as that guarantees they don't have contents that needs to be released.
_layer.contents = nil;
_placeholderLayer.contents = nil; _placeholderLayer.contents = nil;
_placeholderImage = nil; _placeholderImage = nil;
} }

View File

@@ -19,6 +19,7 @@
#import "ASDisplayNode+Subclasses.h" #import "ASDisplayNode+Subclasses.h"
#import "ASLog.h" #import "ASLog.h"
#import "ASPhotosFrameworkImageRequest.h" #import "ASPhotosFrameworkImageRequest.h"
#import "ASEqualityHelpers.h"
#if !AS_IOS8_SDK_OR_LATER #if !AS_IOS8_SDK_OR_LATER
#error ASMultiplexImageNode can be used on iOS 7, but must be linked against the iOS 8 SDK. #error ASMultiplexImageNode can be used on iOS 7, but must be linked against the iOS 8 SDK.
@@ -176,17 +177,13 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
} }
#pragma mark - ASDisplayNode Overrides #pragma mark - ASDisplayNode Overrides
- (void)clearContents - (void)clearContents
{ {
[super clearContents]; // This actually clears the contents, so we need to do this first for our displayedImageIdentifier to be meaningful. [super clearContents]; // This actually clears the contents, so we need to do this first for our displayedImageIdentifier to be meaningful.
[self _setDisplayedImageIdentifier:nil withImage:nil]; [self _setDisplayedImageIdentifier:nil withImage:nil];
[_phImageRequestOperation cancel]; // NOTE: We intentionally do not cancel image downloads until `clearFetchedData`.
if (_downloadIdentifier) {
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
_downloadIdentifier = nil;
}
} }
- (void)clearFetchedData - (void)clearFetchedData
@@ -196,12 +193,9 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
if ([self _shouldClearFetchedImageData]) { if ([self _shouldClearFetchedImageData]) {
[_phImageRequestOperation cancel]; [_phImageRequestOperation cancel];
if (_downloadIdentifier) { [self _setDownloadIdentifier:nil];
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
_downloadIdentifier = nil;
}
// setting this to nil makes the node fetch images the next time its display starts // setting this to nil makes the node fetch images the next time its display starts
_loadedImageIdentifier = nil; _loadedImageIdentifier = nil;
self.image = nil; self.image = nil;
@@ -222,7 +216,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
// We may now be displaying the loaded identifier, if they're different. // We may now be displaying the loaded identifier, if they're different.
UIImage *displayedImage = self.image; UIImage *displayedImage = self.image;
if (displayedImage) { if (displayedImage) {
if (![_displayedImageIdentifier isEqual:_loadedImageIdentifier]) if (!ASObjectIsEqual(_displayedImageIdentifier, _loadedImageIdentifier))
[self _setDisplayedImageIdentifier:_loadedImageIdentifier withImage:displayedImage]; [self _setDisplayedImageIdentifier:_loadedImageIdentifier withImage:displayedImage];
// Delegateify // Delegateify
@@ -286,7 +280,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
{ {
OSSpinLockLock(&_imageIdentifiersLock); OSSpinLockLock(&_imageIdentifiersLock);
if ([_imageIdentifiers isEqual:imageIdentifiers]) { if (ASObjectIsEqual(_imageIdentifiers, imageIdentifiers)) {
OSSpinLockUnlock(&_imageIdentifiersLock); OSSpinLockUnlock(&_imageIdentifiersLock);
return; return;
} }
@@ -308,7 +302,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
#pragma mark - Core Internal #pragma mark - Core Internal
- (void)_setDisplayedImageIdentifier:(id)displayedImageIdentifier withImage:(UIImage *)image - (void)_setDisplayedImageIdentifier:(id)displayedImageIdentifier withImage:(UIImage *)image
{ {
if (_displayedImageIdentifier == displayedImageIdentifier) if (ASObjectIsEqual(displayedImageIdentifier, _displayedImageIdentifier))
return; return;
_displayedImageIdentifier = [displayedImageIdentifier copy]; _displayedImageIdentifier = [displayedImageIdentifier copy];
@@ -332,7 +326,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
- (void)_setDownloadIdentifier:(id)downloadIdentifier - (void)_setDownloadIdentifier:(id)downloadIdentifier
{ {
if (_downloadIdentifier == downloadIdentifier) if (ASObjectIsEqual(downloadIdentifier, _downloadIdentifier))
return; return;
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier]; [_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
@@ -391,7 +385,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
// If we've already loaded the best identifier, we've got nothing else to do. // If we've already loaded the best identifier, we've got nothing else to do.
id bestImageIdentifier = _imageIdentifiers.firstObject; id bestImageIdentifier = _imageIdentifiers.firstObject;
if (!bestImageIdentifier || [_loadedImageIdentifier isEqual:bestImageIdentifier]) { if (!bestImageIdentifier || ASObjectIsEqual(_loadedImageIdentifier, bestImageIdentifier)) {
OSSpinLockUnlock(&_imageIdentifiersLock); OSSpinLockUnlock(&_imageIdentifiersLock);
return nil; return nil;
} }
@@ -439,7 +433,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
return; return;
// Only nil out the loading identifier if the loading identifier hasn't changed. // Only nil out the loading identifier if the loading identifier hasn't changed.
if ([strongSelf.loadingImageIdentifier isEqual:nextImageIdentifier]) { if (ASObjectIsEqual(strongSelf.loadingImageIdentifier, nextImageIdentifier)) {
strongSelf.loadingImageIdentifier = nil; strongSelf.loadingImageIdentifier = nil;
} }
[strongSelf _finishedLoadingImage:image forIdentifier:imageIdentifier error:error]; [strongSelf _finishedLoadingImage:image forIdentifier:imageIdentifier error:error];
@@ -496,7 +490,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
} }
// If the next image to load has changed, bail. // If the next image to load has changed, bail.
if (![[strongSelf _nextImageIdentifierToDownload] isEqual:nextImageIdentifier]) { if (!ASObjectIsEqual([strongSelf _nextImageIdentifierToDownload], nextImageIdentifier)) {
finishedLoadingBlock(nil, nil, [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodeBestImageIdentifierChanged userInfo:nil]); finishedLoadingBlock(nil, nil, [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodeBestImageIdentifierChanged userInfo:nil]);
return; return;
} }

View File

@@ -361,10 +361,4 @@ NS_ASSUME_NONNULL_BEGIN
@end @end
@interface ASTableView (Deprecated)
@property (nonatomic, assign) ASRangeTuningParameters rangeTuningParameters ASDISPLAYNODE_DEPRECATED;
@end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -151,7 +151,7 @@ static BOOL _isInterceptedSelector(SEL sel)
#pragma mark - #pragma mark -
#pragma mark ASTableView #pragma mark ASTableView
@interface ASTableView () <ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate> { @interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate> {
_ASTableViewProxy *_proxyDataSource; _ASTableViewProxy *_proxyDataSource;
_ASTableViewProxy *_proxyDelegate; _ASTableViewProxy *_proxyDelegate;
@@ -193,6 +193,7 @@ static BOOL _isInterceptedSelector(SEL sel)
_rangeController = [[ASRangeController alloc] init]; _rangeController = [[ASRangeController alloc] init];
_rangeController.layoutController = _layoutController; _rangeController.layoutController = _layoutController;
_rangeController.dataSource = self;
_rangeController.delegate = self; _rangeController.delegate = self;
_dataController = [[dataControllerClass alloc] initWithAsyncDataFetching:asyncDataFetching]; _dataController = [[dataControllerClass alloc] initWithAsyncDataFetching:asyncDataFetching];
@@ -639,11 +640,72 @@ static BOOL _isInterceptedSelector(SEL sel)
} }
} }
#pragma mark - ASRangeControllerDataSource
#pragma mark - - (NSArray *)visibleNodeIndexPathsForRangeController:(ASRangeController *)rangeController
#pragma mark ASRangeControllerDelegate {
ASDisplayNodeAssertMainThread();
NSArray *visibleIndexPaths = self.indexPathsForVisibleRows;
if (_pendingVisibleIndexPath) {
NSMutableSet *indexPaths = [NSMutableSet setWithArray:self.indexPathsForVisibleRows];
BOOL (^isAfter)(NSIndexPath *, NSIndexPath *) = ^BOOL(NSIndexPath *indexPath, NSIndexPath *anchor) {
if (!anchor || !indexPath) {
return NO;
}
if (indexPath.section == anchor.section) {
return (indexPath.row == anchor.row+1); // assumes that indexes are valid
} else if (indexPath.section > anchor.section && indexPath.row == 0) {
if (anchor.row != [_dataController numberOfRowsInSection:anchor.section] -1) {
return NO; // anchor is not at the end of the section
}
NSInteger nextSection = anchor.section+1;
while([_dataController numberOfRowsInSection:nextSection] == 0) {
++nextSection;
}
return indexPath.section == nextSection;
}
return NO;
};
BOOL (^isBefore)(NSIndexPath *, NSIndexPath *) = ^BOOL(NSIndexPath *indexPath, NSIndexPath *anchor) {
return isAfter(anchor, indexPath);
};
if ([indexPaths containsObject:_pendingVisibleIndexPath]) {
_pendingVisibleIndexPath = nil; // once it has shown up in visibleIndexPaths, we can stop tracking it
} else if (!isBefore(_pendingVisibleIndexPath, visibleIndexPaths.firstObject) &&
!isAfter(_pendingVisibleIndexPath, visibleIndexPaths.lastObject)) {
_pendingVisibleIndexPath = nil; // not contiguous, ignore.
} else {
[indexPaths addObject:_pendingVisibleIndexPath];
visibleIndexPaths = [indexPaths.allObjects sortedArrayUsingSelector:@selector(compare:)];
}
}
return visibleIndexPaths;
}
- (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController - (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths
{
return [_dataController nodesAtIndexPaths:indexPaths];
}
- (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController
{
ASDisplayNodeAssertMainThread();
return self.bounds.size;
}
#pragma mark - ASRangeControllerDelegate
- (void)didBeginUpdatesInRangeController:(ASRangeController *)rangeController
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
LOG(@"--- UITableView beginUpdates"); LOG(@"--- UITableView beginUpdates");
@@ -659,7 +721,7 @@ static BOOL _isInterceptedSelector(SEL sel)
} }
} }
- (void)rangeController:(ASRangeController *)rangeController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion - (void)rangeController:(ASRangeController *)rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
LOG(@"--- UITableView endUpdates"); LOG(@"--- UITableView endUpdates");
@@ -684,67 +746,6 @@ static BOOL _isInterceptedSelector(SEL sel)
} }
} }
- (NSArray *)rangeControllerVisibleNodeIndexPaths:(ASRangeController *)rangeController
{
ASDisplayNodeAssertMainThread();
NSArray *visibleIndexPaths = self.indexPathsForVisibleRows;
if ( _pendingVisibleIndexPath ) {
NSMutableSet *indexPaths = [NSMutableSet setWithArray:self.indexPathsForVisibleRows];
BOOL (^isAfter)(NSIndexPath *, NSIndexPath *) = ^BOOL(NSIndexPath *indexPath, NSIndexPath *anchor) {
if (!anchor || !indexPath) {
return NO;
}
if (indexPath.section == anchor.section) {
return (indexPath.row == anchor.row+1); // assumes that indexes are valid
} else if (indexPath.section > anchor.section && indexPath.row == 0) {
if (anchor.row != [_dataController numberOfRowsInSection:anchor.section] -1) {
return NO; // anchor is not at the end of the section
}
NSInteger nextSection = anchor.section+1;
while([_dataController numberOfRowsInSection:nextSection] == 0) {
++nextSection;
}
return indexPath.section == nextSection;
}
return NO;
};
BOOL (^isBefore)(NSIndexPath *, NSIndexPath *) = ^BOOL(NSIndexPath *indexPath, NSIndexPath *anchor) {
return isAfter(anchor, indexPath);
};
if ( [indexPaths containsObject:_pendingVisibleIndexPath]) {
_pendingVisibleIndexPath = nil; // once it has shown up in visibleIndexPaths, we can stop tracking it
} else if (!isBefore(_pendingVisibleIndexPath, visibleIndexPaths.firstObject) &&
!isAfter(_pendingVisibleIndexPath, visibleIndexPaths.lastObject)) {
_pendingVisibleIndexPath = nil; // not contiguous, ignore.
} else {
[indexPaths addObject:_pendingVisibleIndexPath];
visibleIndexPaths = [indexPaths.allObjects sortedArrayUsingSelector:@selector(compare:)];
}
}
return visibleIndexPaths;
}
- (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths
{
return [_dataController nodesAtIndexPaths:indexPaths];
}
- (CGSize)rangeControllerViewportSize:(ASRangeController *)rangeController
{
ASDisplayNodeAssertMainThread();
return self.bounds.size;
}
- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions - (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();

View File

@@ -17,8 +17,6 @@ NS_ASSUME_NONNULL_BEGIN
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType; - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
@property (nonatomic, assign) ASRangeTuningParameters tuningParameters ASDISPLAYNODE_DEPRECATED;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -52,33 +52,7 @@
_tuningParameters[rangeType] = tuningParameters; _tuningParameters[rangeType] = tuningParameters;
} }
// Support for the deprecated tuningParameters property #pragma mark - Abstract Index Path Range Support
- (ASRangeTuningParameters)tuningParameters
{
return [self tuningParametersForRangeType:ASLayoutRangeTypeRender];
}
// Support for the deprecated tuningParameters property
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters
{
[self setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender];
}
#pragma mark - Index Path Range Support
// Support for deprecated method
- (BOOL)shouldUpdateForVisibleIndexPath:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize
{
return [self shouldUpdateForVisibleIndexPaths:indexPaths viewportSize:viewportSize rangeType:ASLayoutRangeTypeRender];
}
// Support for the deprecated method
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize
{
return [self indexPathsForScrolling:scrollDirection viewportSize:viewportSize rangeType:ASLayoutRangeTypeRender];
}
#pragma mark - Abstract
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType - (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType
{ {

View File

@@ -14,8 +14,6 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@class ASCellNode;
typedef struct { typedef struct {
CGFloat leadingBufferScreenfuls; CGFloat leadingBufferScreenfuls;
CGFloat trailingBufferScreenfuls; CGFloat trailingBufferScreenfuls;
@@ -34,12 +32,6 @@ typedef struct {
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType; - (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType;
@property (nonatomic, assign) ASRangeTuningParameters tuningParameters ASDISPLAYNODE_DEPRECATED;
- (BOOL)shouldUpdateForVisibleIndexPath:(NSIndexPath *)indexPath viewportSize:(CGSize)viewportSize ASDISPLAYNODE_DEPRECATED;
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize ASDISPLAYNODE_DEPRECATED;
@optional @optional
- (void)insertNodesAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withSizes:(NSArray<NSValue *> *)nodeSizes; - (void)insertNodesAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withSizes:(NSArray<NSValue *> *)nodeSizes;

View File

@@ -15,6 +15,7 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@protocol ASRangeControllerDataSource;
@protocol ASRangeControllerDelegate; @protocol ASRangeControllerDelegate;
/** /**
@@ -47,49 +48,46 @@ NS_ASSUME_NONNULL_BEGIN
- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node; - (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node;
/** /**
* Delegate and ultimate data source. Must not be nil. * An object that describes the layout behavior of the ranged component (table view, collection view, etc.)
*
* Used primarily for providing the current range of index paths and identifying when the
* range controller should invalidate its range.
*/
@property (nonatomic, strong) id<ASLayoutController> layoutController;
/**
* The underlying data source for the range controller
*/
@property (nonatomic, weak) id<ASRangeControllerDataSource> dataSource;
/**
* Delegate for handling range controller events. Must not be nil.
*/ */
@property (nonatomic, weak) id<ASRangeControllerDelegate> delegate; @property (nonatomic, weak) id<ASRangeControllerDelegate> delegate;
@property (nonatomic, strong) id<ASLayoutController> layoutController;
@end @end
/** /**
* <ASRangeController> delegate. For example, <ASTableView>. * Data source for ASRangeController.
*
* Allows the range controller to perform external queries on the range.
* Ex. range nodes, visible index paths, and viewport size.
*/ */
@protocol ASRangeControllerDelegate <NSObject> @protocol ASRangeControllerDataSource <NSObject>
/** /**
* @param rangeController Sender. * @param rangeController Sender.
* *
* @returns an array of index paths corresponding to the nodes currently visible onscreen (i.e., the visible range). * @returns an array of index paths corresponding to the nodes currently visible onscreen (i.e., the visible range).
*/ */
- (NSArray<NSIndexPath *> *)rangeControllerVisibleNodeIndexPaths:(ASRangeController *)rangeController; - (NSArray<NSIndexPath *> *)visibleNodeIndexPathsForRangeController:(ASRangeController *)rangeController;
/** /**
* @param rangeController Sender. * @param rangeController Sender.
* *
* @returns the receiver's viewport size (i.e., the screen space occupied by the visible range). * @returns the receiver's viewport size (i.e., the screen space occupied by the visible range).
*/ */
- (CGSize)rangeControllerViewportSize:(ASRangeController *)rangeController; - (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController;
/**
* Begin updates.
*
* @param rangeController Sender.
*/
- (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController;
/**
* End updates.
*
* @param rangeController Sender.
* @param animated NO if all animations are disabled. YES otherwise.
* @param completion Completion block.
*/
- (void)rangeController:(ASRangeController * )rangeController endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL))completion;
/** /**
* Fetch nodes at specific index paths. * Fetch nodes at specific index paths.
@@ -98,7 +96,30 @@ NS_ASSUME_NONNULL_BEGIN
* *
* @param indexPaths Index paths. * @param indexPaths Index paths.
*/ */
- (NSArray<ASCellNode *> *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths; - (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths;
@end
/**
* Delegate for ASRangeController.
*/
@protocol ASRangeControllerDelegate <NSObject>
/**
* Begin updates.
*
* @param rangeController Sender.
*/
- (void)didBeginUpdatesInRangeController:(ASRangeController *)rangeController;
/**
* End updates.
*
* @param rangeController Sender.
* @param animated NO if all animations are disabled. YES otherwise.
* @param completion Completion block.
*/
- (void)rangeController:(ASRangeController * )rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion;
/** /**
* Called for nodes insertion. * Called for nodes insertion.

View File

@@ -31,22 +31,35 @@
@implementation ASRangeController @implementation ASRangeController
- (instancetype)init { - (instancetype)init {
if (self = [super init]) { self = [super init];
if (self != nil) {
_rangeIsValid = YES; _rangeIsValid = YES;
_rangeTypeIndexPaths = [[NSMutableDictionary alloc] init]; _rangeTypeIndexPaths = [NSMutableDictionary dictionary];
_rangeTypeHandlers = @{ _rangeTypeHandlers = @{
@(ASLayoutRangeTypeRender): [[ASRangeHandlerRender alloc] init], @(ASLayoutRangeTypeRender): [[ASRangeHandlerRender alloc] init],
@(ASLayoutRangeTypePreload): [[ASRangeHandlerPreload alloc] init], @(ASLayoutRangeTypePreload): [[ASRangeHandlerPreload alloc] init],
}; };
} }
return self; return self;
} }
#pragma mark - Cell node view handling
#pragma mark - View manipulation - (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node
{
if (node.view.superview == contentView) {
// this content view is already correctly configured
return;
}
// clean the content view
for (UIView *view in contentView.subviews) {
[view removeFromSuperview];
}
[self moveCellNode:node toView:contentView];
}
- (void)moveCellNode:(ASCellNode *)node toView:(UIView *)view - (void)moveCellNode:(ASCellNode *)node toView:(UIView *)view
{ {
@@ -62,8 +75,7 @@
[view addSubview:node.view]; [view addSubview:node.view];
} }
#pragma mark - Core visible node range managment API
#pragma mark - API
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection - (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
{ {
@@ -76,27 +88,27 @@
// coalesce these events -- handling them multiple times per runloop is noisy and expensive // coalesce these events -- handling them multiple times per runloop is noisy and expensive
_queuedRangeUpdate = YES; _queuedRangeUpdate = YES;
[self performSelector:@selector(updateVisibleNodeIndexPaths) [self performSelector:@selector(_updateVisibleNodeIndexPaths)
withObject:nil withObject:nil
afterDelay:0 afterDelay:0
inModes:@[ NSRunLoopCommonModes ]]; inModes:@[ NSRunLoopCommonModes ]];
} }
- (void)updateVisibleNodeIndexPaths - (void)_updateVisibleNodeIndexPaths
{ {
if (!_queuedRangeUpdate) { if (!_queuedRangeUpdate) {
return; return;
} }
NSArray *visibleNodePaths = [_delegate rangeControllerVisibleNodeIndexPaths:self]; NSArray *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController:self];
if ( visibleNodePaths.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)... if (visibleNodePaths.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)...
_queuedRangeUpdate = NO; _queuedRangeUpdate = NO;
return ; // don't do anything for this update, but leave _rangeIsValid to make sure we update it later return ; // don't do anything for this update, but leave _rangeIsValid to make sure we update it later
} }
NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths]; NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths];
CGSize viewportSize = [_delegate rangeControllerViewportSize:self]; CGSize viewportSize = [_dataSource viewportSizeForRangeController:self];
// the layout controller needs to know what the current visible indices are to calculate range offsets // the layout controller needs to know what the current visible indices are to calculate range offsets
if ([_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)]) { if ([_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)]) {
@@ -108,28 +120,30 @@
id rangeKey = @(rangeType); id rangeKey = @(rangeType);
// this delegate decide what happens when a node is added or removed from a range // this delegate decide what happens when a node is added or removed from a range
id<ASRangeHandler> rangeDelegate = _rangeTypeHandlers[rangeKey]; id<ASRangeHandler> rangeHandler = _rangeTypeHandlers[rangeKey];
if (!_rangeIsValid || [_layoutController shouldUpdateForVisibleIndexPaths:visibleNodePaths viewportSize:viewportSize rangeType:rangeType]) { if (!_rangeIsValid || [_layoutController shouldUpdateForVisibleIndexPaths:visibleNodePaths viewportSize:viewportSize rangeType:rangeType]) {
NSSet *indexPaths = [_layoutController indexPathsForScrolling:_scrollDirection viewportSize:viewportSize rangeType:rangeType]; NSSet *indexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
viewportSize:viewportSize
rangeType:rangeType];
// Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths // Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths
NSMutableSet *removedIndexPaths = _rangeIsValid ? [[_rangeTypeIndexPaths objectForKey:rangeKey] mutableCopy] : [NSMutableSet set]; NSMutableSet *removedIndexPaths = _rangeIsValid ? [_rangeTypeIndexPaths[rangeKey] mutableCopy] : [NSMutableSet set];
[removedIndexPaths minusSet:indexPaths]; [removedIndexPaths minusSet:indexPaths];
[removedIndexPaths minusSet:visibleNodePathsSet]; [removedIndexPaths minusSet:visibleNodePathsSet];
if (removedIndexPaths.count) { if (removedIndexPaths.count) {
NSArray *removedNodes = [_delegate rangeController:self nodesAtIndexPaths:[removedIndexPaths allObjects]]; NSArray *removedNodes = [_dataSource rangeController:self nodesAtIndexPaths:[removedIndexPaths allObjects]];
[removedNodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) { for (ASCellNode *node in removedNodes) {
// since this class usually manages large or infinite data sets, the working range // since this class usually manages large or infinite data sets, the working range
// directly bounds memory usage by requiring redrawing any content that falls outside the range. // directly bounds memory usage by requiring redrawing any content that falls outside the range.
[rangeDelegate node:node exitedRangeOfType:rangeType]; [rangeHandler node:node exitedRangeOfType:rangeType];
}]; }
} }
// Notify to add indexpaths that are not currently in _rangeTypeIndexPaths // Notify to add index paths that are not currently in _rangeTypeIndexPaths
NSMutableSet *addedIndexPaths = [indexPaths mutableCopy]; NSMutableSet *addedIndexPaths = [indexPaths mutableCopy];
[addedIndexPaths minusSet:[_rangeTypeIndexPaths objectForKey:rangeKey]]; [addedIndexPaths minusSet:_rangeTypeIndexPaths[rangeKey]];
// The preload range (for example) should include nodes that are visible // The preload range (for example) should include nodes that are visible
// TODO: remove this once we have removed the dependency on Core Animation's -display // TODO: remove this once we have removed the dependency on Core Animation's -display
@@ -138,14 +152,14 @@
} }
if (addedIndexPaths.count) { if (addedIndexPaths.count) {
NSArray *addedNodes = [_delegate rangeController:self nodesAtIndexPaths:[addedIndexPaths allObjects]]; NSArray *addedNodes = [_dataSource rangeController:self nodesAtIndexPaths:[addedIndexPaths allObjects]];
[addedNodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) { for (ASCellNode *node in addedNodes) {
[rangeDelegate node:node enteredRangeOfType:rangeType]; [rangeHandler node:node enteredRangeOfType:rangeType];
}]; }
} }
// set the range indexpaths so that we can remove/add on the next update pass // set the range indexpaths so that we can remove/add on the next update pass
[_rangeTypeIndexPaths setObject:indexPaths forKey:rangeKey]; _rangeTypeIndexPaths[rangeKey] = indexPaths;
} }
} }
@@ -158,33 +172,17 @@
return rangeType == ASLayoutRangeTypeRender; return rangeType == ASLayoutRangeTypeRender;
} }
- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node
{
if (node.view.superview == contentView) {
// this content view is already correctly configured
return;
}
// clean the content view
for (UIView *view in contentView.subviews) {
[view removeFromSuperview];
}
[self moveCellNode:node toView:contentView];
}
#pragma mark - ASDataControllerDelegete #pragma mark - ASDataControllerDelegete
- (void)dataControllerBeginUpdates:(ASDataController *)dataController { - (void)dataControllerBeginUpdates:(ASDataController *)dataController {
ASPerformBlockOnMainThread(^{ ASPerformBlockOnMainThread(^{
[_delegate rangeControllerBeginUpdates:self]; [_delegate didBeginUpdatesInRangeController:self];
}); });
} }
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion { - (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion {
ASPerformBlockOnMainThread(^{ ASPerformBlockOnMainThread(^{
[_delegate rangeController:self endUpdatesAnimated:animated completion:completion]; [_delegate rangeController:self didEndUpdatesAnimated:animated completion:completion];
}); });
} }

View File

@@ -204,7 +204,6 @@
3EEA4EE01BECC4A1008A7F35 /* Sources */, 3EEA4EE01BECC4A1008A7F35 /* Sources */,
3EEA4EE11BECC4A1008A7F35 /* Frameworks */, 3EEA4EE11BECC4A1008A7F35 /* Frameworks */,
3EEA4EE21BECC4A1008A7F35 /* Resources */, 3EEA4EE21BECC4A1008A7F35 /* Resources */,
F8CD1BCEB86661EF98DB1C0C /* Embed Pods Frameworks */,
21F2C1D9B53F9468EAF1653F /* Copy Pods Resources */, 21F2C1D9B53F9468EAF1653F /* Copy Pods Resources */,
); );
buildRules = ( buildRules = (
@@ -311,21 +310,6 @@
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"; 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; showEnvVarsInLog = 0;
}; };
F8CD1BCEB86661EF98DB1C0C /* 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;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */