mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
Merge branch 'master' into pr/664
This commit is contained in:
Binary file not shown.
@@ -128,7 +128,7 @@
|
||||
242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; };
|
||||
251B8EF71BBB3D690087C538 /* ASCollectionDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */; };
|
||||
251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; };
|
||||
251B8EF91BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; };
|
||||
251B8EF91BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; };
|
||||
251B8EFB1BBB3D690087C538 /* ASDataController+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */; };
|
||||
2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */; };
|
||||
@@ -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 */; };
|
||||
@@ -458,8 +458,13 @@
|
||||
D785F6621A74327E00291744 /* ASScrollNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D785F6601A74327E00291744 /* ASScrollNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D785F6631A74327E00291744 /* ASScrollNode.m in Sources */ = {isa = PBXBuildFile; fileRef = D785F6611A74327E00291744 /* ASScrollNode.m */; };
|
||||
DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; };
|
||||
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 */; };
|
||||
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
|
||||
DE8BEAC11C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */; };
|
||||
DE8BEAC21C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */; };
|
||||
DE8BEAC31C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */; };
|
||||
DE8BEAC41C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */; };
|
||||
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, ); }; };
|
||||
DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; };
|
||||
@@ -753,6 +758,8 @@
|
||||
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>"; };
|
||||
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+FrameworkPrivate.h"; sourceTree = "<group>"; };
|
||||
DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDelegateProxy.h; sourceTree = "<group>"; };
|
||||
DE8BEAC01C2DF3FC00D57C12 /* 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>"; };
|
||||
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; };
|
||||
@@ -1148,6 +1155,8 @@
|
||||
25B171EA1C12242700508A7A /* Data Controller */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */,
|
||||
DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */,
|
||||
251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */,
|
||||
251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */,
|
||||
464052191A3F83C40061C0BA /* ASDataController.h */,
|
||||
@@ -1263,6 +1272,7 @@
|
||||
18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
|
||||
257754C01BEE458E00737CA5 /* ASTextNodeWordKerner.h in Headers */,
|
||||
AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */,
|
||||
DE8BEAC11C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */,
|
||||
205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */,
|
||||
AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */,
|
||||
058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */,
|
||||
@@ -1431,6 +1441,7 @@
|
||||
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */,
|
||||
B350625C1B010F070018CF92 /* ASLog.h in Headers */,
|
||||
0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
|
||||
DE8BEAC21C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */,
|
||||
B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */,
|
||||
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */,
|
||||
B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */,
|
||||
@@ -1451,6 +1462,7 @@
|
||||
B35062551B010EFD0018CF92 /* ASSentinel.h in Headers */,
|
||||
9C8221961BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */,
|
||||
9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */,
|
||||
DE040EF91C2B40AC004692FF /* ASCollectionViewFlowLayoutInspector.h in Headers */,
|
||||
34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */,
|
||||
254C6B7B1BF94DF4003EC431 /* ASTextKitRenderer+Positioning.h in Headers */,
|
||||
CC7FD9E21BB603FF005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */,
|
||||
@@ -1523,7 +1535,6 @@
|
||||
058D09B9195D04C000B7D73C /* Frameworks */,
|
||||
058D09BA195D04C000B7D73C /* Resources */,
|
||||
3B9D88CDF51B429C8409E4B6 /* Copy Pods Resources */,
|
||||
1B86F48711505F91D5FEF571 /* Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -1623,21 +1634,6 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
1B86F48711505F91D5FEF571 /* 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-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
2E61B6A0DB0F436A9DDBE86F /* Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -1701,6 +1697,7 @@
|
||||
0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */,
|
||||
299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */,
|
||||
AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */,
|
||||
DE8BEAC31C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */,
|
||||
ACF6ED1D1B17843500DA7C62 /* ASCenterLayoutSpec.mm in Sources */,
|
||||
18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m in Sources */,
|
||||
92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */,
|
||||
@@ -1830,6 +1827,7 @@
|
||||
509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */,
|
||||
254C6B861BF94F8A003EC431 /* ASTextKitContext.mm in Sources */,
|
||||
34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */,
|
||||
DE8BEAC41C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */,
|
||||
B35062141B010EFD0018CF92 /* ASBasicImageDownloader.mm in Sources */,
|
||||
B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */,
|
||||
AC47D9421B3B891B00AAEE9D /* ASCellNode.m in Sources */,
|
||||
@@ -1936,6 +1934,7 @@
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
@@ -1956,6 +1955,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
|
||||
INFOPLIST_FILE = AsyncDisplayKitTestHost/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
@@ -2043,6 +2043,7 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
DSTROOT = /tmp/AsyncDisplayKit.dst;
|
||||
GCC_INPUT_FILETYPE = automatic;
|
||||
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
@@ -2061,6 +2062,7 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
DSTROOT = /tmp/AsyncDisplayKit.dst;
|
||||
GCC_INPUT_FILETYPE = automatic;
|
||||
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
@@ -2083,6 +2085,7 @@
|
||||
"$(inherited)",
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
);
|
||||
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
@@ -2110,6 +2113,7 @@
|
||||
"$(inherited)",
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
);
|
||||
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
@@ -2138,6 +2142,7 @@
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
@@ -2170,6 +2175,7 @@
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit-iOS/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
|
||||
@@ -31,6 +31,17 @@ typedef enum : NSUInteger {
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL laysOutHorizontally;
|
||||
|
||||
/** Horizontally align content (text or image).
|
||||
Defaults to ASAlignmentMiddle.
|
||||
*/
|
||||
@property (nonatomic, assign) ASHorizontalAlignment contentHorizontalAlignment;
|
||||
|
||||
/** Vertically align content (text or image).
|
||||
Defaults to ASAlignmentCenter.
|
||||
*/
|
||||
@property (nonatomic, assign) ASVerticalAlignment contentVerticalAlignment;
|
||||
|
||||
|
||||
- (NSAttributedString *)attributedTitleForState:(ASButtonState)state;
|
||||
- (void)setAttributedTitle:(NSAttributedString *)title forState:(ASButtonState)state;
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
_titleNode = [[ASTextNode alloc] init];
|
||||
_imageNode = [[ASImageNode alloc] init];
|
||||
|
||||
_contentHorizontalAlignment = ASAlignmentMiddle;
|
||||
_contentVerticalAlignment = ASAlignmentCenter;
|
||||
|
||||
[self addSubnode:_titleNode];
|
||||
[self addSubnode:_imageNode];
|
||||
|
||||
@@ -195,8 +198,8 @@
|
||||
ASStackLayoutSpec *stack = [[ASStackLayoutSpec alloc] init];
|
||||
stack.direction = self.laysOutHorizontally ? ASStackLayoutDirectionHorizontal : ASStackLayoutDirectionVertical;
|
||||
stack.spacing = self.contentSpacing;
|
||||
stack.justifyContent = ASStackLayoutJustifyContentCenter;
|
||||
stack.alignItems = ASStackLayoutAlignItemsCenter;
|
||||
stack.horizontalAlignment = _contentHorizontalAlignment;
|
||||
stack.verticalAlignment = _contentVerticalAlignment;
|
||||
|
||||
NSMutableArray *children = [[NSMutableArray alloc] initWithCapacity:2];
|
||||
if (self.imageNode.image) {
|
||||
|
||||
@@ -16,7 +16,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@interface ASCollectionNode : ASDisplayNode
|
||||
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout NS_DESIGNATED_INITIALIZER;
|
||||
- (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,6 +9,22 @@
|
||||
#import "ASCollectionNode.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
|
||||
@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
|
||||
|
||||
@implementation ASCollectionNode
|
||||
|
||||
- (instancetype)init
|
||||
@@ -21,17 +37,92 @@
|
||||
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
if (self = [super initWithViewBlock:^UIView *{ return [[ASCollectionView alloc] initWithCollectionViewLayout:layout]; }]) {
|
||||
return [self initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)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];
|
||||
}
|
||||
|
||||
- (void)visibilityDidChange:(BOOL)isVisible
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)clearContents
|
||||
{
|
||||
[super clearContents];
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
#import <AsyncDisplayKit/ASCollectionViewProtocols.h>
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
#import <AsyncDisplayKit/ASBatchContext.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASCollectionViewFlowLayoutInspector.h>
|
||||
|
||||
@class ASCellNode;
|
||||
@protocol ASCollectionViewDataSource;
|
||||
@protocol ASCollectionViewDelegate;
|
||||
@protocol ASCollectionDataSource;
|
||||
@protocol ASCollectionDelegate;
|
||||
@protocol ASCollectionViewLayoutInspecting;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@@ -29,10 +29,16 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@interface ASCollectionView : UICollectionView
|
||||
|
||||
/**
|
||||
* Initializer.
|
||||
*
|
||||
* @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.
|
||||
*/
|
||||
- (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.
|
||||
@@ -54,27 +60,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
/**
|
||||
* Initializer.
|
||||
*
|
||||
* @param frame The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview
|
||||
* in which you plan to add it. This frame is passed to the superclass during initialization.
|
||||
*
|
||||
* @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object.
|
||||
* Must not be nil.
|
||||
*
|
||||
* @param asyncDataFetchingEnabled Enable the data fetching in async mode.
|
||||
*
|
||||
* @discussion If asyncDataFetching is enabled, the `ASCollectionView` will fetch data through `collectionView:numberOfRowsInSection:` and
|
||||
* `collectionView:nodeForRowAtIndexPath:` in async mode from background thread. Otherwise, the methods will be invoked synchronically
|
||||
* from calling thread.
|
||||
* Enabling asyncDataFetching could avoid blocking main thread for `ASCellNode` allocation, which is frequently reported issue for
|
||||
* large scale data. On another hand, the application code need take the responsibility to avoid data inconsistence. Specifically,
|
||||
* we will lock the data source through `collectionViewLockDataSource`, and unlock it by `collectionViewUnlockDataSource` after the data fetching.
|
||||
* The application should not update the data source while the data source is locked, to keep data consistence.
|
||||
*/
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled;
|
||||
|
||||
/**
|
||||
* The number of screens left to scroll before the delegate -collectionView:beginBatchFetchingWithContext: is called.
|
||||
*
|
||||
@@ -303,7 +288,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* This is a node-based UICollectionViewDataSource.
|
||||
*/
|
||||
@protocol ASCollectionViewDataSource <ASCommonCollectionViewDataSource, NSObject>
|
||||
#define ASCollectionViewDataSource ASCollectionDataSource
|
||||
@protocol ASCollectionDataSource <ASCommonCollectionViewDataSource, NSObject>
|
||||
|
||||
/**
|
||||
* Similar to -collectionView:cellForItemAtIndexPath:.
|
||||
@@ -364,7 +350,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* This is a node-based UICollectionViewDelegate.
|
||||
*/
|
||||
@protocol ASCollectionViewDelegate <ASCommonCollectionViewDelegate, NSObject>
|
||||
#define ASCollectionViewDelegate ASCollectionDelegate
|
||||
@protocol ASCollectionDelegate <ASCommonCollectionViewDelegate, NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
@@ -433,4 +420,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionView (Deprecated)
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
#import "ASAssert.h"
|
||||
#import "ASBatchFetching.h"
|
||||
#import "ASDelegateProxy.h"
|
||||
#import "ASCollectionView.h"
|
||||
#import "ASCollectionNode.h"
|
||||
#import "ASCollectionDataController.h"
|
||||
#import "ASCollectionViewLayoutController.h"
|
||||
#import "ASCollectionViewFlowLayoutInspector.h"
|
||||
@@ -21,87 +23,6 @@ static const NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimation
|
||||
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
|
||||
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 ASCellNode<->UICollectionViewCell bridging.
|
||||
|
||||
@@ -137,24 +58,25 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
#pragma mark -
|
||||
#pragma mark ASCollectionView.
|
||||
|
||||
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate> {
|
||||
_ASCollectionViewProxy *_proxyDataSource;
|
||||
_ASCollectionViewProxy *_proxyDelegate;
|
||||
|
||||
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor> {
|
||||
ASCollectionViewProxy *_proxyDataSource;
|
||||
ASCollectionViewProxy *_proxyDelegate;
|
||||
|
||||
ASCollectionDataController *_dataController;
|
||||
ASRangeController *_rangeController;
|
||||
ASCollectionViewLayoutController *_layoutController;
|
||||
ASCollectionViewFlowLayoutInspector *_flowLayoutInspector;
|
||||
|
||||
|
||||
BOOL _performingBatchUpdates;
|
||||
NSMutableArray *_batchUpdateBlocks;
|
||||
|
||||
|
||||
BOOL _asyncDataFetchingEnabled;
|
||||
BOOL _asyncDelegateImplementsInsetSection;
|
||||
BOOL _collectionViewLayoutImplementsInsetSection;
|
||||
BOOL _asyncDataSourceImplementsConstrainedSizeForNode;
|
||||
BOOL _queuedNodeSizeUpdate;
|
||||
|
||||
BOOL _isDeallocating;
|
||||
|
||||
ASBatchContext *_batchContext;
|
||||
|
||||
CGSize _maxSizeForNodesConstrainedSize;
|
||||
@@ -172,7 +94,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
* You will get an assertion failure saying `Invalid number of items in section 0.
|
||||
* The number of items after the update (1) must be equal to the number of items before the update (1) plus or minus the items added and removed (1 added, 0 removed).`
|
||||
* The collection view never queried your data source before the update to see that it actually had 0 items.
|
||||
*/
|
||||
*/
|
||||
BOOL _superIsPendingDataLoad;
|
||||
}
|
||||
|
||||
@@ -187,48 +109,51 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
return [self initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO];
|
||||
return [self initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
return [self initWithFrame:frame collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionNode *collectionNode = [[ASCollectionNode alloc] initWithFrame:frame collectionViewLayout:layout];
|
||||
return collectionNode.view;
|
||||
}
|
||||
|
||||
// FIXME: This method is deprecated and will probably be removed in or shortly after 2.0.
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled
|
||||
{
|
||||
return [self initWithFrame:frame collectionViewLayout:layout];
|
||||
}
|
||||
|
||||
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
if (!(self = [super initWithFrame:frame collectionViewLayout:layout]))
|
||||
return nil;
|
||||
|
||||
// FIXME: asyncDataFetching is currently unreliable for some use cases.
|
||||
// https://github.com/facebook/AsyncDisplayKit/issues/385
|
||||
asyncDataFetchingEnabled = NO;
|
||||
|
||||
_layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self];
|
||||
|
||||
|
||||
_rangeController = [[ASRangeController alloc] init];
|
||||
_rangeController.dataSource = self;
|
||||
_rangeController.delegate = self;
|
||||
_rangeController.layoutController = _layoutController;
|
||||
|
||||
_dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled];
|
||||
|
||||
_dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:NO];
|
||||
_dataController.delegate = _rangeController;
|
||||
_dataController.dataSource = self;
|
||||
|
||||
_batchContext = [[ASBatchContext alloc] init];
|
||||
|
||||
|
||||
_leadingScreensForBatching = 1.0;
|
||||
|
||||
_asyncDataFetchingEnabled = asyncDataFetchingEnabled;
|
||||
|
||||
_asyncDataFetchingEnabled = NO;
|
||||
_asyncDataSourceLocked = NO;
|
||||
|
||||
|
||||
_performingBatchUpdates = NO;
|
||||
_batchUpdateBlocks = [NSMutableArray array];
|
||||
|
||||
|
||||
_superIsPendingDataLoad = YES;
|
||||
|
||||
_collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)];
|
||||
|
||||
|
||||
_maxSizeForNodesConstrainedSize = self.bounds.size;
|
||||
// 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.
|
||||
@@ -240,6 +165,12 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
_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];
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
@@ -251,10 +182,10 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// Sometimes the UIKit classes can call back to their delegate even during deallocation.
|
||||
// This bug might be iOS 7-specific.
|
||||
super.delegate = nil;
|
||||
super.dataSource = nil;
|
||||
// Sometimes the UIKit classes can call back to their delegate even during deallocation, due to animation completion blocks etc.
|
||||
_isDeallocating = YES;
|
||||
[self setAsyncDelegate:nil];
|
||||
[self setAsyncDataSource:nil];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,13 +193,13 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
*/
|
||||
- (ASCollectionViewFlowLayoutInspector *)flowLayoutInspector
|
||||
{
|
||||
if (_flowLayoutInspector == nil) {
|
||||
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout;
|
||||
ASDisplayNodeAssertNotNil(layout, @"Collection view layout must be a flow layout to use the built-in inspector");
|
||||
_flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:self
|
||||
flowLayout:layout];
|
||||
}
|
||||
return _flowLayoutInspector;
|
||||
if (_flowLayoutInspector == nil) {
|
||||
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout;
|
||||
ASDisplayNodeAssertNotNil(layout, @"Collection view layout must be a flow layout to use the built-in inspector");
|
||||
_flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:self
|
||||
flowLayout:layout];
|
||||
}
|
||||
return _flowLayoutInspector;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@@ -308,24 +239,35 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
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
|
||||
{
|
||||
// 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
|
||||
// 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) {
|
||||
super.dataSource = nil;
|
||||
_asyncDataSource = nil;
|
||||
_proxyDataSource = nil;
|
||||
_proxyDataSource = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||
_asyncDataSourceImplementsConstrainedSizeForNode = NO;
|
||||
} else {
|
||||
_asyncDataSource = asyncDataSource;
|
||||
_proxyDataSource = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
||||
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
|
||||
_proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
||||
_asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0);
|
||||
}
|
||||
|
||||
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
|
||||
}
|
||||
|
||||
- (void)setAsyncDelegate:(id<ASCollectionViewDelegate>)asyncDelegate
|
||||
@@ -333,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
|
||||
// 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
|
||||
// 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) {
|
||||
// order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes
|
||||
// in UIScrollViewAccessibility.
|
||||
super.delegate = nil;
|
||||
_asyncDelegate = nil;
|
||||
_proxyDelegate = nil;
|
||||
_proxyDelegate = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||
_asyncDelegateImplementsInsetSection = NO;
|
||||
} else {
|
||||
_asyncDelegate = asyncDelegate;
|
||||
_proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
|
||||
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
|
||||
_proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
|
||||
_asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
|
||||
|
||||
[_layoutInspector didChangeCollectionViewDelegate:asyncDelegate];
|
||||
}
|
||||
|
||||
@@ -409,7 +354,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
|
||||
[_dataController beginUpdates];
|
||||
updates();
|
||||
[_dataController endUpdatesAnimated:animated completion:completion];
|
||||
@@ -487,7 +432,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
_ASCollectionViewCell *cell = [self dequeueReusableCellWithReuseIdentifier:kCellReuseIdentifier forIndexPath:indexPath];
|
||||
|
||||
|
||||
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
|
||||
cell.node = node;
|
||||
[_rangeController configureContentView:cell.contentView forCellNode:node];
|
||||
@@ -524,7 +469,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
CGPoint scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview];
|
||||
return [self scrollDirectionForVelocity:scrollVelocity];
|
||||
}
|
||||
|
||||
|
||||
- (ASScrollDirection)scrollDirectionForVelocity:(CGPoint)scrollVelocity
|
||||
{
|
||||
ASScrollDirection direction = ASScrollDirectionNone;
|
||||
@@ -544,7 +489,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
direction |= ASScrollDirectionUp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return direction;
|
||||
}
|
||||
|
||||
@@ -623,7 +568,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
|
||||
{
|
||||
[self handleBatchFetchScrollingToOffset:*targetContentOffset];
|
||||
|
||||
|
||||
if ([_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) {
|
||||
[_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
|
||||
}
|
||||
@@ -643,11 +588,11 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)handleBatchFetchScrollingToOffset:(CGPoint)targetOffset
|
||||
{
|
||||
ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist");
|
||||
|
||||
|
||||
if (![self shouldBatchFetch]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollDirection], self.bounds, self.contentSize, targetOffset, _leadingScreensForBatching)) {
|
||||
[_batchContext beginBatchFetching];
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
@@ -695,7 +640,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
}
|
||||
constrainedSize = ASSizeRangeMake(CGSizeZero, maxSize);
|
||||
}
|
||||
|
||||
|
||||
UIEdgeInsets sectionInset = UIEdgeInsetsZero;
|
||||
if (_collectionViewLayoutImplementsInsetSection) {
|
||||
sectionInset = [(UICollectionViewFlowLayout *)self.collectionViewLayout sectionInset];
|
||||
@@ -704,7 +649,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
if (_asyncDelegateImplementsInsetSection) {
|
||||
sectionInset = [(id<ASCollectionViewDelegateFlowLayout>)_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section];
|
||||
}
|
||||
|
||||
|
||||
if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) {
|
||||
constrainedSize.min.width = MAX(0, constrainedSize.min.width - sectionInset.left - sectionInset.right);
|
||||
//ignore insets for FLT_MAX so FLT_MAX can be compared against
|
||||
@@ -718,7 +663,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
constrainedSize.max.height = MAX(0, constrainedSize.max.height - sectionInset.top - sectionInset.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return constrainedSize;
|
||||
}
|
||||
|
||||
@@ -738,7 +683,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)dataControllerLockDataSource
|
||||
{
|
||||
ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has already been locked");
|
||||
|
||||
|
||||
self.asyncDataSourceLocked = YES;
|
||||
if ([_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSource:)]) {
|
||||
[_asyncDataSource collectionViewLockDataSource:self];
|
||||
@@ -748,7 +693,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)dataControllerUnlockDataSource
|
||||
{
|
||||
ASDisplayNodeAssert(self.asyncDataSourceLocked, @"The data source has already been unlocked");
|
||||
|
||||
|
||||
self.asyncDataSourceLocked = NO;
|
||||
if ([_asyncDataSource respondsToSelector:@selector(collectionViewUnlockDataSource:)]) {
|
||||
[_asyncDataSource collectionViewUnlockDataSource:self];
|
||||
@@ -817,7 +762,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)rangeController:(ASRangeController *)rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
|
||||
if (!self.asyncDataSource || _superIsPendingDataLoad) {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
@@ -832,7 +777,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
}
|
||||
} completion:completion];
|
||||
});
|
||||
|
||||
|
||||
[_batchUpdateBlocks removeAllObjects];
|
||||
_performingBatchUpdates = NO;
|
||||
}
|
||||
@@ -840,11 +785,11 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
|
||||
if (!self.asyncDataSource || _superIsPendingDataLoad) {
|
||||
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
||||
}
|
||||
|
||||
|
||||
if (_performingBatchUpdates) {
|
||||
[_batchUpdateBlocks addObject:^{
|
||||
[super insertItemsAtIndexPaths:indexPaths];
|
||||
@@ -859,11 +804,11 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
|
||||
if (!self.asyncDataSource || _superIsPendingDataLoad) {
|
||||
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
||||
}
|
||||
|
||||
|
||||
if (_performingBatchUpdates) {
|
||||
[_batchUpdateBlocks addObject:^{
|
||||
[super deleteItemsAtIndexPaths:indexPaths];
|
||||
@@ -878,11 +823,11 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
|
||||
if (!self.asyncDataSource || _superIsPendingDataLoad) {
|
||||
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
||||
}
|
||||
|
||||
|
||||
if (_performingBatchUpdates) {
|
||||
[_batchUpdateBlocks addObject:^{
|
||||
[super insertSections:indexSet];
|
||||
@@ -897,11 +842,11 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
|
||||
if (!self.asyncDataSource || _superIsPendingDataLoad) {
|
||||
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
||||
}
|
||||
|
||||
|
||||
if (_performingBatchUpdates) {
|
||||
[_batchUpdateBlocks addObject:^{
|
||||
[super deleteSections:indexSet];
|
||||
@@ -918,11 +863,11 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
|
||||
if (!sizeChanged || _queuedNodeSizeUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_queuedNodeSizeUpdate = YES;
|
||||
[self performSelector:@selector(requeryNodeSizes)
|
||||
withObject:nil
|
||||
@@ -934,7 +879,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)requeryNodeSizes
|
||||
{
|
||||
_queuedNodeSizeUpdate = NO;
|
||||
|
||||
|
||||
[super performBatchUpdates:^{} completion:nil];
|
||||
}
|
||||
|
||||
@@ -944,7 +889,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
{
|
||||
for (NSArray *section in [_dataController completedNodes]) {
|
||||
for (ASDisplayNode *node in section) {
|
||||
[node recursivelyClearContents];
|
||||
[node exitInterfaceState:ASInterfaceStateDisplay];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -953,7 +898,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
{
|
||||
for (NSArray *section in [_dataController completedNodes]) {
|
||||
for (ASDisplayNode *node in section) {
|
||||
[node recursivelyClearFetchedData];
|
||||
[node exitInterfaceState:ASInterfaceStateFetchData];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,6 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
_controlEventDispatchTable = [[NSMutableDictionary alloc] initWithCapacity:kASControlNodeEventDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries.
|
||||
_enabled = YES;
|
||||
|
||||
// As we have no targets yet, we start off with user interaction off. When a target is added, it'll get turned back on.
|
||||
@@ -214,6 +213,10 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
// Convert nil to [NSNull null] so that it can be used as a key for NSMapTable.
|
||||
if (!target)
|
||||
target = [NSNull null];
|
||||
|
||||
if (!_controlEventDispatchTable) {
|
||||
_controlEventDispatchTable = [[NSMutableDictionary alloc] initWithCapacity:kASControlNodeEventDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries.
|
||||
}
|
||||
|
||||
// Enumerate the events in the mask, adding the target-action pair for each control event included in controlEventMask
|
||||
_ASEnumerateControlEventsIncludedInMaskWithBlock(controlEventMask, ^
|
||||
|
||||
@@ -39,35 +39,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ASDisplayNode (Subclassing)
|
||||
|
||||
|
||||
/** @name View Configuration */
|
||||
|
||||
|
||||
/**
|
||||
* @return The view class to use when creating a new display node instance. Defaults to _ASDisplayView.
|
||||
*/
|
||||
+ (Class)viewClass;
|
||||
|
||||
|
||||
/** @name Properties */
|
||||
|
||||
|
||||
/**
|
||||
* @abstract The scale factor to apply to the rendering.
|
||||
*
|
||||
* @discussion Use setNeedsDisplayAtScale: to set a value and then after display, the display node will set the layer's
|
||||
* contentsScale. This is to prevent jumps when re-rasterizing at a different contentsScale.
|
||||
* Read this property if you need to know the future contentsScale of your layer, eg in drawParameters.
|
||||
*
|
||||
* @see setNeedsDisplayAtScale:
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) CGFloat contentsScaleForDisplay;
|
||||
|
||||
/**
|
||||
* @abstract Whether the view or layer of this display node is currently in a window
|
||||
*/
|
||||
@property (nonatomic, readonly, assign, getter=isInHierarchy) BOOL inHierarchy;
|
||||
|
||||
/**
|
||||
* @abstract Return the calculated layout.
|
||||
*
|
||||
@@ -192,10 +165,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*
|
||||
* @note Called on the display queue and/or main queue (MUST BE THREAD SAFE)
|
||||
*/
|
||||
+ (void)drawRect:(CGRect)bounds
|
||||
withParameters:(nullable id<NSObject>)parameters
|
||||
isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock
|
||||
isRasterizing:(BOOL)isRasterizing;
|
||||
+ (void)drawRect:(CGRect)bounds withParameters:(nullable id <NSObject>)parameters
|
||||
isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock
|
||||
isRasterizing:(BOOL)isRasterizing;
|
||||
|
||||
/**
|
||||
* @summary Delegate override to provide new layer contents as a UIImage.
|
||||
@@ -238,6 +210,33 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)displayDidFinish ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
/** @name Observing node-related changes */
|
||||
|
||||
/**
|
||||
* @abstract Called whenever any bit in the ASInterfaceState bitfield is changed.
|
||||
*
|
||||
* @discussion Subclasses may use this to monitor when they become visible, should free cached data, and much more.
|
||||
* @see ASInterfaceState
|
||||
*/
|
||||
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState;
|
||||
|
||||
- (void)visibilityDidChange:(BOOL)isVisible;
|
||||
|
||||
/**
|
||||
* Called just before the view is added to a window.
|
||||
*/
|
||||
- (void)willEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* Called after the view is removed from the window.
|
||||
*/
|
||||
- (void)didExitHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* @abstract Whether the view or layer of this display node is currently in a window
|
||||
*/
|
||||
@property (nonatomic, readonly, assign, getter=isInHierarchy) BOOL inHierarchy;
|
||||
|
||||
/**
|
||||
* @abstract Indicates that the node should fetch any external data, such as images.
|
||||
*
|
||||
@@ -247,6 +246,23 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)fetchData ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node.
|
||||
*
|
||||
* @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearFetchedData or
|
||||
* selectively clear fetched data.
|
||||
*/
|
||||
- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers
|
||||
* on the current node.
|
||||
*
|
||||
* @discussion Called by -recursivelyClearContents. Base class implements self.contents = nil, clearing any backing
|
||||
* store, for asynchronous regeneration when needed.
|
||||
*/
|
||||
- (void)clearContents ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* @abstract Indicates that the receiver is about to display its subnodes. This method is not called if there are no
|
||||
* subnodes present.
|
||||
@@ -269,7 +285,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
|
||||
/**
|
||||
* @abstract Marks the receiver's bounds as needing to be redrawn, with a scale value.
|
||||
*
|
||||
@@ -297,6 +312,17 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale;
|
||||
|
||||
/**
|
||||
* @abstract The scale factor to apply to the rendering.
|
||||
*
|
||||
* @discussion Use setNeedsDisplayAtScale: to set a value and then after display, the display node will set the layer's
|
||||
* contentsScale. This is to prevent jumps when re-rasterizing at a different contentsScale.
|
||||
* Read this property if you need to know the future contentsScale of your layer, eg in drawParameters.
|
||||
*
|
||||
* @see setNeedsDisplayAtScale:
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) CGFloat contentsScaleForDisplay;
|
||||
|
||||
|
||||
/** @name Touch handling */
|
||||
|
||||
@@ -363,38 +389,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
|
||||
|
||||
|
||||
/** @name Observing node-related changes */
|
||||
|
||||
|
||||
/**
|
||||
* Called just before the view is added to a window.
|
||||
*/
|
||||
- (void)willEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* Called after the view is removed from the window.
|
||||
*/
|
||||
- (void)didExitHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers
|
||||
* on the current node.
|
||||
*
|
||||
* @discussion Called by -recursivelyClearContents. Base class implements self.contents = nil, clearing any backing
|
||||
* store, for asynchronous regeneration when needed.
|
||||
*/
|
||||
- (void)clearContents ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node.
|
||||
*
|
||||
* @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearFetchedData or
|
||||
* selectively clear fetched data.
|
||||
*/
|
||||
- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER;
|
||||
|
||||
|
||||
/** @name Placeholders */
|
||||
|
||||
/**
|
||||
@@ -414,6 +408,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (nullable UIImage *)placeholderImage;
|
||||
|
||||
|
||||
/** @name Description */
|
||||
|
||||
/**
|
||||
|
||||
@@ -1470,9 +1470,9 @@ static NSInteger incrementIfFound(NSInteger i) {
|
||||
}
|
||||
|
||||
// Helper method to summarize whether or not the node run through the display process
|
||||
- (BOOL)_implementsDisplay
|
||||
- (BOOL)__implementsDisplay
|
||||
{
|
||||
return _flags.implementsDrawRect == YES || _flags.implementsImageDisplay == YES;
|
||||
return _flags.implementsDrawRect == YES || _flags.implementsImageDisplay == YES || self.shouldRasterizeDescendants;
|
||||
}
|
||||
|
||||
- (void)_setupPlaceholderLayer
|
||||
@@ -1502,7 +1502,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
// (even a runloop observer at a late call order will not stop the next frame from compositing, showing placeholders).
|
||||
|
||||
ASDisplayNode *node = [layer asyncdisplaykit_node];
|
||||
if (!layer.contents && [node _implementsDisplay]) {
|
||||
if (!layer.contents && [node __implementsDisplay]) {
|
||||
// For layers that do get displayed here, this immediately kicks off the work on the concurrent -[_ASDisplayLayer displayQueue].
|
||||
// At the same time, it creates an associated _ASAsyncTransaction, which we can use to block on display completion. See ASDisplayNode+AsyncDisplay.mm.
|
||||
[layer displayIfNeeded];
|
||||
@@ -1722,6 +1722,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
[self clearFetchedData];
|
||||
}
|
||||
|
||||
- (void)visibilityDidChange:(BOOL)isVisible
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* We currently only set interface state on nodes in table/collection views. For other nodes, if they are
|
||||
* in the hierarchy we enable all ASInterfaceState types with `ASInterfaceStateInHierarchy`, otherwise `None`.
|
||||
@@ -1776,11 +1780,17 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
// Entered or exited data loading state.
|
||||
if ((newState & ASInterfaceStateVisible) != (oldState & ASInterfaceStateVisible)) {
|
||||
if (newState & ASInterfaceStateVisible) {
|
||||
// Consider providing a -didBecomeVisible.
|
||||
[self visibilityDidChange:YES];
|
||||
} else {
|
||||
// Consider providing a -didBecomeInvisible.
|
||||
[self visibilityDidChange:NO];
|
||||
}
|
||||
}
|
||||
|
||||
[self interfaceStateDidChange:newState fromState:oldState];
|
||||
}
|
||||
|
||||
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
|
||||
{
|
||||
}
|
||||
|
||||
- (void)enterInterfaceState:(ASInterfaceState)interfaceState
|
||||
@@ -2138,7 +2148,7 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
|
||||
|
||||
self.asyncLayer.displaySuspended = flag;
|
||||
|
||||
if ([self _implementsDisplay]) {
|
||||
if ([self __implementsDisplay]) {
|
||||
if (flag) {
|
||||
[_supernode subnodeDisplayDidFinish:self];
|
||||
} else {
|
||||
@@ -2307,7 +2317,6 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
|
||||
static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
|
||||
|
||||
@implementation UIView (ASDisplayNodeInternal)
|
||||
@dynamic asyncdisplaykit_node;
|
||||
|
||||
- (void)setAsyncdisplaykit_node:(ASDisplayNode *)node
|
||||
{
|
||||
@@ -2316,16 +2325,24 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
|
||||
|
||||
- (ASDisplayNode *)asyncdisplaykit_node
|
||||
{
|
||||
ASDisplayNode *node = objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
|
||||
return node;
|
||||
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CALayer (ASDisplayNodeInternal)
|
||||
@dynamic asyncdisplaykit_node;
|
||||
@end
|
||||
|
||||
- (void)setAsyncdisplaykit_node:(ASDisplayNode *)node
|
||||
{
|
||||
objc_setAssociatedObject(self, ASDisplayNodeAssociatedNodeKey, node, OBJC_ASSOCIATION_ASSIGN); // Weak reference to avoid cycle, since the node retains the layer.
|
||||
}
|
||||
|
||||
- (ASDisplayNode *)asyncdisplaykit_node
|
||||
{
|
||||
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIView (AsyncDisplayKit)
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@interface ASMapNode : ASImageNode
|
||||
|
||||
/**
|
||||
The current region of ASMapNode. This can be set at any time and ASMapNode will animate the change.
|
||||
The current region of ASMapNode. This can be set at any time and ASMapNode will animate the change. This property may be set from a background thread before the node is loaded, and will automatically be applied to define the region of the static snapshot (if .liveMap = NO) or the internal MKMapView (otherwise).
|
||||
*/
|
||||
@property (nonatomic, assign) MKCoordinateRegion region;
|
||||
|
||||
@@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nullable, nonatomic, readonly) MKMapView *mapView;
|
||||
|
||||
/**
|
||||
Set this to YES to turn the snapshot into an interactive MKMapView and vice versa. Defaults to NO.
|
||||
Set this to YES to turn the snapshot into an interactive MKMapView and vice versa. Defaults to NO. This property may be set on a background thread before the node is loaded, and will automatically be actioned, once the node is loaded.
|
||||
*/
|
||||
@property (nonatomic, assign, getter=isLiveMap) BOOL liveMap;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
@synthesize needsMapReloadOnBoundsChange = _needsMapReloadOnBoundsChange;
|
||||
@synthesize mapDelegate = _mapDelegate;
|
||||
@synthesize region = _region;
|
||||
@synthesize liveMap = _liveMap;
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
- (instancetype)init
|
||||
@@ -37,9 +38,11 @@
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
_needsMapReloadOnBoundsChange = YES;
|
||||
|
||||
_liveMap = NO;
|
||||
_centerCoordinateOfMap = kCLLocationCoordinate2DInvalid;
|
||||
_region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(43.432858, 13.183671), MKCoordinateSpanMake(0.2, 0.2));
|
||||
|
||||
//Default world-scale view
|
||||
_region = MKCoordinateRegionForMapRect(MKMapRectWorld);
|
||||
|
||||
_options = [[MKMapSnapshotOptions alloc] init];
|
||||
_options.region = _region;
|
||||
@@ -50,26 +53,31 @@
|
||||
- (void)didLoad
|
||||
{
|
||||
[super didLoad];
|
||||
if ([self wasLiveMapPreviously]) {
|
||||
if (self.isLiveMap) {
|
||||
self.userInteractionEnabled = YES;
|
||||
[self addLiveMap];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setLayerBacked:(BOOL)layerBacked
|
||||
{
|
||||
ASDisplayNodeAssert(!self.isLiveMap, @"ASMapNode can not be layer backed whilst .liveMap = YES, set .liveMap = NO to use layer backing.");
|
||||
[super setLayerBacked:layerBacked];
|
||||
}
|
||||
|
||||
- (void)fetchData
|
||||
{
|
||||
[super fetchData];
|
||||
if ([self wasLiveMapPreviously]) {
|
||||
if (self.isLiveMap) {
|
||||
[self addLiveMap];
|
||||
} else {
|
||||
[self setUpSnapshotter];
|
||||
[self takeSnapshot];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearFetchedData
|
||||
- (void)clearContents
|
||||
{
|
||||
[super clearFetchedData];
|
||||
[super clearContents];
|
||||
if (self.isLiveMap) {
|
||||
[self removeLiveMap];
|
||||
}
|
||||
@@ -79,17 +87,21 @@
|
||||
|
||||
- (BOOL)isLiveMap
|
||||
{
|
||||
return (_mapView != nil);
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _liveMap;
|
||||
}
|
||||
|
||||
- (void)setLiveMap:(BOOL)liveMap
|
||||
{
|
||||
liveMap ? [self addLiveMap] : [self removeLiveMap];
|
||||
}
|
||||
|
||||
- (BOOL)wasLiveMapPreviously
|
||||
{
|
||||
return CLLocationCoordinate2DIsValid(_centerCoordinateOfMap);
|
||||
ASDisplayNodeAssert(!self.isLayerBacked, @"ASMapNode can not use the interactive map feature whilst .isLayerBacked = YES, set .layerBacked = NO to use the interactive map feature.");
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
if (liveMap == _liveMap) {
|
||||
return;
|
||||
}
|
||||
_liveMap = liveMap;
|
||||
if (self.nodeLoaded) {
|
||||
liveMap ? [self addLiveMap] : [self removeLiveMap];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)needsMapReloadOnBoundsChange
|
||||
@@ -127,63 +139,62 @@
|
||||
|
||||
- (void)takeSnapshot
|
||||
{
|
||||
if (!_snapshotter.isLoading) {
|
||||
[_snapshotter startWithCompletionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
|
||||
if (!error) {
|
||||
UIImage *image = snapshot.image;
|
||||
CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale);
|
||||
[image drawAtPoint:CGPointMake(0, 0)];
|
||||
|
||||
if (_annotations.count > 0 ) {
|
||||
// Get a standard annotation view pin. Future implementations should use a custom annotation image property.
|
||||
MKAnnotationView *pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""];
|
||||
UIImage *pinImage = pin.image;
|
||||
for (id<MKAnnotation>annotation in _annotations)
|
||||
if (!_snapshotter) {
|
||||
[self setUpSnapshotter];
|
||||
}
|
||||
[_snapshotter cancel];
|
||||
[_snapshotter startWithCompletionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
|
||||
if (!error) {
|
||||
UIImage *image = snapshot.image;
|
||||
CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale);
|
||||
[image drawAtPoint:CGPointMake(0, 0)];
|
||||
|
||||
if (_annotations.count > 0 ) {
|
||||
// Get a standard annotation view pin. Future implementations should use a custom annotation image property.
|
||||
MKAnnotationView *pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""];
|
||||
UIImage *pinImage = pin.image;
|
||||
for (id<MKAnnotation>annotation in _annotations)
|
||||
{
|
||||
CGPoint point = [snapshot pointForCoordinate:annotation.coordinate];
|
||||
if (CGRectContainsPoint(finalImageRect, point))
|
||||
{
|
||||
CGPoint point = [snapshot pointForCoordinate:annotation.coordinate];
|
||||
if (CGRectContainsPoint(finalImageRect, point))
|
||||
{
|
||||
CGPoint pinCenterOffset = pin.centerOffset;
|
||||
point.x -= pin.bounds.size.width / 2.0;
|
||||
point.y -= pin.bounds.size.height / 2.0;
|
||||
point.x += pinCenterOffset.x;
|
||||
point.y += pinCenterOffset.y;
|
||||
[pinImage drawAtPoint:point];
|
||||
}
|
||||
CGPoint pinCenterOffset = pin.centerOffset;
|
||||
point.x -= pin.bounds.size.width / 2.0;
|
||||
point.y -= pin.bounds.size.height / 2.0;
|
||||
point.x += pinCenterOffset.x;
|
||||
point.y += pinCenterOffset.y;
|
||||
[pinImage drawAtPoint:point];
|
||||
}
|
||||
}
|
||||
|
||||
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
self.image = finalImage;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
self.image = finalImage;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setUpSnapshotter
|
||||
{
|
||||
if (!_snapshotter) {
|
||||
ASDisplayNodeAssert(!CGSizeEqualToSize(CGSizeZero, self.calculatedSize), @"self.calculatedSize can not be zero. Make sure that you are setting a preferredFrameSize or wrapping ASMapNode in a ASRatioLayoutSpec or similar.");
|
||||
_options.size = self.calculatedSize;
|
||||
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
|
||||
}
|
||||
ASDisplayNodeAssert(!CGSizeEqualToSize(CGSizeZero, self.calculatedSize), @"self.calculatedSize can not be zero. Make sure that you are setting a preferredFrameSize or wrapping ASMapNode in a ASRatioLayoutSpec or similar.");
|
||||
_options.size = self.calculatedSize;
|
||||
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
|
||||
}
|
||||
|
||||
- (void)resetSnapshotter
|
||||
{
|
||||
if (!_snapshotter.isLoading) {
|
||||
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
|
||||
}
|
||||
[_snapshotter cancel];
|
||||
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
- (void)addLiveMap
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (!self.isLiveMap) {
|
||||
if (!_mapView) {
|
||||
__weak ASMapNode *weakSelf = self;
|
||||
_mapView = [[MKMapView alloc] initWithFrame:CGRectZero];
|
||||
_mapView.delegate = weakSelf.mapDelegate;
|
||||
@@ -194,8 +205,6 @@
|
||||
|
||||
if (CLLocationCoordinate2DIsValid(_centerCoordinateOfMap)) {
|
||||
[_mapView setCenterCoordinate:_centerCoordinateOfMap];
|
||||
} else {
|
||||
_centerCoordinateOfMap = _options.region.center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,41 +7,77 @@
|
||||
//
|
||||
|
||||
#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
|
||||
{
|
||||
_flowLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||
_flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
_flowLayout.minimumInteritemSpacing = 0;
|
||||
_flowLayout.minimumLineSpacing = 0;
|
||||
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
flowLayout.minimumInteritemSpacing = 0;
|
||||
flowLayout.minimumLineSpacing = 0;
|
||||
|
||||
self = [super initWithCollectionViewLayout:_flowLayout];
|
||||
return [self initWithFlowLayout:flowLayout];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout
|
||||
{
|
||||
self = [super initWithCollectionViewLayout:flowLayout];
|
||||
if (self != nil) {
|
||||
_flowLayout = flowLayout;
|
||||
}
|
||||
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 };
|
||||
@@ -61,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,18 +7,16 @@
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASRangeController.h>
|
||||
#import <AsyncDisplayKit/ASTableViewProtocols.h>
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
#import <AsyncDisplayKit/ASBatchContext.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ASCellNode;
|
||||
@protocol ASTableViewDataSource;
|
||||
@protocol ASTableViewDelegate;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@protocol ASTableDataSource;
|
||||
@protocol ASTableDelegate;
|
||||
|
||||
/**
|
||||
* Node-based table view.
|
||||
@@ -28,8 +26,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@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.
|
||||
@@ -281,7 +279,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* This is a node-based UITableViewDataSource.
|
||||
*/
|
||||
@protocol ASTableViewDataSource <ASCommonTableViewDataSource, NSObject>
|
||||
@protocol ASTableDataSource <ASCommonTableViewDataSource, NSObject>
|
||||
|
||||
/**
|
||||
* Similar to -tableView:cellForRowAtIndexPath:.
|
||||
@@ -318,6 +316,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
@protocol ASTableViewDataSource <ASTableDataSource>
|
||||
@end
|
||||
|
||||
/**
|
||||
* This is a node-based UITableViewDelegate.
|
||||
@@ -325,7 +325,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* 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
|
||||
|
||||
@@ -361,4 +361,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
<<<<<<< HEAD
|
||||
NS_ASSUME_NONNULL_END
|
||||
=======
|
||||
@protocol ASTableViewDelegate <ASTableDelegate>;
|
||||
@end
|
||||
>>>>>>> master
|
||||
|
||||
@@ -8,103 +8,26 @@
|
||||
|
||||
#import "ASTableView.h"
|
||||
#import "ASTableViewInternal.h"
|
||||
#import "ASTableNode.h"
|
||||
|
||||
#import "ASAssert.h"
|
||||
#import "ASBatchFetching.h"
|
||||
#import "ASChangeSetDataController.h"
|
||||
#import "ASCollectionViewLayoutController.h"
|
||||
#import "ASDelegateProxy.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASLayout.h"
|
||||
#import "ASLayoutController.h"
|
||||
#import "ASRangeController.h"
|
||||
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
//#define LOG(...) NSLog(__VA_ARGS__)
|
||||
#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 ASCellNode<->UITableViewCell bridging.
|
||||
|
||||
@@ -156,13 +79,16 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASTableView
|
||||
|
||||
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate> {
|
||||
_ASTableViewProxy *_proxyDataSource;
|
||||
_ASTableViewProxy *_proxyDelegate;
|
||||
@interface ASTableNode ()
|
||||
- (instancetype)_initWithStyle:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass;
|
||||
@end
|
||||
|
||||
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor> {
|
||||
ASTableViewProxy *_proxyDataSource;
|
||||
ASTableViewProxy *_proxyDelegate;
|
||||
|
||||
ASFlowLayoutController *_layoutController;
|
||||
|
||||
@@ -180,6 +106,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
CGFloat _nodesConstrainedWidth;
|
||||
BOOL _ignoreNodesConstrainedWidthChange;
|
||||
BOOL _queuedNodeHeightUpdate;
|
||||
BOOL _isDeallocating;
|
||||
}
|
||||
|
||||
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
||||
@@ -224,6 +151,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
|
||||
// and should not trigger a relayout.
|
||||
_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];
|
||||
}
|
||||
@@ -233,41 +166,45 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
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
|
||||
{
|
||||
// Sometimes the UIKit classes can call back to their delegate even during deallocation.
|
||||
// This bug might be iOS 7-specific.
|
||||
super.delegate = nil;
|
||||
super.dataSource = nil;
|
||||
_isDeallocating = YES;
|
||||
[self setAsyncDelegate:nil];
|
||||
[self setAsyncDataSource:nil];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@@ -290,17 +227,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
|
||||
// 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
|
||||
// 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) {
|
||||
super.dataSource = nil;
|
||||
_asyncDataSource = nil;
|
||||
_proxyDataSource = nil;
|
||||
_proxyDataSource = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||
} else {
|
||||
_asyncDataSource = asyncDataSource;
|
||||
_proxyDataSource = [[_ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
||||
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
|
||||
_proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
||||
}
|
||||
|
||||
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
|
||||
}
|
||||
|
||||
- (void)setAsyncDelegate:(id<ASTableViewDelegate>)asyncDelegate
|
||||
@@ -308,18 +247,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
|
||||
// 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
|
||||
// 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) {
|
||||
// order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes
|
||||
// in UIScrollViewAccessibility.
|
||||
super.delegate = nil;
|
||||
_asyncDelegate = nil;
|
||||
_proxyDelegate = nil;
|
||||
_proxyDelegate = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||
} else {
|
||||
_asyncDelegate = asyncDelegate;
|
||||
_proxyDelegate = [[_ASTableViewProxy alloc] initWithTarget:asyncDelegate interceptor:self];
|
||||
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
|
||||
_proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
|
||||
}
|
||||
|
||||
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
|
||||
}
|
||||
|
||||
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy
|
||||
{
|
||||
if (proxy == _proxyDelegate) {
|
||||
[self setAsyncDelegate:nil];
|
||||
} else if (proxy == _proxyDataSource) {
|
||||
[self setAsyncDataSource:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,15 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
||||
- (void)dealloc
|
||||
{
|
||||
CGColorRelease(_backgroundColor);
|
||||
|
||||
// Destruction of the layout managers/containers/text storage is quite
|
||||
// expensive, and can take some time, so we dispatch onto a bg queue to
|
||||
// actually dealloc.
|
||||
__block ASTextKitRenderer *renderer = _renderer;
|
||||
ASPerformBlockOnBackgroundThread(^{
|
||||
renderer = nil;
|
||||
});
|
||||
_renderer = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -97,6 +106,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
@@ -120,7 +131,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
||||
self.opaque = NO;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
|
||||
self.linkAttributeNames = @[ NSLinkAttributeName ];
|
||||
self.linkAttributeNames = DefaultLinkAttributeNames;
|
||||
|
||||
// Accessibility
|
||||
self.isAccessibilityElement = YES;
|
||||
@@ -155,6 +166,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
||||
if (_shadowColor != NULL) {
|
||||
CGColorRelease(_shadowColor);
|
||||
}
|
||||
|
||||
[self _invalidateRenderer];
|
||||
|
||||
if (_longPressGestureRecognizer) {
|
||||
_longPressGestureRecognizer.delegate = nil;
|
||||
@@ -187,6 +200,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
||||
return [[self _renderer] size];
|
||||
}
|
||||
|
||||
// FIXME: Re-evaluate if it is still the right decision to clear the renderer at this stage.
|
||||
// This code was written before TextKit and when 512MB devices were still the overwhelming majority.
|
||||
- (void)displayDidFinish
|
||||
{
|
||||
[super displayDidFinish];
|
||||
@@ -261,16 +276,17 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
||||
- (void)_invalidateRenderer
|
||||
{
|
||||
ASDN::MutexLocker l(_rendererLock);
|
||||
|
||||
if (_renderer) {
|
||||
// Destruction of the layout managers/containers/text storage is quite
|
||||
// expensive, and can take some time, so we dispatch onto a bg queue to
|
||||
// actually dealloc.
|
||||
__block ASTextKitRenderer *renderer = _renderer;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
ASPerformBlockOnBackgroundThread(^{
|
||||
renderer = nil;
|
||||
});
|
||||
_renderer = nil;
|
||||
}
|
||||
_renderer = nil;
|
||||
}
|
||||
|
||||
- (void)_invalidateRendererIfNeeded
|
||||
@@ -318,7 +334,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
||||
|
||||
#pragma mark - Modifying User Text
|
||||
|
||||
- (void)setAttributedString:(NSAttributedString *)attributedString {
|
||||
- (void)setAttributedString:(NSAttributedString *)attributedString
|
||||
{
|
||||
if (ASObjectIsEqual(attributedString, _attributedString)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,14 +13,23 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ASViewController : UIViewController
|
||||
|
||||
- (instancetype)initWithNode:(ASDisplayNode *)node NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@property (nonatomic, strong, readonly) ASDisplayNode *node;
|
||||
|
||||
/**
|
||||
* @abstract Passthrough property to the the .interfaceState of the node.
|
||||
* @return The current ASInterfaceState of the node, indicating whether it is visible and other situational properties.
|
||||
* @see ASInterfaceState
|
||||
*/
|
||||
@property (nonatomic, readonly) ASInterfaceState interfaceState;
|
||||
|
||||
|
||||
// AsyncDisplayKit 2.0 BETA: This property is still being tested, but it allows
|
||||
// blocking as a view controller becomes visible to ensure no placeholders flash onscreen.
|
||||
// Refer to examples/SynchronousConcurrency, AsyncViewController.m
|
||||
@property (nonatomic, assign) BOOL neverShowPlaceholders;
|
||||
|
||||
- (instancetype)initWithNode:(ASDisplayNode *)node;
|
||||
|
||||
/**
|
||||
* The constrained size used to measure the backing node.
|
||||
|
||||
@@ -16,6 +16,16 @@
|
||||
BOOL _ensureDisplayed;
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
|
||||
{
|
||||
return [self initWithNode:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
return [self initWithNode:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithNode:(ASDisplayNode *)node
|
||||
{
|
||||
if (!(self = [super initWithNibName:nil bundle:nil])) {
|
||||
@@ -65,4 +75,9 @@
|
||||
return ASSizeRangeMake(viewSize, viewSize);
|
||||
}
|
||||
|
||||
- (ASInterfaceState)interfaceState
|
||||
{
|
||||
return _node.interfaceState;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -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;
|
||||
|
||||
55
AsyncDisplayKit/Details/ASDelegateProxy.h
Normal file
55
AsyncDisplayKit/Details/ASDelegateProxy.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* 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
|
||||
|
||||
@interface ASPagerNodeProxy : ASDelegateProxy
|
||||
@end
|
||||
|
||||
127
AsyncDisplayKit/Details/ASDelegateProxy.m
Normal file
127
AsyncDisplayKit/Details/ASDelegateProxy.m
Normal file
@@ -0,0 +1,127 @@
|
||||
/* 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 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;
|
||||
}
|
||||
|
||||
- (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
|
||||
{
|
||||
if ([self interceptsSelector:aSelector]) {
|
||||
return (_interceptor != nil);
|
||||
} 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
|
||||
{
|
||||
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
|
||||
@@ -10,19 +10,53 @@
|
||||
|
||||
#import "_ASAsyncTransaction.h"
|
||||
#import "_ASAsyncTransactionGroup.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
static const char *ASDisplayNodeAssociatedTransactionsKey = "ASAssociatedTransactions";
|
||||
static const char *ASDisplayNodeAssociatedCurrentTransactionKey = "ASAssociatedCurrentTransaction";
|
||||
|
||||
@implementation CALayer (ASAsyncTransactionContainerTransactions)
|
||||
@dynamic asyncdisplaykit_asyncLayerTransactions;
|
||||
@dynamic asyncdisplaykit_currentAsyncLayerTransaction;
|
||||
|
||||
- (_ASAsyncTransaction *)asyncdisplaykit_currentAsyncLayerTransaction
|
||||
{
|
||||
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedCurrentTransactionKey);
|
||||
}
|
||||
|
||||
- (void)asyncdisplaykit_setCurrentAsyncLayerTransaction:(_ASAsyncTransaction *)transaction
|
||||
{
|
||||
objc_setAssociatedObject(self, ASDisplayNodeAssociatedCurrentTransactionKey, transaction, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
- (NSHashTable *)asyncdisplaykit_asyncLayerTransactions
|
||||
{
|
||||
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedTransactionsKey);
|
||||
}
|
||||
|
||||
- (void)asyncdisplaykit_setAsyncLayerTransactions:(NSHashTable *)transactions
|
||||
{
|
||||
objc_setAssociatedObject(self, ASDisplayNodeAssociatedTransactionsKey, transactions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
// No-ops in the base class. Mostly exposed for testing.
|
||||
- (void)asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:(_ASAsyncTransaction *)transaction {}
|
||||
- (void)asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:(_ASAsyncTransaction *)transaction {}
|
||||
@end
|
||||
|
||||
static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer";
|
||||
|
||||
@implementation CALayer (ASDisplayNodeAsyncTransactionContainer)
|
||||
|
||||
@dynamic asyncdisplaykit_asyncTransactionContainer;
|
||||
- (BOOL)asyncdisplaykit_isAsyncTransactionContainer
|
||||
{
|
||||
CFBooleanRef isContainerBool = (__bridge CFBooleanRef)objc_getAssociatedObject(self, ASAsyncTransactionIsContainerKey);
|
||||
BOOL isContainer = (isContainerBool == kCFBooleanTrue);
|
||||
return isContainer;
|
||||
}
|
||||
|
||||
- (void)asyncdisplaykit_setAsyncTransactionContainer:(BOOL)isContainer
|
||||
{
|
||||
objc_setAssociatedObject(self, ASAsyncTransactionIsContainerKey, (id)(isContainer ? kCFBooleanTrue : kCFBooleanFalse), OBJC_ASSOCIATION_ASSIGN);
|
||||
}
|
||||
|
||||
- (ASAsyncTransactionContainerState)asyncdisplaykit_asyncTransactionContainerState
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -175,8 +175,6 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
|
||||
|
||||
- (asyncdisplaykit_async_transaction_operation_block_t)_displayBlockWithAsynchronous:(BOOL)asynchronous isCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock rasterizing:(BOOL)rasterizing
|
||||
{
|
||||
id nodeClass = [self class];
|
||||
|
||||
asyncdisplaykit_async_transaction_operation_block_t displayBlock = nil;
|
||||
|
||||
ASDisplayNodeAssert(rasterizing || !(_hierarchyState & ASHierarchyStateRasterized), @"Rasterized descendants should never display unless being drawn into the rasterized container.");
|
||||
@@ -235,7 +233,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
|
||||
|
||||
ASDN_DELAY_FOR_DISPLAY();
|
||||
|
||||
UIImage *result = [nodeClass displayWithParameters:drawParameters isCancelled:isCancelledBlock];
|
||||
UIImage *result = [[self class] displayWithParameters:drawParameters isCancelled:isCancelledBlock];
|
||||
__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);
|
||||
return result;
|
||||
};
|
||||
@@ -265,7 +263,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
|
||||
}
|
||||
|
||||
[nodeClass drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing];
|
||||
[[self class] drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing];
|
||||
|
||||
if (isCancelledBlock()) {
|
||||
if (!rasterizing) {
|
||||
|
||||
@@ -59,6 +59,9 @@ typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
|
||||
ASHierarchyState _hierarchyState;
|
||||
}
|
||||
|
||||
// The view class to use when creating a new display node instance. Defaults to _ASDisplayView.
|
||||
+ (Class)viewClass;
|
||||
|
||||
// These methods are recursive, and either union or remove the provided interfaceState to all sub-elements.
|
||||
- (void)enterInterfaceState:(ASInterfaceState)interfaceState;
|
||||
- (void)exitInterfaceState:(ASInterfaceState)interfaceState;
|
||||
|
||||
@@ -446,19 +446,27 @@
|
||||
{
|
||||
_bridge_prologue;
|
||||
if (__loaded) {
|
||||
return ASDisplayNodeUIContentModeFromCAContentsGravity(_layer.contentsGravity);
|
||||
if (_flags.layerBacked) {
|
||||
return ASDisplayNodeUIContentModeFromCAContentsGravity(_layer.contentsGravity);
|
||||
} else {
|
||||
return _view.contentMode;
|
||||
}
|
||||
} else {
|
||||
return self.pendingViewState.contentMode;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setContentMode:(UIViewContentMode)mode
|
||||
- (void)setContentMode:(UIViewContentMode)contentMode
|
||||
{
|
||||
_bridge_prologue;
|
||||
if (__loaded) {
|
||||
_layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(mode);
|
||||
if (_flags.layerBacked) {
|
||||
_layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(contentMode);
|
||||
} else {
|
||||
_view.contentMode = contentMode;
|
||||
}
|
||||
} else {
|
||||
self.pendingViewState.contentMode = mode;
|
||||
self.pendingViewState.contentMode = contentMode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,9 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
|
||||
// Call didExitHierarchy if necessary and set inHierarchy = NO if visibility notifications are enabled on all of its parents
|
||||
- (void)__exitHierarchy;
|
||||
|
||||
// Helper method to summarize whether or not the node run through the display process
|
||||
- (BOOL)__implementsDisplay;
|
||||
|
||||
// Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated.
|
||||
- (void)displayImmediately;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ ASDISPLAYNODE_EXTERN_C_BEGIN
|
||||
BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector);
|
||||
BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector);
|
||||
void ASPerformBlockOnMainThread(void (^block)());
|
||||
void ASPerformBlockOnBackgroundThread(void (^block)()); // DISPATCH_QUEUE_PRIORITY_DEFAULT
|
||||
|
||||
CGFloat ASScreenScale();
|
||||
|
||||
|
||||
@@ -57,6 +57,18 @@ void ASPerformBlockOnMainThread(void (^block)())
|
||||
}
|
||||
}
|
||||
|
||||
void ASPerformBlockOnBackgroundThread(void (^block)())
|
||||
{
|
||||
if ([NSThread isMainThread]) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
block();
|
||||
});
|
||||
} else {
|
||||
block();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CGFloat ASScreenScale()
|
||||
{
|
||||
static CGFloat _scale;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -124,20 +119,34 @@ NSString *const ASDisplayNodeCAContentsGravityFromUIContentMode(UIViewContentMod
|
||||
return nil;
|
||||
}
|
||||
|
||||
#define ContentModeCacheSize 10
|
||||
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]) {
|
||||
return UIContentModeCAGravityLUT[i].contentMode;
|
||||
static int currentCacheIndex = 0;
|
||||
static NSMutableArray *cachedStrings = [NSMutableArray arrayWithCapacity:ContentModeCacheSize];
|
||||
static UIViewContentMode cachedModes[ContentModeCacheSize] = {};
|
||||
|
||||
NSInteger foundCacheIndex = [cachedStrings indexOfObjectIdenticalTo:contentsGravity];
|
||||
if (foundCacheIndex != NSNotFound && foundCacheIndex < ContentModeCacheSize) {
|
||||
return cachedModes[foundCacheIndex];
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_COUNT(UIContentModeCAGravityLUT); i++) {
|
||||
if (ASObjectIsEqual(UIContentModeCAGravityLUT[i].string, contentsGravity)) {
|
||||
UIViewContentMode foundContentMode = UIContentModeCAGravityLUT[i].contentMode;
|
||||
|
||||
if (currentCacheIndex < ContentModeCacheSize) {
|
||||
// Cache the input value. This is almost always a different pointer than in our LUT and will frequently
|
||||
// be the same value for an overwhelming majority of inputs.
|
||||
[cachedStrings addObject:contentsGravity];
|
||||
cachedModes[currentCacheIndex] = foundContentMode;
|
||||
currentCacheIndex++;
|
||||
}
|
||||
|
||||
return foundContentMode;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -135,19 +135,24 @@
|
||||
@synthesize borderColor=borderColor;
|
||||
@synthesize asyncdisplaykit_asyncTransactionContainer=asyncTransactionContainer;
|
||||
|
||||
|
||||
static CGColorRef blackColorRef = NULL;
|
||||
static UIColor *defaultTintColor = nil;
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
// Default UIKit color is an RGB color
|
||||
static CGColorRef black;
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
// Default UIKit color is an RGB color
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
black = CGColorCreate(colorSpace, (CGFloat[]){0,0,0,1} );
|
||||
CFRetain(black);
|
||||
blackColorRef = CGColorCreate(colorSpace, (CGFloat[]){0,0,0,1} );
|
||||
CFRetain(blackColorRef);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
defaultTintColor = [UIColor colorWithRed:0.0 green:0.478 blue:1.0 alpha:1.0];
|
||||
});
|
||||
|
||||
// Set defaults, these come from the defaults specified in CALayer and UIView
|
||||
@@ -156,7 +161,7 @@
|
||||
frame = CGRectZero;
|
||||
bounds = CGRectZero;
|
||||
backgroundColor = nil;
|
||||
tintColor = [UIColor colorWithRed:0.0 green:0.478 blue:1.0 alpha:1.0];
|
||||
tintColor = defaultTintColor;
|
||||
contents = nil;
|
||||
isHidden = NO;
|
||||
needsDisplayOnBoundsChange = NO;
|
||||
@@ -172,14 +177,12 @@
|
||||
transform = CATransform3DIdentity;
|
||||
sublayerTransform = CATransform3DIdentity;
|
||||
userInteractionEnabled = YES;
|
||||
CFRetain(black);
|
||||
shadowColor = black;
|
||||
shadowColor = blackColorRef;
|
||||
shadowOpacity = 0.0;
|
||||
shadowOffset = CGSizeMake(0, -3);
|
||||
shadowRadius = 3;
|
||||
borderWidth = 0;
|
||||
CFRetain(black);
|
||||
borderColor = black;
|
||||
borderColor = blackColorRef;
|
||||
isAccessibilityElement = NO;
|
||||
accessibilityLabel = nil;
|
||||
accessibilityHint = nil;
|
||||
@@ -376,7 +379,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
CGColorRelease(shadowColor);
|
||||
if (shadowColor != blackColorRef) {
|
||||
CGColorRelease(shadowColor);
|
||||
}
|
||||
shadowColor = color;
|
||||
CGColorRetain(shadowColor);
|
||||
|
||||
@@ -413,7 +418,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
CGColorRelease(borderColor);
|
||||
if (borderColor != blackColorRef) {
|
||||
CGColorRelease(borderColor);
|
||||
}
|
||||
borderColor = color;
|
||||
CGColorRetain(borderColor);
|
||||
|
||||
@@ -1002,8 +1009,14 @@
|
||||
- (void)dealloc
|
||||
{
|
||||
CGColorRelease(backgroundColor);
|
||||
CGColorRelease(shadowColor);
|
||||
CGColorRelease(borderColor);
|
||||
|
||||
if (shadowColor != blackColorRef) {
|
||||
CGColorRelease(shadowColor);
|
||||
}
|
||||
|
||||
if (borderColor != blackColorRef) {
|
||||
CGColorRelease(borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -32,7 +32,9 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
|
||||
@implementation ASTextKitRenderer {
|
||||
CGSize _calculatedSize;
|
||||
BOOL _sizeIsCalculated;
|
||||
}
|
||||
@synthesize attributes = _attributes, context = _context, shadower = _shadower, truncater = _truncater;
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
@@ -42,30 +44,50 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
if (self = [super init]) {
|
||||
_constrainedSize = constrainedSize;
|
||||
_attributes = attributes;
|
||||
_sizeIsCalculated = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (ASTextKitShadower *)shadower
|
||||
{
|
||||
if (!_shadower) {
|
||||
ASTextKitAttributes attributes = _attributes;
|
||||
_shadower = [[ASTextKitShadower alloc] initWithShadowOffset:attributes.shadowOffset
|
||||
shadowColor:attributes.shadowColor
|
||||
shadowOpacity:attributes.shadowOpacity
|
||||
shadowRadius:attributes.shadowRadius];
|
||||
}
|
||||
return _shadower;
|
||||
}
|
||||
|
||||
- (ASTextKitTailTruncater *)truncater
|
||||
{
|
||||
if (!_truncater) {
|
||||
ASTextKitAttributes attributes = _attributes;
|
||||
// We must inset the constrained size by the size of the shadower.
|
||||
CGSize shadowConstrainedSize = [_shadower insetSizeWithConstrainedSize:_constrainedSize];
|
||||
CGSize shadowConstrainedSize = [[self shadower] insetSizeWithConstrainedSize:_constrainedSize];
|
||||
_truncater = [[ASTextKitTailTruncater alloc] initWithContext:[self context]
|
||||
truncationAttributedString:attributes.truncationAttributedString
|
||||
avoidTailTruncationSet:attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet()
|
||||
constrainedSize:shadowConstrainedSize];
|
||||
}
|
||||
return _truncater;
|
||||
}
|
||||
|
||||
- (ASTextKitContext *)context
|
||||
{
|
||||
if (!_context) {
|
||||
ASTextKitAttributes attributes = _attributes;
|
||||
CGSize shadowConstrainedSize = [[self shadower] insetSizeWithConstrainedSize:_constrainedSize];
|
||||
_context = [[ASTextKitContext alloc] initWithAttributedString:attributes.attributedString
|
||||
lineBreakMode:attributes.lineBreakMode
|
||||
maximumNumberOfLines:attributes.maximumNumberOfLines
|
||||
exclusionPaths:attributes.exclusionPaths
|
||||
constrainedSize:shadowConstrainedSize
|
||||
layoutManagerFactory:attributes.layoutManagerFactory];
|
||||
|
||||
_truncater = [[ASTextKitTailTruncater alloc] initWithContext:_context
|
||||
truncationAttributedString:attributes.truncationAttributedString
|
||||
avoidTailTruncationSet:attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet()
|
||||
constrainedSize:shadowConstrainedSize];
|
||||
|
||||
[self _calculateSize];
|
||||
}
|
||||
return self;
|
||||
return _context;
|
||||
}
|
||||
|
||||
#pragma mark - Sizing
|
||||
@@ -74,14 +96,14 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
{
|
||||
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by
|
||||
// -usedRectForTextContainer:).
|
||||
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
}];
|
||||
|
||||
|
||||
CGRect constrainedRect = {CGPointZero, _constrainedSize};
|
||||
__block CGRect boundingRect;
|
||||
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
boundingRect = [layoutManager usedRectForTextContainer:textContainer];
|
||||
}];
|
||||
|
||||
@@ -94,6 +116,10 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
|
||||
- (CGSize)size
|
||||
{
|
||||
if (!_sizeIsCalculated) {
|
||||
[self _calculateSize];
|
||||
_sizeIsCalculated = YES;
|
||||
}
|
||||
return _calculatedSize;
|
||||
}
|
||||
|
||||
@@ -104,13 +130,13 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
// We add an assertion so we can track the rare conditions where a graphics context is not present
|
||||
ASDisplayNodeAssertNotNil(context, @"This is no good without a context.");
|
||||
|
||||
CGRect shadowInsetBounds = [_shadower insetRectWithConstrainedRect:bounds];
|
||||
CGRect shadowInsetBounds = [[self shadower] insetRectWithConstrainedRect:bounds];
|
||||
|
||||
CGContextSaveGState(context);
|
||||
[_shadower setShadowInContext:context];
|
||||
[[self shadower] setShadowInContext:context];
|
||||
UIGraphicsPushContext(context);
|
||||
|
||||
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
|
||||
[layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin];
|
||||
[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin];
|
||||
@@ -125,7 +151,7 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
- (NSUInteger)lineCount
|
||||
{
|
||||
__block NSUInteger lineCount = 0;
|
||||
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
for (NSRange lineRange = { 0, 0 }; NSMaxRange(lineRange) < [layoutManager numberOfGlyphs]; lineCount++) {
|
||||
[layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(lineRange) effectiveRange:&lineRange];
|
||||
}
|
||||
@@ -135,7 +161,7 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
|
||||
- (std::vector<NSRange>)visibleRanges
|
||||
{
|
||||
return _truncater.visibleRanges;
|
||||
return [self truncater].visibleRanges;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// ASBasicImageDownloaderTests.m
|
||||
// ASZBasicImageDownloaderTests.m
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Victor Mayorov on 10/06/15.
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#import <AsyncDisplayKit/ASBasicImageDownloader.h>
|
||||
|
||||
// Z in the name to delay running until after the test instance is operating normally.
|
||||
@interface ASBasicImageDownloaderTests : XCTestCase
|
||||
|
||||
@end
|
||||
@@ -21,34 +22,27 @@
|
||||
ASBasicImageDownloader *downloader = [ASBasicImageDownloader sharedImageDownloader];
|
||||
|
||||
NSURL *URL = [NSURL URLWithString:@"http://wrongPath/wrongResource.png"];
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
|
||||
|
||||
__block BOOL firstDone = NO;
|
||||
|
||||
dispatch_group_enter(group);
|
||||
[downloader downloadImageWithURL:URL
|
||||
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||
downloadProgressBlock:nil
|
||||
completion:^(CGImageRef image, NSError *error) {
|
||||
firstDone = YES;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
__block BOOL secondDone = NO;
|
||||
|
||||
dispatch_group_enter(group);
|
||||
[downloader downloadImageWithURL:URL
|
||||
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||
downloadProgressBlock:nil
|
||||
completion:^(CGImageRef image, NSError *error) {
|
||||
secondDone = YES;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
XCTAssert(0 == dispatch_group_wait(group, dispatch_time(0, 10 * 1000000000)), @"URL loading takes too long");
|
||||
|
||||
XCTAssert(firstDone && secondDone, @"Not all handlers has been called");
|
||||
|
||||
sleep(3);
|
||||
XCTAssert(firstDone && secondDone, @"Not all ASBasicImageDownloader completion handlers have been called after 3 seconds");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
|
||||
|
||||
CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
collectionView.asyncDelegate = delegate;
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
|
||||
|
||||
CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
collectionView.asyncDelegate = delegate;
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
layout.headerReferenceSize = CGSizeMake(125.0, 125.0);
|
||||
|
||||
CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
|
||||
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
|
||||
@@ -167,7 +167,7 @@
|
||||
layout.footerReferenceSize = CGSizeMake(125.0, 125.0);
|
||||
|
||||
CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
|
||||
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
|
||||
@@ -190,7 +190,7 @@
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
|
||||
CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
collectionView.asyncDelegate = delegate;
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
|
||||
CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
collectionView.asyncDelegate = delegate;
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
layout.headerReferenceSize = CGSizeMake(125.0, 125.0);
|
||||
|
||||
CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
|
||||
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
|
||||
@@ -257,7 +257,7 @@
|
||||
layout.footerReferenceSize = CGSizeMake(125.0, 125.0);
|
||||
|
||||
CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
|
||||
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
|
||||
@@ -274,7 +274,7 @@
|
||||
InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init];
|
||||
HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init];
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
collectionView.asyncDelegate = delegate;
|
||||
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
|
||||
@@ -291,7 +291,7 @@
|
||||
- (void)testThatItRespondsWithTheDefaultNumberOfSections
|
||||
{
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
|
||||
NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||
XCTAssert(sections == 1, @"should return 1 by default");
|
||||
@@ -304,7 +304,7 @@
|
||||
{
|
||||
InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init];
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
|
||||
NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||
@@ -321,7 +321,7 @@
|
||||
InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init];
|
||||
HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init];
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
collectionView.asyncDelegate = delegate;
|
||||
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
|
||||
@@ -338,7 +338,7 @@
|
||||
HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init];
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
layout.footerReferenceSize = CGSizeMake(125.0, 125.0);
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
collectionView.asyncDelegate = delegate;
|
||||
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
|
||||
@@ -354,7 +354,7 @@
|
||||
InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init];
|
||||
HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init];
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO];
|
||||
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
collectionView.asyncDataSource = dataSource;
|
||||
collectionView.asyncDelegate = delegate;
|
||||
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A691A11F47200143C57 /* ViewController.m */; };
|
||||
AC3C4A8E1A11F80C00143C57 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AC3C4A8D1A11F80C00143C57 /* Images.xcassets */; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -34,6 +35,8 @@
|
||||
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>"; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -82,6 +85,8 @@
|
||||
AC3C4A661A11F47200143C57 /* AppDelegate.m */,
|
||||
AC3C4A681A11F47200143C57 /* ViewController.h */,
|
||||
AC3C4A691A11F47200143C57 /* ViewController.m */,
|
||||
FC3FCA7E1C2B1564009F6D6D /* PresentingViewController.h */,
|
||||
FC3FCA7F1C2B1564009F6D6D /* PresentingViewController.m */,
|
||||
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */,
|
||||
AC3C4A611A11F47200143C57 /* Supporting Files */,
|
||||
9B92C87F1BC17D3000EE46B2 /* SupplementaryNode.h */,
|
||||
@@ -125,7 +130,6 @@
|
||||
AC3C4A5B1A11F47200143C57 /* Frameworks */,
|
||||
AC3C4A5C1A11F47200143C57 /* Resources */,
|
||||
A6902C454C7661D0D277AC62 /* Copy Pods Resources */,
|
||||
EC37EEC9933F5786936BFE7C /* Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -196,21 +200,6 @@
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
||||
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 */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -236,6 +225,7 @@
|
||||
25FDEC921BF31EE700CEB123 /* ItemNode.m in Sources */,
|
||||
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */,
|
||||
9B92C8811BC17D3000EE46B2 /* SupplementaryNode.m in Sources */,
|
||||
FC3FCA801C2B1564009F6D6D /* PresentingViewController.m in Sources */,
|
||||
AC3C4A671A11F47200143C57 /* AppDelegate.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>
|
||||
|
||||
#define SIMULATE_WEB_RESPONSE 0
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property (strong, nonatomic) UIWindow *window;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "PresentingViewController.h"
|
||||
#import "ViewController.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
@@ -31,10 +32,14 @@
|
||||
- (void)pushNewViewControllerAnimated:(BOOL)animated
|
||||
{
|
||||
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
|
||||
|
||||
|
||||
#if SIMULATE_WEB_RESPONSE
|
||||
UIViewController *viewController = [[PresentingViewController alloc] init];
|
||||
#else
|
||||
UIViewController *viewController = [[ViewController alloc] init];
|
||||
viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Another Copy" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)];
|
||||
|
||||
#endif
|
||||
|
||||
[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>
|
||||
{
|
||||
ASCollectionView *_collectionView;
|
||||
NSArray *_data;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -37,7 +38,7 @@
|
||||
layout.headerReferenceSize = 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.asyncDelegate = self;
|
||||
_collectionView.backgroundColor = [UIColor whiteColor];
|
||||
@@ -45,8 +46,10 @@
|
||||
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
|
||||
|
||||
#if !SIMULATE_WEB_RESPONSE
|
||||
self.navigationItem.leftItemsSupplementBackButton = YES;
|
||||
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadTapped)];
|
||||
#endif
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -56,6 +59,31 @@
|
||||
[super viewDidLoad];
|
||||
|
||||
[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
|
||||
@@ -101,7 +129,11 @@
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
|
||||
{
|
||||
#if SIMULATE_WEB_RESPONSE
|
||||
return _data == nil ? 0 : 100;
|
||||
#else
|
||||
return 100;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView
|
||||
@@ -125,4 +157,11 @@
|
||||
return UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0);
|
||||
}
|
||||
|
||||
#if SIMULATE_WEB_RESPONSE
|
||||
-(void)dealloc
|
||||
{
|
||||
NSLog(@"ViewController is deallocing");
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@@ -130,7 +130,6 @@
|
||||
AC3C4A5B1A11F47200143C57 /* Frameworks */,
|
||||
AC3C4A5C1A11F47200143C57 /* Resources */,
|
||||
A6902C454C7661D0D277AC62 /* Copy Pods Resources */,
|
||||
EC37EEC9933F5786936BFE7C /* Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -201,21 +200,6 @@
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
||||
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 */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
||||
Reference in New Issue
Block a user