mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
New ASDelegateProxy class to unify logic for Table & Collection forwarding. Fix dealloc-during-animation crash.
This commit is contained in:
@@ -461,6 +461,10 @@
|
|||||||
DE040EF91C2B40AC004692FF /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
DE040EF91C2B40AC004692FF /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
DE6EA3221C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
|
DE6EA3221C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
|
||||||
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
|
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
|
||||||
|
DEC447B51C2B9DBC00C8CBD1 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = DEC447B31C2B9DBC00C8CBD1 /* ASDelegateProxy.h */; };
|
||||||
|
DEC447B61C2B9DBC00C8CBD1 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = DEC447B31C2B9DBC00C8CBD1 /* ASDelegateProxy.h */; };
|
||||||
|
DEC447B71C2B9DBC00C8CBD1 /* ASDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC447B41C2B9DBC00C8CBD1 /* ASDelegateProxy.m */; };
|
||||||
|
DEC447B81C2B9DBC00C8CBD1 /* ASDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC447B41C2B9DBC00C8CBD1 /* ASDelegateProxy.m */; };
|
||||||
DECBD6E71BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
DECBD6E71BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; };
|
DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; };
|
||||||
@@ -754,6 +758,8 @@
|
|||||||
D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; };
|
D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; };
|
||||||
D785F6611A74327E00291744 /* ASScrollNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollNode.m; sourceTree = "<group>"; };
|
D785F6611A74327E00291744 /* ASScrollNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollNode.m; sourceTree = "<group>"; };
|
||||||
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+FrameworkPrivate.h"; sourceTree = "<group>"; };
|
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+FrameworkPrivate.h"; sourceTree = "<group>"; };
|
||||||
|
DEC447B31C2B9DBC00C8CBD1 /* ASDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDelegateProxy.h; sourceTree = "<group>"; };
|
||||||
|
DEC447B41C2B9DBC00C8CBD1 /* ASDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDelegateProxy.m; sourceTree = "<group>"; };
|
||||||
DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASButtonNode.h; sourceTree = "<group>"; };
|
DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASButtonNode.h; sourceTree = "<group>"; };
|
||||||
DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonNode.mm; sourceTree = "<group>"; };
|
DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonNode.mm; sourceTree = "<group>"; };
|
||||||
EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -1149,6 +1155,8 @@
|
|||||||
25B171EA1C12242700508A7A /* Data Controller */ = {
|
25B171EA1C12242700508A7A /* Data Controller */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
DEC447B31C2B9DBC00C8CBD1 /* ASDelegateProxy.h */,
|
||||||
|
DEC447B41C2B9DBC00C8CBD1 /* ASDelegateProxy.m */,
|
||||||
251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */,
|
251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */,
|
||||||
251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */,
|
251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */,
|
||||||
464052191A3F83C40061C0BA /* ASDataController.h */,
|
464052191A3F83C40061C0BA /* ASDataController.h */,
|
||||||
@@ -1264,6 +1272,7 @@
|
|||||||
18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
|
18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
|
||||||
257754C01BEE458E00737CA5 /* ASTextNodeWordKerner.h in Headers */,
|
257754C01BEE458E00737CA5 /* ASTextNodeWordKerner.h in Headers */,
|
||||||
AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */,
|
AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */,
|
||||||
|
DEC447B51C2B9DBC00C8CBD1 /* ASDelegateProxy.h in Headers */,
|
||||||
205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */,
|
205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */,
|
||||||
AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */,
|
AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */,
|
||||||
058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */,
|
058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */,
|
||||||
@@ -1432,6 +1441,7 @@
|
|||||||
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */,
|
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */,
|
||||||
B350625C1B010F070018CF92 /* ASLog.h in Headers */,
|
B350625C1B010F070018CF92 /* ASLog.h in Headers */,
|
||||||
0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
|
0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
|
||||||
|
DEC447B61C2B9DBC00C8CBD1 /* ASDelegateProxy.h in Headers */,
|
||||||
B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */,
|
B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */,
|
||||||
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */,
|
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */,
|
||||||
B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */,
|
B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */,
|
||||||
@@ -1687,6 +1697,7 @@
|
|||||||
0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */,
|
0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */,
|
||||||
299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */,
|
299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */,
|
||||||
AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */,
|
AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */,
|
||||||
|
DEC447B71C2B9DBC00C8CBD1 /* ASDelegateProxy.m in Sources */,
|
||||||
ACF6ED1D1B17843500DA7C62 /* ASCenterLayoutSpec.mm in Sources */,
|
ACF6ED1D1B17843500DA7C62 /* ASCenterLayoutSpec.mm in Sources */,
|
||||||
18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m in Sources */,
|
18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m in Sources */,
|
||||||
92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */,
|
92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */,
|
||||||
@@ -1816,6 +1827,7 @@
|
|||||||
509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */,
|
509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */,
|
||||||
254C6B861BF94F8A003EC431 /* ASTextKitContext.mm in Sources */,
|
254C6B861BF94F8A003EC431 /* ASTextKitContext.mm in Sources */,
|
||||||
34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */,
|
34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */,
|
||||||
|
DEC447B81C2B9DBC00C8CBD1 /* ASDelegateProxy.m in Sources */,
|
||||||
B35062141B010EFD0018CF92 /* ASBasicImageDownloader.mm in Sources */,
|
B35062141B010EFD0018CF92 /* ASBasicImageDownloader.mm in Sources */,
|
||||||
B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */,
|
B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */,
|
||||||
AC47D9421B3B891B00AAEE9D /* ASCellNode.m in Sources */,
|
AC47D9421B3B891B00AAEE9D /* ASCellNode.m in Sources */,
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:AsyncDisplayKit/Details/ASDelegateProxy.h">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:AsyncDisplayKit/Details/ASDelegateProxy.m">
|
||||||
|
</FileRef>
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:AsyncDisplayKit.xcodeproj">
|
location = "group:AsyncDisplayKit.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#import "ASAssert.h"
|
#import "ASAssert.h"
|
||||||
#import "ASBatchFetching.h"
|
#import "ASBatchFetching.h"
|
||||||
|
#import "ASDelegateProxy.h"
|
||||||
#import "ASCollectionView.h"
|
#import "ASCollectionView.h"
|
||||||
#import "ASCollectionNode.h"
|
#import "ASCollectionNode.h"
|
||||||
#import "ASCollectionDataController.h"
|
#import "ASCollectionDataController.h"
|
||||||
@@ -22,87 +23,6 @@ static const NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimation
|
|||||||
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
|
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
|
||||||
static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Proxying.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ASCollectionView intercepts and/or overrides a few of UICollectionView's critical data source and delegate methods.
|
|
||||||
*
|
|
||||||
* Any selector included in this function *MUST* be implemented by ASCollectionView.
|
|
||||||
*/
|
|
||||||
static BOOL _isInterceptedSelector(SEL sel)
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
// handled by ASCollectionView node<->cell machinery
|
|
||||||
sel == @selector(collectionView:cellForItemAtIndexPath:) ||
|
|
||||||
sel == @selector(collectionView:layout:sizeForItemAtIndexPath:) ||
|
|
||||||
sel == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) ||
|
|
||||||
|
|
||||||
// handled by ASRangeController
|
|
||||||
sel == @selector(numberOfSectionsInCollectionView:) ||
|
|
||||||
sel == @selector(collectionView:numberOfItemsInSection:) ||
|
|
||||||
|
|
||||||
// used for ASRangeController visibility updates
|
|
||||||
sel == @selector(collectionView:willDisplayCell:forItemAtIndexPath:) ||
|
|
||||||
sel == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) ||
|
|
||||||
|
|
||||||
// used for batch fetching API
|
|
||||||
sel == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stand-in for UICollectionViewDataSource and UICollectionViewDelegate. Any method calls we intercept are routed to ASCollectionView;
|
|
||||||
* everything else leaves AsyncDisplayKit safely and arrives at the original intended data source and delegate.
|
|
||||||
*/
|
|
||||||
@interface _ASCollectionViewProxy : NSProxy
|
|
||||||
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASCollectionView *)interceptor;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _ASCollectionViewProxy {
|
|
||||||
id<NSObject> __weak _target;
|
|
||||||
ASCollectionView * __weak _interceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASCollectionView *)interceptor
|
|
||||||
{
|
|
||||||
// -[NSProxy init] is undefined
|
|
||||||
if (!self) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASDisplayNodeAssert(target, @"target must not be nil");
|
|
||||||
ASDisplayNodeAssert(interceptor, @"interceptor must not be nil");
|
|
||||||
|
|
||||||
_target = target;
|
|
||||||
_interceptor = interceptor;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)respondsToSelector:(SEL)aSelector
|
|
||||||
{
|
|
||||||
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
|
|
||||||
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
|
|
||||||
|
|
||||||
return (_isInterceptedSelector(aSelector) || [_target respondsToSelector:aSelector]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)forwardingTargetForSelector:(SEL)aSelector
|
|
||||||
{
|
|
||||||
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
|
|
||||||
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
|
|
||||||
|
|
||||||
if (_isInterceptedSelector(aSelector)) {
|
|
||||||
return _interceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [_target respondsToSelector:aSelector] ? _target : nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark ASCellNode<->UICollectionViewCell bridging.
|
#pragma mark ASCellNode<->UICollectionViewCell bridging.
|
||||||
|
|
||||||
@@ -138,9 +58,9 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark ASCollectionView.
|
#pragma mark ASCollectionView.
|
||||||
|
|
||||||
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate> {
|
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor> {
|
||||||
_ASCollectionViewProxy *_proxyDataSource;
|
ASCollectionViewProxy *_proxyDataSource;
|
||||||
_ASCollectionViewProxy *_proxyDelegate;
|
ASCollectionViewProxy *_proxyDelegate;
|
||||||
|
|
||||||
ASCollectionDataController *_dataController;
|
ASCollectionDataController *_dataController;
|
||||||
ASRangeController *_rangeController;
|
ASRangeController *_rangeController;
|
||||||
@@ -155,6 +75,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
BOOL _collectionViewLayoutImplementsInsetSection;
|
BOOL _collectionViewLayoutImplementsInsetSection;
|
||||||
BOOL _asyncDataSourceImplementsConstrainedSizeForNode;
|
BOOL _asyncDataSourceImplementsConstrainedSizeForNode;
|
||||||
BOOL _queuedNodeSizeUpdate;
|
BOOL _queuedNodeSizeUpdate;
|
||||||
|
BOOL _isDeallocating;
|
||||||
|
|
||||||
ASBatchContext *_batchContext;
|
ASBatchContext *_batchContext;
|
||||||
|
|
||||||
@@ -244,6 +165,12 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
_layoutInspector = [self flowLayoutInspector];
|
_layoutInspector = [self flowLayoutInspector];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||||
|
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
|
||||||
|
|
||||||
|
_proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||||
|
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
|
||||||
|
|
||||||
_registeredSupplementaryKinds = [NSMutableSet set];
|
_registeredSupplementaryKinds = [NSMutableSet set];
|
||||||
|
|
||||||
self.backgroundColor = [UIColor whiteColor];
|
self.backgroundColor = [UIColor whiteColor];
|
||||||
@@ -255,10 +182,10 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
// Sometimes the UIKit classes can call back to their delegate even during deallocation.
|
// Sometimes the UIKit classes can call back to their delegate even during deallocation, due to animation completion blocks etc.
|
||||||
// This bug might be iOS 7-specific.
|
_isDeallocating = YES;
|
||||||
super.delegate = nil;
|
[self setAsyncDelegate:nil];
|
||||||
super.dataSource = nil;
|
[self setAsyncDataSource:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -312,24 +239,35 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
ASDisplayNodeAssert(delegate == nil, @"ASCollectionView uses asyncDelegate, not UICollectionView's delegate property.");
|
ASDisplayNodeAssert(delegate == nil, @"ASCollectionView uses asyncDelegate, not UICollectionView's delegate property.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy
|
||||||
|
{
|
||||||
|
if (proxy == _proxyDelegate) {
|
||||||
|
[self setAsyncDelegate:nil];
|
||||||
|
} else if (proxy == _proxyDataSource) {
|
||||||
|
[self setAsyncDataSource:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setAsyncDataSource:(id<ASCollectionViewDataSource>)asyncDataSource
|
- (void)setAsyncDataSource:(id<ASCollectionViewDataSource>)asyncDataSource
|
||||||
{
|
{
|
||||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||||
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
|
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
|
||||||
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
|
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
|
||||||
// super.dataSource in this case because calls to _ASTableViewProxy will start failing and cause crashes.
|
// super.dataSource in this case because calls to ASCollectionViewProxy will start failing and cause crashes.
|
||||||
|
|
||||||
|
super.dataSource = nil;
|
||||||
|
|
||||||
if (asyncDataSource == nil) {
|
if (asyncDataSource == nil) {
|
||||||
super.dataSource = nil;
|
|
||||||
_asyncDataSource = nil;
|
_asyncDataSource = nil;
|
||||||
_proxyDataSource = nil;
|
_proxyDataSource = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||||
_asyncDataSourceImplementsConstrainedSizeForNode = NO;
|
_asyncDataSourceImplementsConstrainedSizeForNode = NO;
|
||||||
} else {
|
} else {
|
||||||
_asyncDataSource = asyncDataSource;
|
_asyncDataSource = asyncDataSource;
|
||||||
_proxyDataSource = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
_proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
||||||
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
|
|
||||||
_asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0);
|
_asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setAsyncDelegate:(id<ASCollectionViewDelegate>)asyncDelegate
|
- (void)setAsyncDelegate:(id<ASCollectionViewDelegate>)asyncDelegate
|
||||||
@@ -337,22 +275,25 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||||
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
|
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
|
||||||
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
|
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
|
||||||
// super.delegate in this case because calls to _ASTableViewProxy will start failing and cause crashes.
|
// super.delegate in this case because calls to ASCollectionViewProxy will start failing and cause crashes.
|
||||||
|
|
||||||
|
// Order is important here, the asyncDelegate must be callable while nilling super.delegate to avoid random crashes
|
||||||
|
// in UIScrollViewAccessibility.
|
||||||
|
|
||||||
|
super.delegate = nil;
|
||||||
|
|
||||||
if (asyncDelegate == nil) {
|
if (asyncDelegate == nil) {
|
||||||
// order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes
|
|
||||||
// in UIScrollViewAccessibility.
|
|
||||||
super.delegate = nil;
|
|
||||||
_asyncDelegate = nil;
|
_asyncDelegate = nil;
|
||||||
_proxyDelegate = nil;
|
_proxyDelegate = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||||
_asyncDelegateImplementsInsetSection = NO;
|
_asyncDelegateImplementsInsetSection = NO;
|
||||||
} else {
|
} else {
|
||||||
_asyncDelegate = asyncDelegate;
|
_asyncDelegate = asyncDelegate;
|
||||||
_proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
|
_proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
|
||||||
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
|
|
||||||
_asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0);
|
_asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
|
||||||
|
|
||||||
[_layoutInspector didChangeCollectionViewDelegate:asyncDelegate];
|
[_layoutInspector didChangeCollectionViewDelegate:asyncDelegate];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#import "ASBatchFetching.h"
|
#import "ASBatchFetching.h"
|
||||||
#import "ASChangeSetDataController.h"
|
#import "ASChangeSetDataController.h"
|
||||||
#import "ASCollectionViewLayoutController.h"
|
#import "ASCollectionViewLayoutController.h"
|
||||||
|
#import "ASDelegateProxy.h"
|
||||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||||
#import "ASInternalHelpers.h"
|
#import "ASInternalHelpers.h"
|
||||||
#import "ASLayout.h"
|
#import "ASLayout.h"
|
||||||
@@ -24,87 +25,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
//#define LOG(...) NSLog(__VA_ARGS__)
|
//#define LOG(...) NSLog(__VA_ARGS__)
|
||||||
#define LOG(...)
|
#define LOG(...)
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Proxying.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ASTableView intercepts and/or overrides a few of UITableView's critical data source and delegate methods.
|
|
||||||
*
|
|
||||||
* Any selector included in this function *MUST* be implemented by ASTableView.
|
|
||||||
*/
|
|
||||||
static BOOL _isInterceptedSelector(SEL sel)
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
// handled by ASTableView node<->cell machinery
|
|
||||||
sel == @selector(tableView:cellForRowAtIndexPath:) ||
|
|
||||||
sel == @selector(tableView:heightForRowAtIndexPath:) ||
|
|
||||||
|
|
||||||
// handled by ASRangeController
|
|
||||||
sel == @selector(numberOfSectionsInTableView:) ||
|
|
||||||
sel == @selector(tableView:numberOfRowsInSection:) ||
|
|
||||||
|
|
||||||
// used for ASRangeController visibility updates
|
|
||||||
sel == @selector(tableView:willDisplayCell:forRowAtIndexPath:) ||
|
|
||||||
sel == @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:) ||
|
|
||||||
|
|
||||||
// used for batch fetching API
|
|
||||||
sel == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stand-in for UITableViewDataSource and UITableViewDelegate. Any method calls we intercept are routed to ASTableView;
|
|
||||||
* everything else leaves AsyncDisplayKit safely and arrives at the original intended data source and delegate.
|
|
||||||
*/
|
|
||||||
@interface _ASTableViewProxy : NSProxy
|
|
||||||
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASTableView *)interceptor;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation _ASTableViewProxy {
|
|
||||||
id<NSObject> __weak _target;
|
|
||||||
ASTableView * __weak _interceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASTableView *)interceptor
|
|
||||||
{
|
|
||||||
// -[NSProxy init] is undefined
|
|
||||||
if (!self) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASDisplayNodeAssert(target, @"target must not be nil");
|
|
||||||
ASDisplayNodeAssert(interceptor, @"interceptor must not be nil");
|
|
||||||
|
|
||||||
_target = target;
|
|
||||||
_interceptor = interceptor;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)respondsToSelector:(SEL)aSelector
|
|
||||||
{
|
|
||||||
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
|
|
||||||
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
|
|
||||||
|
|
||||||
return (_isInterceptedSelector(aSelector) || [_target respondsToSelector:aSelector]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)forwardingTargetForSelector:(SEL)aSelector
|
|
||||||
{
|
|
||||||
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
|
|
||||||
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
|
|
||||||
|
|
||||||
if (_isInterceptedSelector(aSelector)) {
|
|
||||||
return _interceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [_target respondsToSelector:aSelector] ? _target : nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark ASCellNode<->UITableViewCell bridging.
|
#pragma mark ASCellNode<->UITableViewCell bridging.
|
||||||
|
|
||||||
@@ -156,13 +76,12 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark ASTableView
|
#pragma mark ASTableView
|
||||||
|
|
||||||
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate> {
|
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor> {
|
||||||
_ASTableViewProxy *_proxyDataSource;
|
ASTableViewProxy *_proxyDataSource;
|
||||||
_ASTableViewProxy *_proxyDelegate;
|
ASTableViewProxy *_proxyDelegate;
|
||||||
|
|
||||||
ASFlowLayoutController *_layoutController;
|
ASFlowLayoutController *_layoutController;
|
||||||
|
|
||||||
@@ -180,6 +99,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
CGFloat _nodesConstrainedWidth;
|
CGFloat _nodesConstrainedWidth;
|
||||||
BOOL _ignoreNodesConstrainedWidthChange;
|
BOOL _ignoreNodesConstrainedWidthChange;
|
||||||
BOOL _queuedNodeHeightUpdate;
|
BOOL _queuedNodeHeightUpdate;
|
||||||
|
BOOL _isDeallocating;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
||||||
@@ -224,6 +144,12 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
// If the initial size is 0, expect a size change very soon which is part of the initial configuration
|
// If the initial size is 0, expect a size change very soon which is part of the initial configuration
|
||||||
// and should not trigger a relayout.
|
// and should not trigger a relayout.
|
||||||
_ignoreNodesConstrainedWidthChange = (_nodesConstrainedWidth == 0);
|
_ignoreNodesConstrainedWidthChange = (_nodesConstrainedWidth == 0);
|
||||||
|
|
||||||
|
_proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||||
|
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
|
||||||
|
|
||||||
|
_proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||||
|
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
|
||||||
|
|
||||||
[self registerClass:_ASTableViewCell.class forCellReuseIdentifier:kCellReuseIdentifier];
|
[self registerClass:_ASTableViewCell.class forCellReuseIdentifier:kCellReuseIdentifier];
|
||||||
}
|
}
|
||||||
@@ -265,9 +191,9 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
// Sometimes the UIKit classes can call back to their delegate even during deallocation.
|
// Sometimes the UIKit classes can call back to their delegate even during deallocation.
|
||||||
// This bug might be iOS 7-specific.
|
_isDeallocating = YES;
|
||||||
super.delegate = nil;
|
[self setAsyncDelegate:nil];
|
||||||
super.dataSource = nil;
|
[self setAsyncDataSource:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
@@ -290,17 +216,19 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||||
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
|
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
|
||||||
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
|
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
|
||||||
// super.dataSource in this case because calls to _ASTableViewProxy will start failing and cause crashes.
|
// super.dataSource in this case because calls to ASTableViewProxy will start failing and cause crashes.
|
||||||
|
|
||||||
|
super.dataSource = nil;
|
||||||
|
|
||||||
if (asyncDataSource == nil) {
|
if (asyncDataSource == nil) {
|
||||||
super.dataSource = nil;
|
|
||||||
_asyncDataSource = nil;
|
_asyncDataSource = nil;
|
||||||
_proxyDataSource = nil;
|
_proxyDataSource = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||||
} else {
|
} else {
|
||||||
_asyncDataSource = asyncDataSource;
|
_asyncDataSource = asyncDataSource;
|
||||||
_proxyDataSource = [[_ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
_proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
||||||
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setAsyncDelegate:(id<ASTableViewDelegate>)asyncDelegate
|
- (void)setAsyncDelegate:(id<ASTableViewDelegate>)asyncDelegate
|
||||||
@@ -308,18 +236,30 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||||
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
|
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
|
||||||
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
|
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
|
||||||
// super.delegate in this case because calls to _ASTableViewProxy will start failing and cause crashes.
|
// super.delegate in this case because calls to ASTableViewProxy will start failing and cause crashes.
|
||||||
|
|
||||||
|
// Order is important here, the asyncDelegate must be callable while nilling super.delegate to avoid random crashes
|
||||||
|
// in UIScrollViewAccessibility.
|
||||||
|
|
||||||
|
super.delegate = nil;
|
||||||
|
|
||||||
if (asyncDelegate == nil) {
|
if (asyncDelegate == nil) {
|
||||||
// order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes
|
|
||||||
// in UIScrollViewAccessibility.
|
|
||||||
super.delegate = nil;
|
|
||||||
_asyncDelegate = nil;
|
_asyncDelegate = nil;
|
||||||
_proxyDelegate = nil;
|
_proxyDelegate = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||||
} else {
|
} else {
|
||||||
_asyncDelegate = asyncDelegate;
|
_asyncDelegate = asyncDelegate;
|
||||||
_proxyDelegate = [[_ASTableViewProxy alloc] initWithTarget:asyncDelegate interceptor:self];
|
_proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
|
||||||
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
|
}
|
||||||
|
|
||||||
|
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy
|
||||||
|
{
|
||||||
|
if (proxy == _proxyDelegate) {
|
||||||
|
[self setAsyncDelegate:nil];
|
||||||
|
} else if (proxy == _proxyDataSource) {
|
||||||
|
[self setAsyncDataSource:nil];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
51
AsyncDisplayKit/Details/ASDelegateProxy.h
Normal file
51
AsyncDisplayKit/Details/ASDelegateProxy.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@class ASDelegateProxy;
|
||||||
|
@protocol ASDelegateProxyInterceptor
|
||||||
|
@required
|
||||||
|
// Called if the target object is discovered to be nil if it had been non-nil at init time.
|
||||||
|
// This happens if the object is deallocated, because the proxy must maintain a weak reference to avoid cycles.
|
||||||
|
// Though the target object may become nil, the interceptor must not; it is assumed the interceptor owns the proxy.
|
||||||
|
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy;
|
||||||
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stand-in for delegates like UITableView or UICollectionView's delegate / dataSource.
|
||||||
|
* Any selectors flagged by "interceptsSelector" are routed to the interceptor object and are not delivered to the target.
|
||||||
|
* Everything else leaves AsyncDisplayKit safely and arrives at the original target object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@interface ASDelegateProxy : NSProxy
|
||||||
|
|
||||||
|
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(id <ASDelegateProxyInterceptor>)interceptor;
|
||||||
|
|
||||||
|
// This method must be overridden by a subclass.
|
||||||
|
- (BOOL)interceptsSelector:(SEL)selector;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASTableView intercepts and/or overrides a few of UITableView's critical data source and delegate methods.
|
||||||
|
*
|
||||||
|
* Any selector included in this function *MUST* be implemented by ASTableView.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@interface ASTableViewProxy : ASDelegateProxy
|
||||||
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASCollectionView intercepts and/or overrides a few of UICollectionView's critical data source and delegate methods.
|
||||||
|
*
|
||||||
|
* Any selector included in this function *MUST* be implemented by ASCollectionView.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@interface ASCollectionViewProxy : ASDelegateProxy
|
||||||
|
@end
|
||||||
117
AsyncDisplayKit/Details/ASDelegateProxy.m
Normal file
117
AsyncDisplayKit/Details/ASDelegateProxy.m
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "ASDelegateProxy.h"
|
||||||
|
#import "ASTableView.h"
|
||||||
|
#import "ASCollectionView.h"
|
||||||
|
#import "ASAssert.h"
|
||||||
|
|
||||||
|
@implementation ASTableViewProxy
|
||||||
|
|
||||||
|
- (BOOL)interceptsSelector:(SEL)selector
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
// handled by ASTableView node<->cell machinery
|
||||||
|
selector == @selector(tableView:cellForRowAtIndexPath:) ||
|
||||||
|
selector == @selector(tableView:heightForRowAtIndexPath:) ||
|
||||||
|
|
||||||
|
// handled by ASRangeController
|
||||||
|
selector == @selector(numberOfSectionsInTableView:) ||
|
||||||
|
selector == @selector(tableView:numberOfRowsInSection:) ||
|
||||||
|
|
||||||
|
// used for ASRangeController visibility updates
|
||||||
|
selector == @selector(tableView:willDisplayCell:forRowAtIndexPath:) ||
|
||||||
|
selector == @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:) ||
|
||||||
|
|
||||||
|
// used for batch fetching API
|
||||||
|
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ASCollectionViewProxy
|
||||||
|
|
||||||
|
- (BOOL)interceptsSelector:(SEL)selector
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
// handled by ASCollectionView node<->cell machinery
|
||||||
|
selector == @selector(collectionView:cellForItemAtIndexPath:) ||
|
||||||
|
selector == @selector(collectionView:layout:sizeForItemAtIndexPath:) ||
|
||||||
|
selector == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) ||
|
||||||
|
|
||||||
|
// handled by ASRangeController
|
||||||
|
selector == @selector(numberOfSectionsInCollectionView:) ||
|
||||||
|
selector == @selector(collectionView:numberOfItemsInSection:) ||
|
||||||
|
|
||||||
|
// used for ASRangeController visibility updates
|
||||||
|
selector == @selector(collectionView:willDisplayCell:forItemAtIndexPath:) ||
|
||||||
|
selector == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) ||
|
||||||
|
|
||||||
|
// used for batch fetching API
|
||||||
|
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ASDelegateProxy {
|
||||||
|
id <NSObject> __weak _target;
|
||||||
|
id <ASDelegateProxyInterceptor> __weak _interceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithTarget:(id <NSObject>)target interceptor:(id <ASDelegateProxyInterceptor>)interceptor
|
||||||
|
{
|
||||||
|
// -[NSProxy init] is undefined
|
||||||
|
if (!self) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASDisplayNodeAssert(interceptor, @"interceptor must not be nil");
|
||||||
|
|
||||||
|
_target = target ? : [NSNull null];
|
||||||
|
_interceptor = interceptor;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)respondsToSelector:(SEL)aSelector
|
||||||
|
{
|
||||||
|
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
|
||||||
|
|
||||||
|
if ([self interceptsSelector:aSelector]) {
|
||||||
|
return YES;
|
||||||
|
} else {
|
||||||
|
// Also return NO if _target has become nil due to zeroing weak reference (or placeholder initialization).
|
||||||
|
return [_target respondsToSelector:aSelector];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)forwardingTargetForSelector:(SEL)aSelector
|
||||||
|
{
|
||||||
|
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
|
||||||
|
|
||||||
|
if ([self interceptsSelector:aSelector]) {
|
||||||
|
return _interceptor;
|
||||||
|
} else {
|
||||||
|
if (_target) {
|
||||||
|
return [_target respondsToSelector:aSelector] ? _target : nil;
|
||||||
|
} else {
|
||||||
|
[_interceptor proxyTargetHasDeallocated:self];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)interceptsSelector:(SEL)selector
|
||||||
|
{
|
||||||
|
ASDisplayNodeAssert(NO, @"This method must be overridden by subclasses.");
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -11,45 +11,38 @@
|
|||||||
#import <AsyncDisplayKit/ASBasicImageDownloader.h>
|
#import <AsyncDisplayKit/ASBasicImageDownloader.h>
|
||||||
|
|
||||||
// Z in the name to delay running until after the test instance is operating normally.
|
// Z in the name to delay running until after the test instance is operating normally.
|
||||||
@interface ASZBasicImageDownloaderTests : XCTestCase
|
@interface ASBasicImageDownloaderTests : XCTestCase
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ASZBasicImageDownloaderTests
|
@implementation ASBasicImageDownloaderTests
|
||||||
|
|
||||||
- (void)testAsynchronouslyDownloadTheSameURLTwice
|
- (void)testAsynchronouslyDownloadTheSameURLTwice
|
||||||
{
|
{
|
||||||
ASBasicImageDownloader *downloader = [ASBasicImageDownloader sharedImageDownloader];
|
ASBasicImageDownloader *downloader = [ASBasicImageDownloader sharedImageDownloader];
|
||||||
|
|
||||||
NSURL *URL = [NSURL URLWithString:@"http://wrongPath/wrongResource.png"];
|
NSURL *URL = [NSURL URLWithString:@"http://wrongPath/wrongResource.png"];
|
||||||
|
|
||||||
dispatch_group_t group = dispatch_group_create();
|
|
||||||
|
|
||||||
__block BOOL firstDone = NO;
|
__block BOOL firstDone = NO;
|
||||||
|
|
||||||
dispatch_group_enter(group);
|
|
||||||
[downloader downloadImageWithURL:URL
|
[downloader downloadImageWithURL:URL
|
||||||
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||||
downloadProgressBlock:nil
|
downloadProgressBlock:nil
|
||||||
completion:^(CGImageRef image, NSError *error) {
|
completion:^(CGImageRef image, NSError *error) {
|
||||||
firstDone = YES;
|
firstDone = YES;
|
||||||
dispatch_group_leave(group);
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
__block BOOL secondDone = NO;
|
__block BOOL secondDone = NO;
|
||||||
|
|
||||||
dispatch_group_enter(group);
|
|
||||||
[downloader downloadImageWithURL:URL
|
[downloader downloadImageWithURL:URL
|
||||||
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||||
downloadProgressBlock:nil
|
downloadProgressBlock:nil
|
||||||
completion:^(CGImageRef image, NSError *error) {
|
completion:^(CGImageRef image, NSError *error) {
|
||||||
secondDone = YES;
|
secondDone = YES;
|
||||||
dispatch_group_leave(group);
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
XCTAssert(0 == dispatch_group_wait(group, dispatch_time(0, 10 * 1000000000)), @"URL loading takes too long");
|
sleep(3);
|
||||||
|
XCTAssert(firstDone && secondDone, @"Not all ASBasicImageDownloader completion handlers have been called after 3 seconds");
|
||||||
XCTAssert(firstDone && secondDone, @"Not all handlers has been called");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A691A11F47200143C57 /* ViewController.m */; };
|
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A691A11F47200143C57 /* ViewController.m */; };
|
||||||
AC3C4A8E1A11F80C00143C57 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AC3C4A8D1A11F80C00143C57 /* Images.xcassets */; };
|
AC3C4A8E1A11F80C00143C57 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AC3C4A8D1A11F80C00143C57 /* Images.xcassets */; };
|
||||||
FABD6D156A3EB118497E5CE6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F02BAF78E68BC56FD8C161B7 /* libPods.a */; };
|
FABD6D156A3EB118497E5CE6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F02BAF78E68BC56FD8C161B7 /* libPods.a */; };
|
||||||
|
FC3FCA801C2B1564009F6D6D /* PresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FC3FCA7F1C2B1564009F6D6D /* PresentingViewController.m */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@@ -34,6 +35,8 @@
|
|||||||
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||||
CD1ABB23007FEDB31D8C1978 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
|
CD1ABB23007FEDB31D8C1978 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
F02BAF78E68BC56FD8C161B7 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
F02BAF78E68BC56FD8C161B7 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
FC3FCA7E1C2B1564009F6D6D /* PresentingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PresentingViewController.h; sourceTree = "<group>"; };
|
||||||
|
FC3FCA7F1C2B1564009F6D6D /* PresentingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PresentingViewController.m; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -82,6 +85,8 @@
|
|||||||
AC3C4A661A11F47200143C57 /* AppDelegate.m */,
|
AC3C4A661A11F47200143C57 /* AppDelegate.m */,
|
||||||
AC3C4A681A11F47200143C57 /* ViewController.h */,
|
AC3C4A681A11F47200143C57 /* ViewController.h */,
|
||||||
AC3C4A691A11F47200143C57 /* ViewController.m */,
|
AC3C4A691A11F47200143C57 /* ViewController.m */,
|
||||||
|
FC3FCA7E1C2B1564009F6D6D /* PresentingViewController.h */,
|
||||||
|
FC3FCA7F1C2B1564009F6D6D /* PresentingViewController.m */,
|
||||||
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */,
|
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */,
|
||||||
AC3C4A611A11F47200143C57 /* Supporting Files */,
|
AC3C4A611A11F47200143C57 /* Supporting Files */,
|
||||||
9B92C87F1BC17D3000EE46B2 /* SupplementaryNode.h */,
|
9B92C87F1BC17D3000EE46B2 /* SupplementaryNode.h */,
|
||||||
@@ -125,7 +130,6 @@
|
|||||||
AC3C4A5B1A11F47200143C57 /* Frameworks */,
|
AC3C4A5B1A11F47200143C57 /* Frameworks */,
|
||||||
AC3C4A5C1A11F47200143C57 /* Resources */,
|
AC3C4A5C1A11F47200143C57 /* Resources */,
|
||||||
A6902C454C7661D0D277AC62 /* Copy Pods Resources */,
|
A6902C454C7661D0D277AC62 /* Copy Pods Resources */,
|
||||||
EC37EEC9933F5786936BFE7C /* Embed Pods Frameworks */,
|
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -196,21 +200,6 @@
|
|||||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
EC37EEC9933F5786936BFE7C /* Embed Pods Frameworks */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "Embed Pods Frameworks";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */ = {
|
F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -236,6 +225,7 @@
|
|||||||
25FDEC921BF31EE700CEB123 /* ItemNode.m in Sources */,
|
25FDEC921BF31EE700CEB123 /* ItemNode.m in Sources */,
|
||||||
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */,
|
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */,
|
||||||
9B92C8811BC17D3000EE46B2 /* SupplementaryNode.m in Sources */,
|
9B92C8811BC17D3000EE46B2 /* SupplementaryNode.m in Sources */,
|
||||||
|
FC3FCA801C2B1564009F6D6D /* PresentingViewController.m in Sources */,
|
||||||
AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */,
|
AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */,
|
||||||
AC3C4A641A11F47200143C57 /* main.m in Sources */,
|
AC3C4A641A11F47200143C57 /* main.m in Sources */,
|
||||||
);
|
);
|
||||||
|
|||||||
10
examples/ASCollectionView/Sample.xcworkspace/contents.xcworkspacedata
generated
Normal file
10
examples/ASCollectionView/Sample.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Sample.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#define SIMULATE_WEB_RESPONSE 0
|
||||||
|
|
||||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||||
|
|
||||||
@property (strong, nonatomic) UIWindow *window;
|
@property (strong, nonatomic) UIWindow *window;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
#import "PresentingViewController.h"
|
||||||
#import "ViewController.h"
|
#import "ViewController.h"
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
@@ -31,10 +32,14 @@
|
|||||||
- (void)pushNewViewControllerAnimated:(BOOL)animated
|
- (void)pushNewViewControllerAnimated:(BOOL)animated
|
||||||
{
|
{
|
||||||
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
|
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
|
||||||
|
|
||||||
|
#if SIMULATE_WEB_RESPONSE
|
||||||
|
UIViewController *viewController = [[PresentingViewController alloc] init];
|
||||||
|
#else
|
||||||
UIViewController *viewController = [[ViewController alloc] init];
|
UIViewController *viewController = [[ViewController alloc] init];
|
||||||
viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Another Copy" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)];
|
viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Another Copy" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)];
|
||||||
|
#endif
|
||||||
|
|
||||||
[navController pushViewController:viewController animated:animated];
|
[navController pushViewController:viewController animated:animated];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
examples/ASCollectionView/Sample/PresentingViewController.h
Normal file
13
examples/ASCollectionView/Sample/PresentingViewController.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// PresentingViewController.h
|
||||||
|
// Sample
|
||||||
|
//
|
||||||
|
// Created by Tom King on 12/23/15.
|
||||||
|
// Copyright © 2015 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface PresentingViewController : UIViewController
|
||||||
|
|
||||||
|
@end
|
||||||
30
examples/ASCollectionView/Sample/PresentingViewController.m
Normal file
30
examples/ASCollectionView/Sample/PresentingViewController.m
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// PresentingViewController.m
|
||||||
|
// Sample
|
||||||
|
//
|
||||||
|
// Created by Tom King on 12/23/15.
|
||||||
|
// Copyright © 2015 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "PresentingViewController.h"
|
||||||
|
#import "ViewController.h"
|
||||||
|
|
||||||
|
@interface PresentingViewController ()
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation PresentingViewController
|
||||||
|
|
||||||
|
- (void)viewDidLoad
|
||||||
|
{
|
||||||
|
[super viewDidLoad];
|
||||||
|
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Details" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pushNewViewController
|
||||||
|
{
|
||||||
|
ViewController *controller = [[ViewController alloc] init];
|
||||||
|
[self.navigationController pushViewController:controller animated:true];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
@interface ViewController () <ASCollectionViewDataSource, ASCollectionViewDelegateFlowLayout>
|
@interface ViewController () <ASCollectionViewDataSource, ASCollectionViewDelegateFlowLayout>
|
||||||
{
|
{
|
||||||
ASCollectionView *_collectionView;
|
ASCollectionView *_collectionView;
|
||||||
|
NSArray *_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
layout.headerReferenceSize = CGSizeMake(50.0, 50.0);
|
layout.headerReferenceSize = CGSizeMake(50.0, 50.0);
|
||||||
layout.footerReferenceSize = CGSizeMake(50.0, 50.0);
|
layout.footerReferenceSize = CGSizeMake(50.0, 50.0);
|
||||||
|
|
||||||
_collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:YES];
|
_collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||||
_collectionView.asyncDataSource = self;
|
_collectionView.asyncDataSource = self;
|
||||||
_collectionView.asyncDelegate = self;
|
_collectionView.asyncDelegate = self;
|
||||||
_collectionView.backgroundColor = [UIColor whiteColor];
|
_collectionView.backgroundColor = [UIColor whiteColor];
|
||||||
@@ -45,8 +46,10 @@
|
|||||||
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||||
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
|
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
|
||||||
|
|
||||||
|
#if !SIMULATE_WEB_RESPONSE
|
||||||
self.navigationItem.leftItemsSupplementBackButton = YES;
|
self.navigationItem.leftItemsSupplementBackButton = YES;
|
||||||
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadTapped)];
|
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadTapped)];
|
||||||
|
#endif
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -56,6 +59,31 @@
|
|||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
|
|
||||||
[self.view addSubview:_collectionView];
|
[self.view addSubview:_collectionView];
|
||||||
|
|
||||||
|
#if SIMULATE_WEB_RESPONSE
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
|
void(^mockWebService)() = ^{
|
||||||
|
NSLog(@"ViewController \"got data from a web service\"");
|
||||||
|
ViewController *strongSelf = weakSelf;
|
||||||
|
if (strongSelf != nil)
|
||||||
|
{
|
||||||
|
NSLog(@"ViewController is not nil");
|
||||||
|
strongSelf->_data = [[NSArray alloc] init];
|
||||||
|
[strongSelf->_collectionView performBatchUpdates:^{
|
||||||
|
[strongSelf->_collectionView insertSections:[[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, 100)]];
|
||||||
|
} completion:nil];
|
||||||
|
NSLog(@"ViewController finished updating collectionView");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NSLog(@"ViewController is nil - won't update collectionView");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), mockWebService);
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
|
[self.navigationController popViewControllerAnimated:YES];
|
||||||
|
});
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewWillLayoutSubviews
|
- (void)viewWillLayoutSubviews
|
||||||
@@ -101,7 +129,11 @@
|
|||||||
|
|
||||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
|
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
|
||||||
{
|
{
|
||||||
|
#if SIMULATE_WEB_RESPONSE
|
||||||
|
return _data == nil ? 0 : 100;
|
||||||
|
#else
|
||||||
return 100;
|
return 100;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView
|
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView
|
||||||
@@ -125,4 +157,11 @@
|
|||||||
return UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0);
|
return UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SIMULATE_WEB_RESPONSE
|
||||||
|
-(void)dealloc
|
||||||
|
{
|
||||||
|
NSLog(@"ViewController is deallocing");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
Reference in New Issue
Block a user