mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-04-01 08:45:09 +08:00
Merge pull request #990 from facebook/ASPagerNodeAndCollectionAndTable
[ASPagerNode] New API tweaks. Support setting delegate + dataSource on ASCollectionNode and ASTableNode without triggering view creation.
This commit is contained in:
@@ -242,7 +242,7 @@
|
||||
34EFC7771B701D2D00AD841F /* ASStackUnpositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */; };
|
||||
34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */; };
|
||||
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED451B17847A00DA7C62 /* ASLayoutSpecUtilities.h */; };
|
||||
3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
|
||||
3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */; };
|
||||
430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
430E7C911B4C23F100697A4C /* ASIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 430E7C8E1B4C23F100697A4C /* ASIndexPath.m */; };
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
|
||||
@property (weak, nonatomic) id <ASCollectionDelegate> delegate;
|
||||
@property (weak, nonatomic) id <ASCollectionDataSource> dataSource;
|
||||
|
||||
@property (nonatomic, readonly) ASCollectionView *view;
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,19 @@
|
||||
#import "ASCollectionNode.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
|
||||
@interface ASCollectionView (Internal)
|
||||
@interface _ASCollectionPendingState : NSObject
|
||||
@property (weak, nonatomic) id <ASCollectionDelegate> delegate;
|
||||
@property (weak, nonatomic) id <ASCollectionDataSource> dataSource;
|
||||
@end
|
||||
|
||||
@implementation _ASCollectionPendingState
|
||||
@end
|
||||
|
||||
@interface ASCollectionNode ()
|
||||
@property (nonatomic) _ASCollectionPendingState *pendingState;
|
||||
@end
|
||||
|
||||
@interface ASCollectionView ()
|
||||
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
@end
|
||||
|
||||
@@ -29,12 +41,77 @@
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
if (self = [super initWithViewBlock:^UIView *{ return [[ASCollectionView alloc] _initWithFrame:frame collectionViewLayout:layout]; }]) {
|
||||
ASDisplayNodeViewBlock collectionViewBlock = ^UIView *{
|
||||
return [[ASCollectionView alloc] _initWithFrame:frame collectionViewLayout:layout];
|
||||
};
|
||||
|
||||
if (self = [super initWithViewBlock:collectionViewBlock]) {
|
||||
return self;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)didLoad
|
||||
{
|
||||
[super didLoad];
|
||||
|
||||
if (_pendingState) {
|
||||
_ASCollectionPendingState *pendingState = _pendingState;
|
||||
self.pendingState = nil;
|
||||
|
||||
ASCollectionView *view = self.view;
|
||||
view.asyncDelegate = pendingState.delegate;
|
||||
view.asyncDataSource = pendingState.dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
- (_ASCollectionPendingState *)pendingState
|
||||
{
|
||||
if (!_pendingState && ![self isNodeLoaded]) {
|
||||
self.pendingState = [[_ASCollectionPendingState alloc] init];
|
||||
}
|
||||
ASDisplayNodeAssert(![self isNodeLoaded] || !_pendingState, @"ASCollectionNode should not have a pendingState once it is loaded");
|
||||
return _pendingState;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id <ASCollectionDelegate>)delegate
|
||||
{
|
||||
if ([self pendingState]) {
|
||||
_pendingState.delegate = delegate;
|
||||
} else {
|
||||
ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist");
|
||||
self.view.asyncDelegate = delegate;
|
||||
}
|
||||
}
|
||||
|
||||
- (id <ASCollectionDelegate>)delegate
|
||||
{
|
||||
if ([self pendingState]) {
|
||||
return _pendingState.delegate;
|
||||
} else {
|
||||
return self.view.asyncDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDataSource:(id <ASCollectionDataSource>)dataSource
|
||||
{
|
||||
if ([self pendingState]) {
|
||||
_pendingState.dataSource = dataSource;
|
||||
} else {
|
||||
ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist");
|
||||
self.view.asyncDataSource = dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
- (id <ASCollectionDataSource>)dataSource
|
||||
{
|
||||
if ([self pendingState]) {
|
||||
return _pendingState.dataSource;
|
||||
} else {
|
||||
return self.view.asyncDataSource;
|
||||
}
|
||||
}
|
||||
|
||||
- (ASCollectionView *)view
|
||||
{
|
||||
return (ASCollectionView *)[super view];
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#import <AsyncDisplayKit/ASCollectionViewFlowLayoutInspector.h>
|
||||
|
||||
@class ASCellNode;
|
||||
@protocol ASCollectionViewDataSource;
|
||||
@protocol ASCollectionViewDelegate;
|
||||
@protocol ASCollectionDataSource;
|
||||
@protocol ASCollectionDelegate;
|
||||
@protocol ASCollectionViewLayoutInspecting;
|
||||
|
||||
/**
|
||||
@@ -35,8 +35,8 @@
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
|
||||
@property (nonatomic, weak) id<ASCollectionViewDataSource> asyncDataSource;
|
||||
@property (nonatomic, weak) id<ASCollectionViewDelegate> asyncDelegate; // must not be nil
|
||||
@property (nonatomic, weak) id<ASCollectionDelegate> asyncDelegate;
|
||||
@property (nonatomic, weak) id<ASCollectionDataSource> asyncDataSource;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type.
|
||||
@@ -286,7 +286,8 @@
|
||||
/**
|
||||
* This is a node-based UICollectionViewDataSource.
|
||||
*/
|
||||
@protocol ASCollectionViewDataSource <ASCommonCollectionViewDataSource, NSObject>
|
||||
#define ASCollectionViewDataSource ASCollectionDataSource
|
||||
@protocol ASCollectionDataSource <ASCommonCollectionViewDataSource, NSObject>
|
||||
|
||||
/**
|
||||
* Similar to -collectionView:cellForItemAtIndexPath:.
|
||||
@@ -347,7 +348,8 @@
|
||||
/**
|
||||
* This is a node-based UICollectionViewDelegate.
|
||||
*/
|
||||
@protocol ASCollectionViewDelegate <ASCommonCollectionViewDelegate, NSObject>
|
||||
#define ASCollectionViewDelegate ASCollectionDelegate
|
||||
@protocol ASCollectionDelegate <ASCommonCollectionViewDelegate, NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
|
||||
@@ -8,20 +8,35 @@
|
||||
|
||||
#import <AsyncDisplayKit/ASCollectionNode.h>
|
||||
|
||||
@protocol ASPagerNodeDataSource;
|
||||
@class ASPagerNode;
|
||||
@protocol ASPagerNodeDataSource <NSObject>
|
||||
// This method replaces -collectionView:numberOfItemsInSection:
|
||||
- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode;
|
||||
|
||||
// This method replaces -collectionView:nodeForItemAtIndexPath:
|
||||
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index;
|
||||
@end
|
||||
|
||||
@interface ASPagerNode : ASCollectionNode
|
||||
|
||||
@property (weak, nonatomic) id<ASPagerNodeDataSource> dataSource;
|
||||
// Configures a default horizontal, paging flow layout with 0 inter-item spacing.
|
||||
- (instancetype)init;
|
||||
|
||||
// Initializer with custom-configured flow layout properties.
|
||||
- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout;
|
||||
|
||||
// The underlying ASCollectionView object.
|
||||
- (ASCollectionView *)collectionView;
|
||||
|
||||
// Delegate is optional, and uses the same protocol as ASCollectionNode.
|
||||
// This includes UIScrollViewDelegate as well as most methods from UICollectionViewDelegate, like willDisplay...
|
||||
@property (weak, nonatomic) id <ASCollectionDelegate> delegate;
|
||||
|
||||
// Data Source is required, and uses a different protocol from ASCollectionNode.
|
||||
- (void)setDataSource:(id <ASPagerNodeDataSource>)dataSource;
|
||||
- (id <ASPagerNodeDataSource>)dataSource;
|
||||
|
||||
- (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated;
|
||||
|
||||
@end
|
||||
|
||||
@protocol ASPagerNodeDataSource <NSObject>
|
||||
|
||||
- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode;
|
||||
|
||||
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index;
|
||||
|
||||
@end
|
||||
@@ -7,16 +7,20 @@
|
||||
//
|
||||
|
||||
#import "ASPagerNode.h"
|
||||
#import "ASDelegateProxy.h"
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface ASPagerNode () <ASCollectionViewDataSource, ASCollectionViewDelegateFlowLayout> {
|
||||
@interface ASPagerNode () <ASCollectionDataSource, ASCollectionViewDelegateFlowLayout, ASDelegateProxyInterceptor> {
|
||||
UICollectionViewFlowLayout *_flowLayout;
|
||||
ASPagerNodeProxy *_proxy;
|
||||
id <ASPagerNodeDataSource> _pagerDataSource;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASPagerNode
|
||||
@dynamic delegate;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
@@ -25,6 +29,11 @@
|
||||
flowLayout.minimumInteritemSpacing = 0;
|
||||
flowLayout.minimumLineSpacing = 0;
|
||||
|
||||
return [self initWithFlowLayout:flowLayout];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout
|
||||
{
|
||||
self = [super initWithCollectionViewLayout:flowLayout];
|
||||
if (self != nil) {
|
||||
_flowLayout = flowLayout;
|
||||
@@ -32,17 +41,43 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (ASCollectionView *)collectionView
|
||||
{
|
||||
return self.view;
|
||||
}
|
||||
|
||||
- (void)setDataSource:(id <ASPagerNodeDataSource>)pagerDataSource
|
||||
{
|
||||
if (pagerDataSource != _pagerDataSource) {
|
||||
_pagerDataSource = pagerDataSource;
|
||||
_proxy = pagerDataSource ? [[ASPagerNodeProxy alloc] initWithTarget:pagerDataSource interceptor:self] : nil;
|
||||
super.dataSource = (id <ASCollectionDataSource>)_proxy;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy
|
||||
{
|
||||
[self setDataSource:nil];
|
||||
}
|
||||
|
||||
- (id <ASPagerNodeDataSource>)dataSource
|
||||
{
|
||||
return _pagerDataSource;
|
||||
}
|
||||
|
||||
- (void)didLoad
|
||||
{
|
||||
[super didLoad];
|
||||
|
||||
self.view.asyncDataSource = self;
|
||||
self.view.asyncDelegate = self;
|
||||
ASCollectionView *cv = self.view;
|
||||
cv.asyncDataSource = self;
|
||||
cv.asyncDelegate = self;
|
||||
|
||||
self.view.pagingEnabled = YES;
|
||||
self.view.allowsSelection = NO;
|
||||
self.view.showsVerticalScrollIndicator = NO;
|
||||
self.view.showsHorizontalScrollIndicator = NO;
|
||||
cv.pagingEnabled = YES;
|
||||
cv.allowsSelection = NO;
|
||||
cv.showsVerticalScrollIndicator = NO;
|
||||
cv.showsHorizontalScrollIndicator = NO;
|
||||
cv.scrollsToTop = NO;
|
||||
|
||||
ASRangeTuningParameters preloadParams = { .leadingBufferScreenfuls = 2.0, .trailingBufferScreenfuls = 2.0 };
|
||||
ASRangeTuningParameters renderParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 };
|
||||
@@ -62,14 +97,14 @@
|
||||
|
||||
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASDisplayNodeAssert(self.dataSource != nil, @"ASPagerNode must have a data source to load paging nodes");
|
||||
return [self.dataSource pagerNode:self nodeAtIndex:indexPath.item];
|
||||
ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load paging nodes");
|
||||
return [_pagerDataSource pagerNode:self nodeAtIndex:indexPath.item];
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
ASDisplayNodeAssert(self.dataSource != nil, @"ASPagerNode must have a data source to load paging nodes");
|
||||
return [self.dataSource numberOfPagesInPagerNode:self];
|
||||
ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load paging nodes");
|
||||
return [_pagerDataSource numberOfPagesInPagerNode:self];
|
||||
}
|
||||
|
||||
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
|
||||
@@ -14,8 +14,13 @@
|
||||
*/
|
||||
@interface ASTableNode : ASDisplayNode
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)init; // UITableViewStylePlain
|
||||
- (instancetype)initWithStyle:(UITableViewStyle)style;
|
||||
|
||||
@property (nonatomic, readonly) ASTableView *view;
|
||||
|
||||
// These properties can be set without triggering the view to be created, so it's fine to set them in -init.
|
||||
@property (weak, nonatomic) id <ASTableDelegate> delegate;
|
||||
@property (weak, nonatomic) id <ASTableDataSource> dataSource;
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,18 +8,105 @@
|
||||
|
||||
#import "ASTableNode.h"
|
||||
|
||||
@interface _ASTablePendingState : NSObject
|
||||
@property (weak, nonatomic) id <ASTableDelegate> delegate;
|
||||
@property (weak, nonatomic) id <ASTableDataSource> dataSource;
|
||||
@end
|
||||
|
||||
@implementation _ASTablePendingState
|
||||
@end
|
||||
|
||||
@interface ASTableNode ()
|
||||
@property (nonatomic) _ASTablePendingState *pendingState;
|
||||
@end
|
||||
|
||||
@interface ASTableView ()
|
||||
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass;
|
||||
@end
|
||||
|
||||
@implementation ASTableNode
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewStyle)style
|
||||
- (instancetype)_initWithStyle:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass
|
||||
{
|
||||
if (self = [super initWithViewBlock:^UIView *{
|
||||
return [[ASTableView alloc] initWithFrame:CGRectZero style:style];
|
||||
}]) {
|
||||
if (self = [super initWithViewBlock:^UIView *{ return [[ASTableView alloc] _initWithFrame:CGRectZero
|
||||
style:style
|
||||
dataControllerClass:dataControllerClass]; }]) {
|
||||
return self;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewStyle)style
|
||||
{
|
||||
return [self _initWithStyle:style dataControllerClass:nil];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self _initWithStyle:UITableViewStylePlain dataControllerClass:nil];
|
||||
}
|
||||
|
||||
- (void)didLoad
|
||||
{
|
||||
[super didLoad];
|
||||
|
||||
if (_pendingState) {
|
||||
_ASTablePendingState *pendingState = _pendingState;
|
||||
self.pendingState = nil;
|
||||
|
||||
ASTableView *view = self.view;
|
||||
view.asyncDelegate = pendingState.delegate;
|
||||
view.asyncDataSource = pendingState.dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
- (_ASTablePendingState *)pendingState
|
||||
{
|
||||
if (!_pendingState && ![self isNodeLoaded]) {
|
||||
self.pendingState = [[_ASTablePendingState alloc] init];
|
||||
}
|
||||
ASDisplayNodeAssert(![self isNodeLoaded] || !_pendingState, @"ASTableNode should not have a pendingState once it is loaded");
|
||||
return _pendingState;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id <ASTableDelegate>)delegate
|
||||
{
|
||||
if ([self pendingState]) {
|
||||
_pendingState.delegate = delegate;
|
||||
} else {
|
||||
ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist");
|
||||
self.view.asyncDelegate = delegate;
|
||||
}
|
||||
}
|
||||
|
||||
- (id <ASTableDelegate>)delegate
|
||||
{
|
||||
if ([self pendingState]) {
|
||||
return _pendingState.delegate;
|
||||
} else {
|
||||
return self.view.asyncDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDataSource:(id <ASTableDataSource>)dataSource
|
||||
{
|
||||
if ([self pendingState]) {
|
||||
_pendingState.dataSource = dataSource;
|
||||
} else {
|
||||
ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist");
|
||||
self.view.asyncDataSource = dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
- (id <ASTableDataSource>)dataSource
|
||||
{
|
||||
if ([self pendingState]) {
|
||||
return _pendingState.dataSource;
|
||||
} else {
|
||||
return self.view.asyncDataSource;
|
||||
}
|
||||
}
|
||||
|
||||
- (ASTableView *)view
|
||||
{
|
||||
return (ASTableView *)[super view];
|
||||
|
||||
@@ -7,17 +7,14 @@
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASRangeController.h>
|
||||
#import <AsyncDisplayKit/ASTableViewProtocols.h>
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
#import <AsyncDisplayKit/ASBatchContext.h>
|
||||
|
||||
|
||||
@class ASCellNode;
|
||||
@protocol ASTableViewDataSource;
|
||||
@protocol ASTableViewDelegate;
|
||||
|
||||
@protocol ASTableDataSource;
|
||||
@protocol ASTableDelegate;
|
||||
|
||||
/**
|
||||
* Node-based table view.
|
||||
@@ -27,8 +24,8 @@
|
||||
*/
|
||||
@interface ASTableView : UITableView
|
||||
|
||||
@property (nonatomic, weak) id<ASTableViewDelegate> asyncDelegate; // must not be nil
|
||||
@property (nonatomic, weak) id<ASTableViewDataSource> asyncDataSource;
|
||||
@property (nonatomic, weak) id<ASTableDelegate> asyncDelegate;
|
||||
@property (nonatomic, weak) id<ASTableDataSource> asyncDataSource;
|
||||
|
||||
/**
|
||||
* Initializer.
|
||||
@@ -280,7 +277,7 @@
|
||||
/**
|
||||
* This is a node-based UITableViewDataSource.
|
||||
*/
|
||||
@protocol ASTableViewDataSource <ASCommonTableViewDataSource, NSObject>
|
||||
@protocol ASTableDataSource <ASCommonTableViewDataSource, NSObject>
|
||||
|
||||
/**
|
||||
* Similar to -tableView:cellForRowAtIndexPath:.
|
||||
@@ -317,6 +314,8 @@
|
||||
|
||||
@end
|
||||
|
||||
@protocol ASTableViewDataSource <ASTableDataSource>
|
||||
@end
|
||||
|
||||
/**
|
||||
* This is a node-based UITableViewDelegate.
|
||||
@@ -324,7 +323,7 @@
|
||||
* Note that -tableView:heightForRowAtIndexPath: has been removed; instead, your custom ASCellNode subclasses are
|
||||
* responsible for deciding their preferred onscreen height in -calculateSizeThatFits:.
|
||||
*/
|
||||
@protocol ASTableViewDelegate <ASCommonTableViewDelegate, NSObject>
|
||||
@protocol ASTableDelegate <ASCommonTableViewDelegate, NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
@@ -358,4 +357,7 @@
|
||||
*/
|
||||
- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView;
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@protocol ASTableViewDelegate <ASTableDelegate>;
|
||||
@end
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#import "ASTableView.h"
|
||||
#import "ASTableViewInternal.h"
|
||||
#import "ASTableNode.h"
|
||||
|
||||
#import "ASAssert.h"
|
||||
#import "ASBatchFetching.h"
|
||||
@@ -20,6 +21,8 @@
|
||||
#import "ASLayoutController.h"
|
||||
#import "ASRangeController.h"
|
||||
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
//#define LOG(...) NSLog(__VA_ARGS__)
|
||||
@@ -79,6 +82,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
#pragma mark -
|
||||
#pragma mark ASTableView
|
||||
|
||||
@interface ASTableNode ()
|
||||
- (instancetype)_initWithStyle:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass;
|
||||
@end
|
||||
|
||||
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor> {
|
||||
ASTableViewProxy *_proxyDataSource;
|
||||
ASTableViewProxy *_proxyDelegate;
|
||||
@@ -159,33 +166,37 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
return [self initWithFrame:frame style:style asyncDataFetching:NO];
|
||||
}
|
||||
|
||||
// FIXME: This method is deprecated and will probably be removed in or shortly after 2.0.
|
||||
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style asyncDataFetching:(BOOL)asyncDataFetchingEnabled
|
||||
{
|
||||
return [self initWithFrame:frame style:style dataControllerClass:[self.class dataControllerClass] asyncDataFetching:asyncDataFetchingEnabled];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass asyncDataFetching:(BOOL)asyncDataFetchingEnabled
|
||||
{
|
||||
// ASTableNode *tableNode = [[ASTableNode alloc] _initWithStyle:style dataControllerClass:dataControllerClass];
|
||||
// tableNode.frame = frame;
|
||||
// return tableNode.view;
|
||||
return [self _initWithFrame:frame style:style dataControllerClass:dataControllerClass];
|
||||
}
|
||||
|
||||
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass
|
||||
{
|
||||
if (!(self = [super initWithFrame:frame style:style]))
|
||||
return nil;
|
||||
|
||||
// FIXME: asyncDataFetching is currently unreliable for some use cases.
|
||||
// https://github.com/facebook/AsyncDisplayKit/issues/385
|
||||
asyncDataFetchingEnabled = NO;
|
||||
|
||||
[self configureWithDataControllerClass:dataControllerClass asyncDataFetching:asyncDataFetchingEnabled];
|
||||
if (!dataControllerClass) {
|
||||
dataControllerClass = [self.class dataControllerClass];
|
||||
}
|
||||
[self configureWithDataControllerClass:dataControllerClass asyncDataFetching:NO];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
if (!(self = [super initWithCoder:aDecoder]))
|
||||
return nil;
|
||||
|
||||
[self configureWithDataControllerClass:[self.class dataControllerClass] asyncDataFetching:NO];
|
||||
|
||||
return self;
|
||||
NSLog(@"Warning: AsyncDisplayKit is not designed to be used with Interface Builder. Table properties set in IB will be lost.");
|
||||
return [self initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
|
||||
@class ASCollectionView;
|
||||
@protocol ASCollectionViewDelegate;
|
||||
@protocol ASCollectionDelegate;
|
||||
|
||||
@protocol ASCollectionViewLayoutInspecting <NSObject>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
*
|
||||
* @discussion A great time to update perform selector caches!
|
||||
*/
|
||||
- (void)didChangeCollectionViewDelegate:(id<ASCollectionViewDelegate>)delegate;
|
||||
- (void)didChangeCollectionViewDelegate:(id<ASCollectionDelegate>)delegate;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)didChangeCollectionViewDelegate:(id<ASCollectionViewDelegate>)delegate;
|
||||
- (void)didChangeCollectionViewDelegate:(id<ASCollectionDelegate>)delegate;
|
||||
{
|
||||
if (delegate == nil) {
|
||||
_delegateImplementsReferenceSizeForHeader = NO;
|
||||
|
||||
@@ -49,3 +49,7 @@
|
||||
|
||||
@interface ASCollectionViewProxy : ASDelegateProxy
|
||||
@end
|
||||
|
||||
@interface ASPagerNodeProxy : ASDelegateProxy
|
||||
@end
|
||||
|
||||
|
||||
@@ -60,6 +60,20 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASPagerNodeProxy
|
||||
|
||||
- (BOOL)interceptsSelector:(SEL)selector
|
||||
{
|
||||
return (
|
||||
// handled by ASPagerNodeDataSource node<->cell machinery
|
||||
selector == @selector(collectionView:nodeForItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:numberOfItemsInSection:) ||
|
||||
selector == @selector(collectionView:constrainedSizeForNodeAtIndexPath:)
|
||||
);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASDelegateProxy {
|
||||
id <NSObject> __weak _target;
|
||||
id <ASDelegateProxyInterceptor> __weak _interceptor;
|
||||
|
||||
@@ -39,33 +39,34 @@
|
||||
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
{
|
||||
CGSize size = {
|
||||
constrainedSize.max.width,
|
||||
constrainedSize.max.height
|
||||
};
|
||||
CGSize maxConstrainedSize = CGSizeMake(constrainedSize.max.width, constrainedSize.max.height);
|
||||
|
||||
NSArray *children = self.children;
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:children.count];
|
||||
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count];
|
||||
for (id<ASLayoutable> child in self.children) {
|
||||
CGSize autoMaxSize = {
|
||||
constrainedSize.max.width - child.layoutPosition.x,
|
||||
constrainedSize.max.height - child.layoutPosition.y
|
||||
};
|
||||
ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.sizeRange)
|
||||
? ASSizeRangeMake({0, 0}, autoMaxSize)
|
||||
: ASRelativeSizeRangeResolve(child.sizeRange, size);
|
||||
for (id<ASLayoutable> child in children) {
|
||||
CGPoint layoutPosition = child.layoutPosition;
|
||||
CGSize autoMaxSize = CGSizeMake(maxConstrainedSize.width - layoutPosition.x,
|
||||
maxConstrainedSize.height - layoutPosition.y);
|
||||
|
||||
ASRelativeSizeRange childSizeRange = child.sizeRange;
|
||||
BOOL childIsUnconstrained = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, childSizeRange);
|
||||
ASSizeRange childConstraint = childIsUnconstrained ? ASSizeRangeMake({0, 0}, autoMaxSize)
|
||||
: ASRelativeSizeRangeResolve(childSizeRange, maxConstrainedSize);
|
||||
|
||||
ASLayout *sublayout = [child measureWithSizeRange:childConstraint];
|
||||
sublayout.position = child.layoutPosition;
|
||||
sublayout.position = layoutPosition;
|
||||
[sublayouts addObject:sublayout];
|
||||
}
|
||||
|
||||
size.width = constrainedSize.min.width;
|
||||
for (ASLayout *sublayout in sublayouts) {
|
||||
size.width = MAX(size.width, sublayout.position.x + sublayout.size.width);
|
||||
}
|
||||
CGSize size = CGSizeMake(constrainedSize.min.width, constrainedSize.min.height);
|
||||
|
||||
size.height = constrainedSize.min.height;
|
||||
for (ASLayout *sublayout in sublayouts) {
|
||||
size.height = MAX(size.height, sublayout.position.y + sublayout.size.height);
|
||||
CGPoint sublayoutPosition = sublayout.position;
|
||||
CGSize sublayoutSize = sublayout.size;
|
||||
|
||||
size.width = MAX(size.width, sublayoutPosition.x + sublayoutSize.width);
|
||||
size.height = MAX(size.height, sublayoutPosition.y + sublayoutSize.height);
|
||||
}
|
||||
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
@@ -75,12 +76,12 @@
|
||||
|
||||
- (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren");
|
||||
ASDisplayNodeAssert(NO, @"ASStaticLayoutSpec only supports setChildren");
|
||||
}
|
||||
|
||||
- (id<ASLayoutable>)childForIdentifier:(NSString *)identifier
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports children");
|
||||
ASDisplayNodeAssert(NO, @"ASStaticLayoutSpec only supports children");
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#import "_ASCoreAnimationExtras.h"
|
||||
|
||||
#import "ASEqualityHelpers.h"
|
||||
#import "ASAssert.h"
|
||||
|
||||
extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image)
|
||||
@@ -87,7 +87,8 @@ static const struct _UIContentModeStringLUTEntry UIContentModeDescriptionLUT[] =
|
||||
{UIViewContentModeBottomRight, @"bottomRight"},
|
||||
};
|
||||
|
||||
NSString *ASDisplayNodeNSStringFromUIContentMode(UIViewContentMode contentMode) {
|
||||
NSString *ASDisplayNodeNSStringFromUIContentMode(UIViewContentMode contentMode)
|
||||
{
|
||||
for (int i=0; i< ARRAY_COUNT(UIContentModeDescriptionLUT); i++) {
|
||||
if (UIContentModeDescriptionLUT[i].contentMode == contentMode) {
|
||||
return UIContentModeDescriptionLUT[i].string;
|
||||
@@ -96,16 +97,10 @@ NSString *ASDisplayNodeNSStringFromUIContentMode(UIViewContentMode contentMode)
|
||||
return [NSString stringWithFormat:@"%d", (int)contentMode];
|
||||
}
|
||||
|
||||
UIViewContentMode ASDisplayNodeUIContentModeFromNSString(NSString *string) {
|
||||
// If you passed one of the constants (this is just an optimization to avoid string comparison)
|
||||
UIViewContentMode ASDisplayNodeUIContentModeFromNSString(NSString *string)
|
||||
{
|
||||
for (int i=0; i < ARRAY_COUNT(UIContentModeDescriptionLUT); i++) {
|
||||
if (UIContentModeDescriptionLUT[i].string == string) {
|
||||
return UIContentModeDescriptionLUT[i].contentMode;
|
||||
}
|
||||
}
|
||||
// If you passed something isEqualToString: to one of the constants
|
||||
for (int i=0; i < ARRAY_COUNT(UIContentModeDescriptionLUT); i++) {
|
||||
if ([UIContentModeDescriptionLUT[i].string isEqualToString:string]) {
|
||||
if (ASObjectIsEqual(UIContentModeDescriptionLUT[i].string, string)) {
|
||||
return UIContentModeDescriptionLUT[i].contentMode;
|
||||
}
|
||||
}
|
||||
@@ -126,18 +121,12 @@ NSString *const ASDisplayNodeCAContentsGravityFromUIContentMode(UIViewContentMod
|
||||
|
||||
UIViewContentMode ASDisplayNodeUIContentModeFromCAContentsGravity(NSString *const contentsGravity)
|
||||
{
|
||||
// If you passed one of the constants (this is just an optimization to avoid string comparison)
|
||||
for (int i=0; i < ARRAY_COUNT(UIContentModeCAGravityLUT); i++) {
|
||||
if (UIContentModeCAGravityLUT[i].string == contentsGravity) {
|
||||
return UIContentModeCAGravityLUT[i].contentMode;
|
||||
}
|
||||
}
|
||||
// If you passed something isEqualToString: to one of the constants
|
||||
for (int i=0; i < ARRAY_COUNT(UIContentModeCAGravityLUT); i++) {
|
||||
if ([UIContentModeCAGravityLUT[i].string isEqualToString:contentsGravity]) {
|
||||
if (ASObjectIsEqual(UIContentModeCAGravityLUT[i].string, contentsGravity)) {
|
||||
return UIContentModeCAGravityLUT[i].contentMode;
|
||||
}
|
||||
}
|
||||
|
||||
ASDisplayNodeCAssert(contentsGravity, @"Encountered an unknown contentsGravity \"%@\". Is this a new version of iOS?", contentsGravity);
|
||||
ASDisplayNodeCAssert(!contentsGravity, @"You passed nil to ASDisplayNodeUIContentModeFromCAContentsGravity. We're falling back to resize, but this is probably a bug.");
|
||||
// If asserts disabled, fall back to this
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
if (_willDeallocBlock) {
|
||||
_willDeallocBlock(self);
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -78,7 +77,6 @@
|
||||
if (_willDeallocBlock) {
|
||||
_willDeallocBlock(self);
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -130,6 +128,7 @@
|
||||
|
||||
@implementation ASTableViewTests
|
||||
|
||||
// TODO: Convert this to ARC.
|
||||
- (void)DISABLED_testTableViewDoesNotRetainItselfAndDelegate
|
||||
{
|
||||
ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||
@@ -148,11 +147,11 @@
|
||||
|
||||
tableView.asyncDataSource = delegate;
|
||||
tableView.asyncDelegate = delegate;
|
||||
|
||||
[delegate release];
|
||||
|
||||
// [delegate release];
|
||||
XCTAssertTrue(delegateDidDealloc, @"unexpected delegate lifetime:%@", delegate);
|
||||
|
||||
XCTAssertNoThrow([tableView release], @"unexpected exception when deallocating table view:%@", tableView);
|
||||
// XCTAssertNoThrow([tableView release], @"unexpected exception when deallocating table view:%@", tableView);
|
||||
XCTAssertTrue(tableViewDidDealloc, @"unexpected table view lifetime:%@", tableView);
|
||||
}
|
||||
|
||||
@@ -399,7 +398,10 @@
|
||||
style:UITableViewStylePlain
|
||||
asyncDataFetching:YES];
|
||||
ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new];
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
tableView.asyncDelegate = dataSource;
|
||||
tableView.asyncDataSource = dataSource;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user