mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
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:
@@ -433,10 +433,4 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionView (Deprecated)
|
||||
|
||||
@property (nonatomic, assign) ASRangeTuningParameters rangeTuningParameters ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -130,7 +130,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
#pragma mark -
|
||||
#pragma mark ASCollectionView.
|
||||
|
||||
@interface ASCollectionView () <ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate> {
|
||||
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate> {
|
||||
_ASCollectionViewProxy *_proxyDataSource;
|
||||
_ASCollectionViewProxy *_proxyDelegate;
|
||||
|
||||
@@ -199,6 +199,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
_layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self];
|
||||
|
||||
_rangeController = [[ASRangeController alloc] init];
|
||||
_rangeController.dataSource = self;
|
||||
_rangeController.delegate = self;
|
||||
_rangeController.layoutController = _layoutController;
|
||||
|
||||
@@ -767,14 +768,35 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
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();
|
||||
_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();
|
||||
|
||||
if (!self.asyncDataSource || _superIsPendingDataLoad) {
|
||||
@@ -796,23 +818,6 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
_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
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
@@ -1644,7 +1644,8 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
||||
|
||||
- (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;
|
||||
_placeholderImage = nil;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASLog.h"
|
||||
#import "ASPhotosFrameworkImageRequest.h"
|
||||
#import "ASEqualityHelpers.h"
|
||||
|
||||
#if !AS_IOS8_SDK_OR_LATER
|
||||
#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
|
||||
|
||||
- (void)clearContents
|
||||
{
|
||||
[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];
|
||||
|
||||
[_phImageRequestOperation cancel];
|
||||
|
||||
if (_downloadIdentifier) {
|
||||
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
|
||||
_downloadIdentifier = nil;
|
||||
}
|
||||
// NOTE: We intentionally do not cancel image downloads until `clearFetchedData`.
|
||||
}
|
||||
|
||||
- (void)clearFetchedData
|
||||
@@ -196,12 +193,9 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
if ([self _shouldClearFetchedImageData]) {
|
||||
|
||||
[_phImageRequestOperation cancel];
|
||||
|
||||
if (_downloadIdentifier) {
|
||||
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
|
||||
_downloadIdentifier = nil;
|
||||
}
|
||||
|
||||
|
||||
[self _setDownloadIdentifier:nil];
|
||||
|
||||
// setting this to nil makes the node fetch images the next time its display starts
|
||||
_loadedImageIdentifier = 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.
|
||||
UIImage *displayedImage = self.image;
|
||||
if (displayedImage) {
|
||||
if (![_displayedImageIdentifier isEqual:_loadedImageIdentifier])
|
||||
if (!ASObjectIsEqual(_displayedImageIdentifier, _loadedImageIdentifier))
|
||||
[self _setDisplayedImageIdentifier:_loadedImageIdentifier withImage:displayedImage];
|
||||
|
||||
// Delegateify
|
||||
@@ -286,7 +280,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
{
|
||||
OSSpinLockLock(&_imageIdentifiersLock);
|
||||
|
||||
if ([_imageIdentifiers isEqual:imageIdentifiers]) {
|
||||
if (ASObjectIsEqual(_imageIdentifiers, imageIdentifiers)) {
|
||||
OSSpinLockUnlock(&_imageIdentifiersLock);
|
||||
return;
|
||||
}
|
||||
@@ -308,7 +302,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
#pragma mark - Core Internal
|
||||
- (void)_setDisplayedImageIdentifier:(id)displayedImageIdentifier withImage:(UIImage *)image
|
||||
{
|
||||
if (_displayedImageIdentifier == displayedImageIdentifier)
|
||||
if (ASObjectIsEqual(displayedImageIdentifier, _displayedImageIdentifier))
|
||||
return;
|
||||
|
||||
_displayedImageIdentifier = [displayedImageIdentifier copy];
|
||||
@@ -332,7 +326,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
|
||||
- (void)_setDownloadIdentifier:(id)downloadIdentifier
|
||||
{
|
||||
if (_downloadIdentifier == downloadIdentifier)
|
||||
if (ASObjectIsEqual(downloadIdentifier, _downloadIdentifier))
|
||||
return;
|
||||
|
||||
[_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.
|
||||
id bestImageIdentifier = _imageIdentifiers.firstObject;
|
||||
if (!bestImageIdentifier || [_loadedImageIdentifier isEqual:bestImageIdentifier]) {
|
||||
if (!bestImageIdentifier || ASObjectIsEqual(_loadedImageIdentifier, bestImageIdentifier)) {
|
||||
OSSpinLockUnlock(&_imageIdentifiersLock);
|
||||
return nil;
|
||||
}
|
||||
@@ -439,7 +433,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
return;
|
||||
|
||||
// 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 _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 (![[strongSelf _nextImageIdentifierToDownload] isEqual:nextImageIdentifier]) {
|
||||
if (!ASObjectIsEqual([strongSelf _nextImageIdentifierToDownload], nextImageIdentifier)) {
|
||||
finishedLoadingBlock(nil, nil, [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodeBestImageIdentifierChanged userInfo:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -361,10 +361,4 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTableView (Deprecated)
|
||||
|
||||
@property (nonatomic, assign) ASRangeTuningParameters rangeTuningParameters ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -151,7 +151,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
#pragma mark -
|
||||
#pragma mark ASTableView
|
||||
|
||||
@interface ASTableView () <ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate> {
|
||||
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate> {
|
||||
_ASTableViewProxy *_proxyDataSource;
|
||||
_ASTableViewProxy *_proxyDelegate;
|
||||
|
||||
@@ -193,6 +193,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
|
||||
_rangeController = [[ASRangeController alloc] init];
|
||||
_rangeController.layoutController = _layoutController;
|
||||
_rangeController.dataSource = self;
|
||||
_rangeController.delegate = self;
|
||||
|
||||
_dataController = [[dataControllerClass alloc] initWithAsyncDataFetching:asyncDataFetching];
|
||||
@@ -639,11 +640,72 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - ASRangeControllerDataSource
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASRangeControllerDelegate
|
||||
- (NSArray *)visibleNodeIndexPathsForRangeController:(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;
|
||||
}
|
||||
|
||||
- (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();
|
||||
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();
|
||||
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
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
@@ -17,8 +17,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
@property (nonatomic, assign) ASRangeTuningParameters tuningParameters ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -52,33 +52,7 @@
|
||||
_tuningParameters[rangeType] = tuningParameters;
|
||||
}
|
||||
|
||||
// Support for the deprecated tuningParameters property
|
||||
- (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
|
||||
#pragma mark - Abstract Index Path Range Support
|
||||
|
||||
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ASCellNode;
|
||||
|
||||
typedef struct {
|
||||
CGFloat leadingBufferScreenfuls;
|
||||
CGFloat trailingBufferScreenfuls;
|
||||
@@ -34,12 +32,6 @@ typedef struct {
|
||||
|
||||
- (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
|
||||
|
||||
- (void)insertNodesAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withSizes:(NSArray<NSValue *> *)nodeSizes;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol ASRangeControllerDataSource;
|
||||
@protocol ASRangeControllerDelegate;
|
||||
|
||||
/**
|
||||
@@ -47,49 +48,46 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (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, strong) id<ASLayoutController> layoutController;
|
||||
|
||||
@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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @returns the receiver's viewport size (i.e., the screen space occupied by the visible range).
|
||||
*/
|
||||
- (CGSize)rangeControllerViewportSize:(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;
|
||||
- (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController;
|
||||
|
||||
/**
|
||||
* Fetch nodes at specific index paths.
|
||||
@@ -98,7 +96,30 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*
|
||||
* @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.
|
||||
|
||||
@@ -31,22 +31,35 @@
|
||||
@implementation ASRangeController
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_rangeIsValid = YES;
|
||||
_rangeTypeIndexPaths = [[NSMutableDictionary alloc] init];
|
||||
|
||||
_rangeTypeIndexPaths = [NSMutableDictionary dictionary];
|
||||
_rangeTypeHandlers = @{
|
||||
@(ASLayoutRangeTypeRender): [[ASRangeHandlerRender alloc] init],
|
||||
@(ASLayoutRangeTypePreload): [[ASRangeHandlerPreload alloc] init],
|
||||
};
|
||||
@(ASLayoutRangeTypeRender): [[ASRangeHandlerRender alloc] init],
|
||||
@(ASLayoutRangeTypePreload): [[ASRangeHandlerPreload alloc] init],
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
@@ -62,8 +75,7 @@
|
||||
[view addSubview:node.view];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - API
|
||||
#pragma mark - Core visible node range managment API
|
||||
|
||||
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
|
||||
{
|
||||
@@ -76,27 +88,27 @@
|
||||
// coalesce these events -- handling them multiple times per runloop is noisy and expensive
|
||||
_queuedRangeUpdate = YES;
|
||||
|
||||
[self performSelector:@selector(updateVisibleNodeIndexPaths)
|
||||
[self performSelector:@selector(_updateVisibleNodeIndexPaths)
|
||||
withObject:nil
|
||||
afterDelay:0
|
||||
inModes:@[ NSRunLoopCommonModes ]];
|
||||
}
|
||||
|
||||
- (void)updateVisibleNodeIndexPaths
|
||||
- (void)_updateVisibleNodeIndexPaths
|
||||
{
|
||||
if (!_queuedRangeUpdate) {
|
||||
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;
|
||||
return ; // don't do anything for this update, but leave _rangeIsValid to make sure we update it later
|
||||
}
|
||||
|
||||
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
|
||||
if ([_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)]) {
|
||||
@@ -108,28 +120,30 @@
|
||||
id rangeKey = @(rangeType);
|
||||
|
||||
// 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]) {
|
||||
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
|
||||
NSMutableSet *removedIndexPaths = _rangeIsValid ? [[_rangeTypeIndexPaths objectForKey:rangeKey] mutableCopy] : [NSMutableSet set];
|
||||
NSMutableSet *removedIndexPaths = _rangeIsValid ? [_rangeTypeIndexPaths[rangeKey] mutableCopy] : [NSMutableSet set];
|
||||
[removedIndexPaths minusSet:indexPaths];
|
||||
[removedIndexPaths minusSet:visibleNodePathsSet];
|
||||
|
||||
if (removedIndexPaths.count) {
|
||||
NSArray *removedNodes = [_delegate rangeController:self nodesAtIndexPaths:[removedIndexPaths allObjects]];
|
||||
[removedNodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) {
|
||||
NSArray *removedNodes = [_dataSource rangeController:self nodesAtIndexPaths:[removedIndexPaths allObjects]];
|
||||
for (ASCellNode *node in removedNodes) {
|
||||
// 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.
|
||||
[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];
|
||||
[addedIndexPaths minusSet:[_rangeTypeIndexPaths objectForKey:rangeKey]];
|
||||
[addedIndexPaths minusSet:_rangeTypeIndexPaths[rangeKey]];
|
||||
|
||||
// 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
|
||||
@@ -138,14 +152,14 @@
|
||||
}
|
||||
|
||||
if (addedIndexPaths.count) {
|
||||
NSArray *addedNodes = [_delegate rangeController:self nodesAtIndexPaths:[addedIndexPaths allObjects]];
|
||||
[addedNodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) {
|
||||
[rangeDelegate node:node enteredRangeOfType:rangeType];
|
||||
}];
|
||||
NSArray *addedNodes = [_dataSource rangeController:self nodesAtIndexPaths:[addedIndexPaths allObjects]];
|
||||
for (ASCellNode *node in addedNodes) {
|
||||
[rangeHandler node:node enteredRangeOfType:rangeType];
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
- (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
|
||||
|
||||
- (void)dataControllerBeginUpdates:(ASDataController *)dataController {
|
||||
ASPerformBlockOnMainThread(^{
|
||||
[_delegate rangeControllerBeginUpdates:self];
|
||||
[_delegate didBeginUpdatesInRangeController:self];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion {
|
||||
ASPerformBlockOnMainThread(^{
|
||||
[_delegate rangeController:self endUpdatesAnimated:animated completion:completion];
|
||||
[_delegate rangeController:self didEndUpdatesAnimated:animated completion:completion];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +204,6 @@
|
||||
3EEA4EE01BECC4A1008A7F35 /* Sources */,
|
||||
3EEA4EE11BECC4A1008A7F35 /* Frameworks */,
|
||||
3EEA4EE21BECC4A1008A7F35 /* Resources */,
|
||||
F8CD1BCEB86661EF98DB1C0C /* Embed Pods Frameworks */,
|
||||
21F2C1D9B53F9468EAF1653F /* Copy Pods Resources */,
|
||||
);
|
||||
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";
|
||||
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 */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
|
||||
Reference in New Issue
Block a user