mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
IGListKit Support II: Electric Boogaloo (#2942)
* Reimplement IGListKit support in a cleaner way * Rename and fix some stuff * Fix supplementaries more * Update docs * Update test * Cleanup minor things * Tweak it * Indentation * Remove irrelevant changes * Break out cell into its own file * Fix indentation * Address feedback
This commit is contained in:
@@ -8,12 +8,18 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "ASCollectionView.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "_ASCollectionViewCell.h"
|
||||
#import "ASAssert.h"
|
||||
#import "ASAvailability.h"
|
||||
#import "ASBatchFetching.h"
|
||||
#import "ASDelegateProxy.h"
|
||||
#import "ASCellNode+Internal.h"
|
||||
#import "ASCollectionDataController.h"
|
||||
#import "ASCollectionInternal.h"
|
||||
#import "ASCollectionViewLayoutController.h"
|
||||
#import "ASCollectionViewFlowLayoutInspector.h"
|
||||
#import "ASDisplayNodeExtras.h"
|
||||
@@ -27,6 +33,7 @@
|
||||
#import "ASSectionContext.h"
|
||||
#import "ASCollectionView+Undeprecated.h"
|
||||
#import "_ASHierarchyChangeSet.h"
|
||||
#import "ASCollectionInteropProtocols.h"
|
||||
|
||||
/**
|
||||
* A macro to get self.collectionNode and assign it to a local variable, or return
|
||||
@@ -62,79 +69,6 @@ static const NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimation
|
||||
/// Used for all cells and supplementaries. UICV keys by supp-kind+reuseID so this is plenty.
|
||||
static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASCellNode<->UICollectionViewCell bridging.
|
||||
|
||||
@class _ASCollectionViewCell;
|
||||
|
||||
@interface _ASCollectionViewCell : UICollectionViewCell
|
||||
@property (nonatomic, weak) ASCellNode *node;
|
||||
@property (nonatomic, strong) UICollectionViewLayoutAttributes *layoutAttributes;
|
||||
@end
|
||||
|
||||
@implementation _ASCollectionViewCell
|
||||
|
||||
- (void)setNode:(ASCellNode *)node
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
node.layoutAttributes = _layoutAttributes;
|
||||
_node = node;
|
||||
self.backgroundColor = node.backgroundColor;
|
||||
self.clipsToBounds = node.clipsToBounds;
|
||||
[node __setSelectedFromUIKit:self.selected];
|
||||
[node __setHighlightedFromUIKit:self.highlighted];
|
||||
}
|
||||
|
||||
- (void)setSelected:(BOOL)selected
|
||||
{
|
||||
[super setSelected:selected];
|
||||
[_node __setSelectedFromUIKit:selected];
|
||||
}
|
||||
|
||||
- (void)setHighlighted:(BOOL)highlighted
|
||||
{
|
||||
[super setHighlighted:highlighted];
|
||||
[_node __setHighlightedFromUIKit:highlighted];
|
||||
}
|
||||
|
||||
- (void)setLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
|
||||
{
|
||||
_layoutAttributes = layoutAttributes;
|
||||
_node.layoutAttributes = layoutAttributes;
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
self.layoutAttributes = nil;
|
||||
|
||||
// Need to clear node pointer before UIKit calls setSelected:NO / setHighlighted:NO on its cells
|
||||
self.node = nil;
|
||||
[super prepareForReuse];
|
||||
}
|
||||
|
||||
/**
|
||||
* In the initial case, this is called by UICollectionView during cell dequeueing, before
|
||||
* we get a chance to assign a node to it, so we must be sure to set these layout attributes
|
||||
* on our node when one is next assigned to us in @c setNode: . Since there may be cases when we _do_ already
|
||||
* have our node assigned e.g. during a layout update for existing cells, we also attempt
|
||||
* to update it now.
|
||||
*/
|
||||
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
|
||||
{
|
||||
self.layoutAttributes = layoutAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep our node filling our content view.
|
||||
*/
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
self.node.frame = self.contentView.bounds;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASCollectionView.
|
||||
|
||||
@@ -243,8 +177,9 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
unsigned int collectionNodeWillBeginBatchFetch:1;
|
||||
unsigned int collectionNodeWillDisplaySupplementaryElement:1;
|
||||
unsigned int collectionNodeDidEndDisplayingSupplementaryElement:1;
|
||||
|
||||
unsigned int shouldBatchFetchForCollectionNode:1;
|
||||
// Whether this delegate conforms to ASCollectionDataSourceInterop
|
||||
unsigned int interop:1;
|
||||
} _asyncDelegateFlags;
|
||||
|
||||
struct {
|
||||
@@ -256,9 +191,13 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
unsigned int collectionNodeNodeForItem:1;
|
||||
unsigned int collectionNodeNodeBlockForItem:1;
|
||||
unsigned int collectionNodeNodeForSupplementaryElement:1;
|
||||
unsigned int collectionNodeSupplementaryElementKindsInSection:1;
|
||||
unsigned int numberOfSectionsInCollectionNode:1;
|
||||
unsigned int collectionNodeNumberOfItemsInSection:1;
|
||||
unsigned int collectionNodeContextForSection:1;
|
||||
|
||||
// Whether this data source conforms to ASCollectionDataSourceInterop
|
||||
unsigned int interop:1;
|
||||
} _asyncDataSourceFlags;
|
||||
|
||||
struct {
|
||||
@@ -267,8 +206,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
} _layoutInspectorFlags;
|
||||
}
|
||||
|
||||
@property (nonatomic, weak) ASCollectionNode *collectionNode;
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionNode ()
|
||||
@@ -426,14 +363,14 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
|
||||
- (void)setDataSource:(id<UICollectionViewDataSource>)dataSource
|
||||
{
|
||||
// UIKit can internally generate a call to this method upon changing the asyncDataSource; only assert for non-nil.
|
||||
ASDisplayNodeAssert(dataSource == nil, @"ASCollectionView uses asyncDataSource, not UICollectionView's dataSource property.");
|
||||
// UIKit can internally generate a call to this method upon changing the asyncDataSource; only assert for non-nil. We also allow this when we're doing interop.
|
||||
ASDisplayNodeAssert(_asyncDelegateFlags.interop || dataSource == nil, @"ASCollectionView uses asyncDataSource, not UICollectionView's dataSource property.");
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<UICollectionViewDelegate>)delegate
|
||||
{
|
||||
// Our UIScrollView superclass sets its delegate to nil on dealloc. Only assert if we get a non-nil value here.
|
||||
ASDisplayNodeAssert(delegate == nil, @"ASCollectionView uses asyncDelegate, not UICollectionView's delegate property.");
|
||||
// Our UIScrollView superclass sets its delegate to nil on dealloc. Only assert if we get a non-nil value here. We also allow this when we're doing interop.
|
||||
ASDisplayNodeAssert(_asyncDelegateFlags.interop || delegate == nil, @"ASCollectionView uses asyncDelegate, not UICollectionView's delegate property.");
|
||||
}
|
||||
|
||||
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy
|
||||
@@ -464,8 +401,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
if (asyncDataSource == nil) {
|
||||
_asyncDataSource = nil;
|
||||
_proxyDataSource = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||
|
||||
memset(&_asyncDataSourceFlags, 0, sizeof(_asyncDataSourceFlags));
|
||||
_asyncDataSourceFlags = {};
|
||||
|
||||
} else {
|
||||
_asyncDataSource = asyncDataSource;
|
||||
_proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
||||
@@ -482,7 +419,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
_asyncDataSourceFlags.collectionNodeNumberOfItemsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:numberOfItemsInSection:)];
|
||||
_asyncDataSourceFlags.collectionNodeContextForSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:contextForSection:)];
|
||||
_asyncDataSourceFlags.collectionNodeNodeForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeForSupplementaryElementOfKind:atIndexPath:)];
|
||||
|
||||
_asyncDataSourceFlags.collectionNodeSupplementaryElementKindsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:supplementaryElementKindsInSection:)];
|
||||
_asyncDataSourceFlags.interop = [_asyncDataSource conformsToProtocol:@protocol(ASCollectionDataSourceInterop)];
|
||||
|
||||
ASDisplayNodeAssert(_asyncDataSourceFlags.collectionNodeNumberOfItemsInSection || _asyncDataSourceFlags.collectionViewNumberOfItemsInSection, @"Data source must implement collectionNode:numberOfItemsInSection:");
|
||||
ASDisplayNodeAssert(_asyncDataSourceFlags.collectionNodeNodeBlockForItem
|
||||
@@ -520,8 +458,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
if (asyncDelegate == nil) {
|
||||
_asyncDelegate = nil;
|
||||
_proxyDelegate = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||
|
||||
memset(&_asyncDelegateFlags, 0, sizeof(_asyncDelegateFlags));
|
||||
_asyncDataSourceFlags = {};
|
||||
} else {
|
||||
_asyncDelegate = asyncDelegate;
|
||||
_proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
|
||||
@@ -561,6 +498,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
_asyncDelegateFlags.collectionNodeShouldShowMenuForItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:shouldShowMenuForItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionNodeCanPerformActionForItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:canPerformAction:forItemAtIndexPath:sender:)];
|
||||
_asyncDelegateFlags.collectionNodePerformActionForItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:performAction:forItemAtIndexPath:sender:)];
|
||||
_asyncDelegateFlags.interop = [_asyncDelegate conformsToProtocol:@protocol(ASCollectionDelegateInterop)];
|
||||
}
|
||||
|
||||
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
|
||||
@@ -939,7 +877,13 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
|
||||
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
UICollectionReusableView *view = [self dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kReuseIdentifier forIndexPath:indexPath];
|
||||
UICollectionReusableView *view;
|
||||
if (_asyncDataSource && _asyncDataSourceFlags.interop) {
|
||||
view = [(id<ASCollectionDataSourceInterop>)_asyncDataSource collectionView:collectionView viewForSupplementaryElementOfKind:kind atIndexPath:indexPath];
|
||||
} else {
|
||||
view = [self dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kReuseIdentifier forIndexPath:indexPath];
|
||||
}
|
||||
|
||||
ASCellNode *node = [_dataController supplementaryNodeOfKind:kind atIndexPath:indexPath];
|
||||
ASDisplayNodeAssert(node != nil, @"Supplementary node should exist. Kind = %@, indexPath = %@, collectionDataSource = %@", kind, indexPath, self);
|
||||
[_rangeController configureContentView:view forCellNode:node];
|
||||
@@ -948,7 +892,12 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
_ASCollectionViewCell *cell = [self dequeueReusableCellWithReuseIdentifier:kReuseIdentifier forIndexPath:indexPath];
|
||||
_ASCollectionViewCell *cell;
|
||||
if (_asyncDataSource && _asyncDataSourceFlags.interop) {
|
||||
cell = [(id<ASCollectionDataSourceInterop>)_asyncDataSource collectionView:collectionView cellForItemAtIndexPath:indexPath];
|
||||
} else {
|
||||
cell = [self dequeueReusableCellWithReuseIdentifier:kReuseIdentifier forIndexPath:indexPath];
|
||||
}
|
||||
|
||||
ASCellNode *node = [self nodeForItemAtIndexPath:indexPath];
|
||||
cell.node = node;
|
||||
@@ -979,7 +928,9 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
|
||||
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with cell that will be displayed not to be nil. indexPath: %@", indexPath);
|
||||
|
||||
if (_asyncDelegateFlags.collectionNodeWillDisplayItem) {
|
||||
if (_asyncDelegateFlags.interop) {
|
||||
[(id<ASCollectionDelegateInterop>)_asyncDelegate collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath];
|
||||
} else if (_asyncDelegateFlags.collectionNodeWillDisplayItem) {
|
||||
if (ASCollectionNode *collectionNode = self.collectionNode) {
|
||||
[_asyncDelegate collectionNode:collectionNode willDisplayItemWithNode:cellNode];
|
||||
}
|
||||
@@ -1004,7 +955,9 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
ASCellNode *cellNode = [cell node];
|
||||
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
|
||||
|
||||
if (_asyncDelegateFlags.collectionNodeDidEndDisplayingItem) {
|
||||
if (_asyncDelegateFlags.interop) {
|
||||
[(id<ASCollectionDelegateInterop>)_asyncDelegate collectionView:collectionView didEndDisplayingCell:cell forItemAtIndexPath:indexPath];
|
||||
} else if (_asyncDelegateFlags.collectionNodeDidEndDisplayingItem) {
|
||||
if (ASCollectionNode *collectionNode = self.collectionNode) {
|
||||
[_asyncDelegate collectionNode:collectionNode didEndDisplayingItemWithNode:cellNode];
|
||||
}
|
||||
@@ -1544,10 +1497,20 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
return node;
|
||||
}
|
||||
|
||||
// TODO: Lock this
|
||||
- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController
|
||||
- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController sections:(NSIndexSet *)sections
|
||||
{
|
||||
return [_registeredSupplementaryKinds allObjects];
|
||||
if (_asyncDataSourceFlags.collectionNodeSupplementaryElementKindsInSection) {
|
||||
NSMutableSet *kinds = [NSMutableSet set];
|
||||
GET_COLLECTIONNODE_OR_RETURN(collectionNode, @[]);
|
||||
[sections enumerateIndexesUsingBlock:^(NSUInteger section, BOOL * _Nonnull stop) {
|
||||
NSArray *kindsForSection = [_asyncDataSource collectionNode:collectionNode supplementaryElementKindsInSection:section];
|
||||
[kinds addObjectsFromArray:kindsForSection];
|
||||
}];
|
||||
return [kinds allObjects];
|
||||
} else {
|
||||
// TODO: Lock this
|
||||
return [_registeredSupplementaryKinds allObjects];
|
||||
}
|
||||
}
|
||||
|
||||
- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
||||
|
||||
Reference in New Issue
Block a user