merged upstream

This commit is contained in:
Luke Parham
2015-12-26 01:33:34 -06:00
120 changed files with 1722 additions and 917 deletions

View File

@@ -1,4 +1,5 @@
language: objective-c
osx_image: xcode7.2
before_install:
- brew update
- brew reinstall xctool

Binary file not shown.

View File

@@ -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 */; };
@@ -461,8 +461,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 */; };
@@ -759,6 +764,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; };
@@ -1157,6 +1164,8 @@
25B171EA1C12242700508A7A /* Data Controller */ = {
isa = PBXGroup;
children = (
DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */,
DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */,
251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */,
251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */,
464052191A3F83C40061C0BA /* ASDataController.h */,
@@ -1273,6 +1282,7 @@
257754C01BEE458E00737CA5 /* ASTextNodeWordKerner.h in Headers */,
AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */,
AEEC47E11C20C2DD00EC1693 /* ASVideoNode.h in Headers */,
DE8BEAC11C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */,
205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */,
AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */,
058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */,
@@ -1441,6 +1451,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 */,
@@ -1461,6 +1472,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 */,
@@ -1634,21 +1646,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;
@@ -1727,6 +1724,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 */,
@@ -1858,6 +1856,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 */,
@@ -1964,6 +1963,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@@ -1984,6 +1984,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
INFOPLIST_FILE = AsyncDisplayKitTestHost/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -2071,6 +2072,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
DSTROOT = /tmp/AsyncDisplayKit.dst;
GCC_INPUT_FILETYPE = automatic;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
@@ -2089,6 +2091,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
DSTROOT = /tmp/AsyncDisplayKit.dst;
GCC_INPUT_FILETYPE = automatic;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
@@ -2111,6 +2114,7 @@
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
);
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -2138,6 +2142,7 @@
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
);
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -2166,6 +2171,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -2198,6 +2204,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit-iOS/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";

View File

@@ -6,7 +6,8 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASTextNode.h>
#import <AsyncDisplayKit/ASImageNode.h>
typedef enum : NSUInteger {
ASButtonStateNormal,
@@ -31,6 +32,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;

View File

@@ -7,8 +7,9 @@
*/
#import "ASButtonNode.h"
#import <AsyncDisplayKit/ASThread.h>
#import "ASStackLayoutSpec.h"
#import "ASThread.h"
#import "ASDisplayNode+Subclasses.h"
@interface ASButtonNode ()
{
@@ -39,6 +40,9 @@
_titleNode = [[ASTextNode alloc] init];
_imageNode = [[ASImageNode alloc] init];
_contentHorizontalAlignment = ASAlignmentMiddle;
_contentVerticalAlignment = ASAlignmentCenter;
[self addSubnode:_titleNode];
[self addSubnode:_imageNode];
@@ -195,8 +199,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) {

View File

@@ -8,6 +8,8 @@
#import <AsyncDisplayKit/ASDisplayNode.h>
NS_ASSUME_NONNULL_BEGIN
@class ASCellNode;
typedef NSUInteger ASCellNodeAnimation;
@@ -77,10 +79,10 @@ typedef NSUInteger ASCellNodeAnimation;
* ASCellNode must forward touch events in order for UITableView and UICollectionView tap handling to work. Overriding
* these methods (e.g. for highlighting) requires the super method be called.
*/
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Marks the node as needing layout. Convenience for use whether the view / layer is loaded or not.
@@ -119,3 +121,5 @@ typedef NSUInteger ASCellNodeAnimation;
@property (nonatomic, copy) NSString *text;
@end
NS_ASSUME_NONNULL_END

View File

@@ -167,7 +167,8 @@ static const CGFloat kFontSize = 18.0f;
{
if (!(self = [super init]))
return nil;
_text = @"";
_textNode = [[ASTextNode alloc] init];
[self addSubnode:_textNode];

View File

@@ -8,13 +8,19 @@
#import <AsyncDisplayKit/ASCollectionView.h>
NS_ASSUME_NONNULL_BEGIN
/**
* ASCollectionNode is a node based class that wraps an ASCollectionView. It can be used
* as a subnode of another node, and provide room for many (great) features and improvements later on.
*/
@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;
@@ -63,3 +69,5 @@
- (void)reloadDataImmediately;
@end
NS_ASSUME_NONNULL_END

View File

@@ -9,28 +9,120 @@
#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
{
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
self = [self initWithCollectionViewLayout:nil]; // Will throw an exception for lacking a UICV Layout.
UICollectionViewLayout *nilLayout = nil;
self = [self initWithCollectionViewLayout:nilLayout]; // Will throw an exception for lacking a UICV Layout.
return nil;
}
- (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];

View File

@@ -12,13 +12,15 @@
#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
/**
* Node-based collection view.
*
@@ -27,10 +29,16 @@
*/
@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.
@@ -52,27 +60,6 @@
*/
- (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.
*
@@ -102,7 +89,7 @@
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/
- (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion;
- (void)performBatchAnimated:(BOOL)animated updates:(void (^ _Nullable)())updates completion:(void (^ _Nullable)(BOOL))completion;
/**
* Perform a batch of updates asynchronously. This method must be called from the main thread.
@@ -113,7 +100,7 @@
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/
- (void)performBatchUpdates:(void (^)())updates completion:(void (^)(BOOL))completion;
- (void)performBatchUpdates:(void (^ _Nullable)())updates completion:(void (^ _Nullable)(BOOL))completion;
/**
* Reload everything from scratch, destroying the working range and all cached nodes.
@@ -122,7 +109,7 @@
* the main thread.
* @warning This method is substantially more expensive than UICollectionView's version.
*/
- (void)reloadDataWithCompletion:(void (^)())completion;
- (void)reloadDataWithCompletion:(void (^ _Nullable)())completion;
/**
* Reload everything from scratch, destroying the working range and all cached nodes.
@@ -201,7 +188,7 @@
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
* before this method is called.
*/
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths;
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
/**
* Deletes the items specified by an array of index paths.
@@ -211,7 +198,7 @@
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
* before this method is called.
*/
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths;
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
/**
* Reloads the specified items.
@@ -221,7 +208,7 @@
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
* before this method is called.
*/
- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths;
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
/**
* Moves the item at a specified location to a destination location.
@@ -258,7 +245,7 @@
*
* @returns an array containing the nodes being displayed on screen.
*/
- (NSArray *)visibleNodes;
- (NSArray<ASCellNode *> *)visibleNodes;
/**
* Query the sized node at `indexPath` for its calculatedSize.
@@ -301,7 +288,8 @@
/**
* This is a node-based UICollectionViewDataSource.
*/
@protocol ASCollectionViewDataSource <ASCommonCollectionViewDataSource, NSObject>
#define ASCollectionViewDataSource ASCollectionDataSource
@protocol ASCollectionDataSource <ASCommonCollectionViewDataSource, NSObject>
/**
* Similar to -collectionView:cellForItemAtIndexPath:.
@@ -362,12 +350,13 @@
/**
* This is a node-based UICollectionViewDelegate.
*/
@protocol ASCollectionViewDelegate <ASCommonCollectionViewDelegate, NSObject>
#define ASCollectionViewDelegate ASCollectionDelegate
@protocol ASCollectionDelegate <ASCommonCollectionViewDelegate, NSObject>
@optional
- (void)collectionView:(ASCollectionView *)collectionView willDisplayNodeForItemAtIndexPath:(NSIndexPath *)indexPath;
- (void)collectionView:(ASCollectionView *)collectionView didEndDisplayingNodeForItemAtIndexPath:(NSIndexPath*)indexPath;
- (void)collectionView:(ASCollectionView *)collectionView didEndDisplayingNodeForItemAtIndexPath:(NSIndexPath *)indexPath;
/**
* Receive a message that the collectionView is near the end of its data set and more data should be fetched if
@@ -429,4 +418,12 @@
*/
- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;
@end
@end
@interface ASCollectionView (Deprecated)
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled ASDISPLAYNODE_DEPRECATED;
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,7 +8,8 @@
#import "ASAssert.h"
#import "ASBatchFetching.h"
#import "ASCollectionView.h"
#import "ASDelegateProxy.h"
#import "ASCollectionNode.h"
#import "ASCollectionDataController.h"
#import "ASCollectionViewLayoutController.h"
#import "ASCollectionViewFlowLayoutInspector.h"
@@ -21,87 +22,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 +57,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 +93,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 +108,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 +164,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 +181,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 +192,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 +238,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 +274,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 +353,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 +431,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 +468,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 +488,7 @@ static BOOL _isInterceptedSelector(SEL sel)
direction |= ASScrollDirectionUp;
}
}
return direction;
}
@@ -623,7 +567,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 +587,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 +639,7 @@ static BOOL _isInterceptedSelector(SEL sel)
}
constrainedSize = ASSizeRangeMake(CGSizeZero, maxSize);
}
UIEdgeInsets sectionInset = UIEdgeInsetsZero;
if (_collectionViewLayoutImplementsInsetSection) {
sectionInset = [(UICollectionViewFlowLayout *)self.collectionViewLayout sectionInset];
@@ -704,7 +648,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 +662,7 @@ static BOOL _isInterceptedSelector(SEL sel)
constrainedSize.max.height = MAX(0, constrainedSize.max.height - sectionInset.top - sectionInset.bottom);
}
}
return constrainedSize;
}
@@ -738,7 +682,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 +692,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 +761,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 +776,7 @@ static BOOL _isInterceptedSelector(SEL sel)
}
} completion:completion];
});
[_batchUpdateBlocks removeAllObjects];
_performingBatchUpdates = NO;
}
@@ -840,11 +784,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 +803,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 +822,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 +841,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 +862,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 +878,7 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)requeryNodeSizes
{
_queuedNodeSizeUpdate = NO;
[super performBatchUpdates:^{} completion:nil];
}
@@ -944,7 +888,7 @@ static BOOL _isInterceptedSelector(SEL sel)
{
for (NSArray *section in [_dataController completedNodes]) {
for (ASDisplayNode *node in section) {
[node recursivelyClearContents];
[node exitInterfaceState:ASInterfaceStateDisplay];
}
}
}
@@ -953,7 +897,7 @@ static BOOL _isInterceptedSelector(SEL sel)
{
for (NSArray *section in [_dataController completedNodes]) {
for (ASDisplayNode *node in section) {
[node recursivelyClearFetchedData];
[node exitInterfaceState:ASInterfaceStateFetchData];
}
}
}

View File

@@ -6,6 +6,8 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
NS_ASSUME_NONNULL_BEGIN
/**
* This is a subset of UICollectionViewDataSource.
*
@@ -50,7 +52,9 @@
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender;
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender;
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
@end
NS_ASSUME_NONNULL_END

View File

@@ -6,8 +6,9 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <AsyncDisplayKit/ASControlNode.h>
#import "ASControlNode.h"
NS_ASSUME_NONNULL_BEGIN
/**
* The subclass header _ASControlNode+Subclasses_ defines methods to be
@@ -24,7 +25,7 @@
@param touchEvent An event object encapsulating the information specific to the user event.
@discussion ASControlNode implements this method to send all action messages associated with controlEvents. The list of targets is constructed from prior invocations of addTarget:action:forControlEvents:.
*/
- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(UIEvent *)touchEvent;
- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(nullable UIEvent *)touchEvent;
/**
@abstract Sent to the control when tracking begins.
@@ -32,7 +33,7 @@
@param touchEvent An event object encapsulating the information specific to the user event.
@result YES if the receiver should respond continuously (respond when touch is dragged); NO otherwise.
*/
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent;
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)touchEvent;
/**
@abstract Sent continuously to the control as it tracks a touch within the control's bounds.
@@ -40,20 +41,20 @@
@param touchEvent An event object encapsulating the information specific to the user event.
@result YES if touch tracking should continue; NO otherwise.
*/
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent;
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)touchEvent;
/**
@abstract Sent to the control when tracking should be cancelled.
@param touchEvent An event object encapsulating the information specific to the user event. This parameter may be nil, indicating that the cancelation was caused by something other than an event, such as the display node being removed from its supernode.
*/
- (void)cancelTrackingWithEvent:(UIEvent *)touchEvent;
- (void)cancelTrackingWithEvent:(nullable UIEvent *)touchEvent;
/**
@abstract Sent to the control when the last touch completely ends, telling it to stop tracking.
@param touch The touch that ended.
@param touchEvent An event object encapsulating the information specific to the user event.
*/
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent;
- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)touchEvent;
/**
@abstract Settable version of highlighted property.
@@ -61,3 +62,5 @@
@property (nonatomic, readwrite, assign, getter=isHighlighted) BOOL highlighted;
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,6 +8,7 @@
#import <AsyncDisplayKit/ASDisplayNode.h>
NS_ASSUME_NONNULL_BEGIN
/**
@abstract Kinds of events possible for control nodes.
@@ -75,7 +76,7 @@ typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent)
@param controlEvents A bitmask specifying the control events for which the action message is sent. May not be 0. See "Control Events" for bitmask constants.
@discussion You may call this method multiple times, and you may specify multiple target-action pairs for a particular event. Targets are held weakly.
*/
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEvents;
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEvents;
/**
@abstract Returns the actions that are associated with a target and a particular control event.
@@ -83,7 +84,7 @@ typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent)
@param controlEvent A single constant of type ASControlNodeEvent that specifies a particular user action on the control; for a list of these constants, see "Control Events". May not be 0 or ASControlNodeEventAllEvents.
@result An array of selector names as NSString objects, or nil if there are no action selectors associated with controlEvent.
*/
- (NSArray *)actionsForTarget:(id)target forControlEvent:(ASControlNodeEvent)controlEvent;
- (nullable NSArray<NSString *> *)actionsForTarget:(id)target forControlEvent:(ASControlNodeEvent)controlEvent;
/**
@abstract Returns all target objects associated with the receiver.
@@ -97,13 +98,15 @@ typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent)
@param action A selector identifying an action message. Pass NULL to remove all action messages paired with target.
@param controlEvents A bitmask specifying the control events associated with target and action. See "Control Events" for bitmask constants. May not be 0.
*/
- (void)removeTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEvents;
- (void)removeTarget:(nullable id)target action:(nullable SEL)action forControlEvents:(ASControlNodeEvent)controlEvents;
/**
@abstract Sends the actions for the control events for a particular event.
@param controlEvents A bitmask specifying the control events for which to send actions. See "Control Events" for bitmask constants. May not be 0.
@param event The event which triggered these control actions. May be nil.
*/
- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(UIEvent *)event;
- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(nullable UIEvent *)event;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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, ^

View File

@@ -15,6 +15,8 @@
@class ASLayoutSpec;
NS_ASSUME_NONNULL_BEGIN
/**
* The subclass header _ASDisplayNode+Subclasses_ defines the following methods that either must or can be overriden by
* subclasses of ASDisplayNode.
@@ -37,35 +39,8 @@
@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.
*
@@ -85,7 +60,7 @@
*
* @warning Subclasses must not override this; it returns the last cached layout and is never expensive.
*/
@property (nonatomic, readonly, assign) ASLayout *calculatedLayout;
@property (nullable, nonatomic, readonly, assign) ASLayout *calculatedLayout;
/** @name View Lifecycle */
@@ -190,10 +165,9 @@
*
* @note Called on the display queue and/or main queue (MUST BE THREAD SAFE)
*/
+ (void)drawRect:(CGRect)bounds
withParameters:(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.
@@ -208,7 +182,7 @@
*
* @note Called on the display queue and/or main queue (MUST BE THREAD SAFE)
*/
+ (UIImage *)displayWithParameters:(id<NSObject>)parameters
+ (nullable UIImage *)displayWithParameters:(nullable id<NSObject>)parameters
isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock;
/**
@@ -218,7 +192,7 @@
*
* @note Called on the main thread only
*/
- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer;
- (nullable id<NSObject>)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer;
/**
* @abstract Indicates that the receiver is about to display.
@@ -236,6 +210,33 @@
*/
- (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.
*
@@ -245,6 +246,23 @@
*/
- (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.
@@ -267,7 +285,6 @@
*/
- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Marks the receiver's bounds as needing to be redrawn, with a scale value.
*
@@ -295,6 +312,17 @@
*/
- (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 */
@@ -305,7 +333,7 @@
* @param touches A set of UITouch instances.
* @param event A UIEvent associated with the touch.
*/
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
/**
* @abstract Tells the node when touches moved in its view.
@@ -313,7 +341,7 @@
* @param touches A set of UITouch instances.
* @param event A UIEvent associated with the touch.
*/
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
/**
* @abstract Tells the node when touches ended in its view.
@@ -321,7 +349,7 @@
* @param touches A set of UITouch instances.
* @param event A UIEvent associated with the touch.
*/
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
/**
* @abstract Tells the node when touches was cancelled in its view.
@@ -329,7 +357,7 @@
* @param touches A set of UITouch instances.
* @param event A UIEvent associated with the touch.
*/
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
/** @name Managing Gesture Recognizers */
@@ -359,39 +387,7 @@
* 1) allows sending events to plain UIViews that don't have attached nodes,
* 2) hitTest: is never called before the views are created.
*/
- (UIView *)hitTest:(CGPoint)point withEvent:(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;
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
/** @name Placeholders */
@@ -410,7 +406,8 @@
*
* @note Called on the display queue and/or main queue (MUST BE THREAD SAFE)
*/
- (UIImage *)placeholderImage;
- (nullable UIImage *)placeholderImage;
/** @name Description */
@@ -425,3 +422,5 @@
#define ASDisplayNodeAssertThreadAffinity(viewNode) ASDisplayNodeAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created")
#define ASDisplayNodeCAssertThreadAffinity(viewNode) ASDisplayNodeCAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created")
NS_ASSUME_NONNULL_END

View File

@@ -20,22 +20,22 @@
/**
* UIView creation block. Used to create the backing view of a new display node.
*/
typedef UIView *(^ASDisplayNodeViewBlock)();
typedef UIView * _Nonnull(^ASDisplayNodeViewBlock)();
/**
* UIView creation block. Used to create the backing view of a new display node.
*/
typedef UIViewController *(^ASDisplayNodeViewControllerBlock)();
typedef UIViewController * _Nonnull(^ASDisplayNodeViewControllerBlock)();
/**
* CALayer creation block. Used to create the backing layer of a new display node.
*/
typedef CALayer *(^ASDisplayNodeLayerBlock)();
typedef CALayer * _Nonnull(^ASDisplayNodeLayerBlock)();
/**
* ASDisplayNode loaded callback block. This block is called BEFORE the -didLoad method and is always called on the main thread.
*/
typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node);
typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode * _Nonnull node);
/**
Interface state is available on ASDisplayNode and ASViewController, and
@@ -84,6 +84,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
*
*/
NS_ASSUME_NONNULL_BEGIN
@interface ASDisplayNode : ASDealloc2MainObject <ASLayoutable>
@@ -96,7 +97,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
* @return An ASDisplayNode instance whose view will be a subclass that enables asynchronous rendering, and passes
* through -layout and touch handling methods.
*/
- (id)init;
- (instancetype)init;
/**
@@ -107,7 +108,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
* @return An ASDisplayNode instance that loads its view with the given block that is guaranteed to run on the main
* queue. The view will render synchronously and -layout and touch handling methods on the node will not be called.
*/
- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock;
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock;
/**
* @abstract Alternative initializer with a block to create the backing view.
@@ -118,7 +119,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
* @return An ASDisplayNode instance that loads its view with the given block that is guaranteed to run on the main
* queue. The view will render synchronously and -layout and touch handling methods on the node will not be called.
*/
- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock;
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock;
/**
* @abstract Alternative initializer with a block to create the backing layer.
@@ -128,7 +129,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
* @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main
* queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called.
*/
- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock;
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock;
/**
* @abstract Alternative initializer with a block to create the backing layer.
@@ -139,7 +140,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
* @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main
* queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called.
*/
- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock;
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock;
/** @name Properties */
@@ -147,7 +148,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
/**
* @abstract The name of this node, which will be displayed in `description`. The default value is nil.
*/
@property (atomic, copy) NSString *name;
@property (nullable, atomic, copy) NSString *name;
/**
* @abstract Returns whether the node is synchronous.
@@ -193,7 +194,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
* @warning The first access to it must be on the main thread, and should only be used on the main thread thereafter as
* well.
*/
@property (nonatomic, readonly, retain) CALayer *layer;
@property (nonatomic, readonly, retain) CALayer * _Nonnull layer;
/**
* @abstract Returns the Interface State of the node.
@@ -341,7 +342,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
/**
* @abstract The receiver's immediate subnodes.
*/
@property (nonatomic, readonly, retain) NSArray *subnodes;
@property (nonatomic, readonly, retain) NSArray<ASDisplayNode *> *subnodes;
/**
* @abstract The receiver's supernode.
@@ -500,7 +501,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
*
* @return YES if point is inside the receiver's bounds; otherwise, NO.
*/
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
/** @name Converting Between View Coordinate Systems */
@@ -514,7 +515,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
*
* @return The point converted to the coordinate system of node.
*/
- (CGPoint)convertPoint:(CGPoint)point toNode:(ASDisplayNode *)node;
- (CGPoint)convertPoint:(CGPoint)point toNode:(nullable ASDisplayNode *)node;
/**
@@ -525,7 +526,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
*
* @return The point converted to the local coordinate system (bounds) of the receiver.
*/
- (CGPoint)convertPoint:(CGPoint)point fromNode:(ASDisplayNode *)node;
- (CGPoint)convertPoint:(CGPoint)point fromNode:(nullable ASDisplayNode *)node;
/**
@@ -536,7 +537,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
*
* @return The converted rectangle.
*/
- (CGRect)convertRect:(CGRect)rect toNode:(ASDisplayNode *)node;
- (CGRect)convertRect:(CGRect)rect toNode:(nullable ASDisplayNode *)node;
/**
* @abstract Converts a rectangle from the coordinate system of another node to that of the receiver.
@@ -546,7 +547,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
*
* @return The converted rectangle.
*/
- (CGRect)convertRect:(CGRect)rect fromNode:(ASDisplayNode *)node;
- (CGRect)convertRect:(CGRect)rect fromNode:(nullable ASDisplayNode *)node;
@end
@@ -566,7 +567,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
@end
NS_ASSUME_NONNULL_END
/**
* ## UIView bridge
*
@@ -597,7 +598,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
*/
- (void)setNeedsLayout;
@property (atomic, retain) id contents; // default=nil
@property (atomic, retain, nullable) id contents; // default=nil
@property (atomic, assign) BOOL clipsToBounds; // default==NO
@property (atomic, getter=isOpaque) BOOL opaque; // default==YES
@@ -625,9 +626,9 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
* @discussion In contrast to UIView, setting a transparent color will not set opaque = NO.
* This only affects nodes that implement +drawRect like ASTextNode.
*/
@property (atomic, retain) UIColor *backgroundColor; // default=nil
@property (atomic, retain, nullable) UIColor *backgroundColor; // default=nil
@property (atomic, retain) UIColor *tintColor; // default=Blue
@property (atomic, retain, null_resettable) UIColor *tintColor; // default=Blue
- (void)tintColorDidChange; // Notifies the node when the tintColor has changed.
/**
@@ -642,12 +643,12 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
@property (atomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // default=YES (NO for layer-backed nodes)
@property (atomic, assign, getter=isExclusiveTouch) BOOL exclusiveTouch; // default=NO
@property (atomic, assign) CGColorRef shadowColor; // default=opaque rgb black
@property (atomic, assign, nullable) CGColorRef shadowColor; // default=opaque rgb black
@property (atomic, assign) CGFloat shadowOpacity; // default=0.0
@property (atomic, assign) CGSize shadowOffset; // default=(0, -3)
@property (atomic, assign) CGFloat shadowRadius; // default=3
@property (atomic, assign) CGFloat borderWidth; // default=0
@property (atomic, assign) CGColorRef borderColor; // default=opaque rgb black
@property (atomic, assign, nullable) CGColorRef borderColor; // default=opaque rgb black
// UIResponder methods
// By default these fall through to the underlying view, but can be overridden.
@@ -656,22 +657,22 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
- (BOOL)canResignFirstResponder; // default==YES
- (BOOL)resignFirstResponder; // default==NO (no-op)
- (BOOL)isFirstResponder;
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender;
- (BOOL)canPerformAction:(nonnull SEL)action withSender:(nonnull id)sender;
// Accessibility support
@property (atomic, assign) BOOL isAccessibilityElement;
@property (atomic, copy) NSString *accessibilityLabel;
@property (atomic, copy) NSString *accessibilityHint;
@property (atomic, copy) NSString *accessibilityValue;
@property (nullable, atomic, copy) NSString *accessibilityLabel;
@property (nullable, atomic, copy) NSString *accessibilityHint;
@property (nullable, atomic, copy) NSString *accessibilityValue;
@property (atomic, assign) UIAccessibilityTraits accessibilityTraits;
@property (atomic, assign) CGRect accessibilityFrame;
@property (atomic, retain) NSString *accessibilityLanguage;
@property (nullable, atomic, retain) NSString *accessibilityLanguage;
@property (atomic, assign) BOOL accessibilityElementsHidden;
@property (atomic, assign) BOOL accessibilityViewIsModal;
@property (atomic, assign) BOOL shouldGroupAccessibilityChildren;
// Accessibility identification support
@property (nonatomic, copy) NSString *accessibilityIdentifier;
@property (nullable, nonatomic, copy) NSString *accessibilityIdentifier;
@end
@@ -689,7 +690,7 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
*
* @param node The node to be added.
*/
- (void)addSubnode:(ASDisplayNode *)node;
- (void)addSubnode:(nonnull ASDisplayNode *)node;
@end
/** CALayer(AsyncDisplayKit) defines convenience method for adding sub-ASDisplayNode to a CALayer. */
@@ -699,9 +700,10 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
*
* @param node The node to be added.
*/
- (void)addSubnode:(ASDisplayNode *)node;
- (void)addSubnode:(nonnull ASDisplayNode *)node;
@end
@interface ASDisplayNode (Deprecated)
- (void)reclaimMemory ASDISPLAYNODE_DEPRECATED;

View File

@@ -6,7 +6,6 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ASDisplayNode.h"
#import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+Subclasses.h"
#import "ASDisplayNode+FrameworkPrivate.h"
@@ -49,6 +48,9 @@
#define TIME_SCOPED(outVar)
#endif
@interface ASDisplayNode () <_ASDisplayLayerDelegate>
@end
@implementation ASDisplayNode
// these dynamic properties all defined in ASLayoutOptionsPrivate.m
@@ -1470,9 +1472,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 +1504,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 +1724,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 +1782,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 +2150,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 +2319,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 +2327,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)

View File

@@ -12,17 +12,19 @@
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
NS_ASSUME_NONNULL_BEGIN
ASDISPLAYNODE_EXTERN_C_BEGIN
/**
Given a layer, returns the associated display node, if any.
*/
extern ASDisplayNode *ASLayerToDisplayNode(CALayer *layer);
extern ASDisplayNode * _Nullable ASLayerToDisplayNode(CALayer * _Nullable layer);
/**
Given a view, returns the associated display node, if any.
*/
extern ASDisplayNode *ASViewToDisplayNode(UIView *view);
extern ASDisplayNode * _Nullable ASViewToDisplayNode(UIView * _Nullable view);
/**
Given a node, returns the root of the node heirarchy (where supernode == nil)
@@ -35,7 +37,7 @@ extern ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node);
In the event that a node's backing layer is not created yet, the function will only walk the direct subnodes instead
of forcing the layer heirarchy to be created.
*/
extern void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *node, void(^block)(ASDisplayNode *node));
extern void ASDisplayNodePerformBlockOnEveryNode(CALayer * _Nullable layer, ASDisplayNode * _Nullable node, void(^block)(ASDisplayNode *node));
/**
Identical to ASDisplayNodePerformBlockOnEveryNode, except it does not run the block on the
@@ -46,12 +48,12 @@ extern void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, void(^b
/**
Given a display node, traverses up the layer tree hierarchy, returning the first display node that passes block.
*/
extern id ASDisplayNodeFind(ASDisplayNode *node, BOOL (^block)(ASDisplayNode *node));
extern id _Nullable ASDisplayNodeFind(ASDisplayNode * _Nullable node, BOOL (^block)(ASDisplayNode *node));
/**
Given a display node, traverses up the layer tree hierarchy, returning the first display node of kind class.
*/
extern id ASDisplayNodeFindClass(ASDisplayNode *start, Class c);
extern id _Nullable ASDisplayNodeFindClass(ASDisplayNode *start, Class c);
/**
* Given two nodes, finds their most immediate common parent. Used for geometry conversion methods.
@@ -61,32 +63,32 @@ extern id ASDisplayNodeFindClass(ASDisplayNode *start, Class c);
* undefined and undocumented behavior of UIKit in ASDisplayNode, this operation is defined to be incorrect in all
* circumstances and must be fixed wherever encountered.
*/
extern ASDisplayNode *ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2);
extern ASDisplayNode * _Nullable ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2);
/**
Given a display node, collects all descendents. This is a specialization of ASCollectContainer() that walks the Core Animation layer tree as opposed to the display node tree, thus supporting non-continues display node hierarchies.
*/
extern NSArray *ASCollectDisplayNodes(ASDisplayNode *node);
extern NSArray<ASDisplayNode *> *ASCollectDisplayNodes(ASDisplayNode *node);
/**
Given a display node, traverses down the node hierarchy, returning all the display nodes that pass the block.
*/
extern NSArray *ASDisplayNodeFindAllSubnodes(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node));
extern NSArray<ASDisplayNode *> *ASDisplayNodeFindAllSubnodes(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node));
/**
Given a display node, traverses down the node hierarchy, returning all the display nodes of kind class.
*/
extern NSArray *ASDisplayNodeFindAllSubnodesOfClass(ASDisplayNode *start, Class c);
extern NSArray<ASDisplayNode *> *ASDisplayNodeFindAllSubnodesOfClass(ASDisplayNode *start, Class c);
/**
Given a display node, traverses down the node hierarchy, returning the depth-first display node that pass the block.
*/
extern id ASDisplayNodeFindFirstSubnode(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node));
extern __kindof ASDisplayNode * ASDisplayNodeFindFirstSubnode(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node));
/**
Given a display node, traverses down the node hierarchy, returning the depth-first display node of kind class.
*/
extern id ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c);
extern __kindof ASDisplayNode * ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c);
extern UIColor *ASDisplayNodeDefaultPlaceholderColor();
extern UIColor *ASDisplayNodeDefaultTintColor();
@@ -98,3 +100,5 @@ extern void ASDisplayNodeDisableHierarchyNotifications(ASDisplayNode *node);
extern void ASDisplayNodeEnableHierarchyNotifications(ASDisplayNode *node);
ASDISPLAYNODE_EXTERN_C_END
NS_ASSUME_NONNULL_END

View File

@@ -87,7 +87,7 @@ static void _ASCollectDisplayNodes(NSMutableArray *array, CALayer *layer)
_ASCollectDisplayNodes(array, sublayer);
}
extern NSArray *ASCollectDisplayNodes(ASDisplayNode *node)
extern NSArray<ASDisplayNode *> *ASCollectDisplayNodes(ASDisplayNode *node)
{
NSMutableArray *list = [NSMutableArray array];
for (CALayer *sublayer in node.layer.sublayers) {
@@ -112,14 +112,14 @@ static void _ASDisplayNodeFindAllSubnodes(NSMutableArray *array, ASDisplayNode *
}
}
extern NSArray *ASDisplayNodeFindAllSubnodes(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node))
extern NSArray<ASDisplayNode *> *ASDisplayNodeFindAllSubnodes(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node))
{
NSMutableArray *list = [NSMutableArray array];
_ASDisplayNodeFindAllSubnodes(list, start, block);
return list;
}
extern NSArray *ASDisplayNodeFindAllSubnodesOfClass(ASDisplayNode *start, Class c)
extern NSArray<ASDisplayNode *> *ASDisplayNodeFindAllSubnodesOfClass(ASDisplayNode *start, Class c)
{
return ASDisplayNodeFindAllSubnodes(start, ^(ASDisplayNode *n) {
return [n isKindOfClass:c];
@@ -143,12 +143,12 @@ static ASDisplayNode *_ASDisplayNodeFindFirstSubnode(ASDisplayNode *startNode, B
return nil;
}
extern id ASDisplayNodeFindFirstSubnode(ASDisplayNode *startNode, BOOL (^block)(ASDisplayNode *node))
extern __kindof ASDisplayNode * ASDisplayNodeFindFirstSubnode(ASDisplayNode *startNode, BOOL (^block)(ASDisplayNode *node))
{
return _ASDisplayNodeFindFirstSubnode(startNode, NO, block);
}
extern id ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c)
extern __kindof ASDisplayNode * ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c)
{
return ASDisplayNodeFindFirstSubnode(start, ^(ASDisplayNode *n) {
return [n isKindOfClass:c];

View File

@@ -8,6 +8,7 @@
#import <AsyncDisplayKit/ASDisplayNode.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASEditableTextNodeDelegate;
@@ -29,7 +30,7 @@
@property (nonatomic, readonly, strong) UITextView *textView;
//! @abstract The attributes to apply to new text being entered by the user.
@property (nonatomic, readwrite, strong) NSDictionary *typingAttributes;
@property (nonatomic, readwrite, strong, nullable) NSDictionary<NSString *, id> *typingAttributes;
//! @abstract The range of text currently selected. If length is zero, the range is the cursor location.
@property (nonatomic, readwrite, assign) NSRange selectedRange;
@@ -46,14 +47,14 @@
@abstract The styled placeholder text displayed by the text node while no text is entered
@discussion The placeholder is displayed when the user has not entered any text and the keyboard is not visible.
*/
@property (nonatomic, readwrite, strong) NSAttributedString *attributedPlaceholderText;
@property (nonatomic, readwrite, strong, nullable) NSAttributedString *attributedPlaceholderText;
#pragma mark - Modifying User Text
/**
@abstract The styled text displayed by the receiver.
@discussion When the placeholder is displayed (as indicated by -isDisplayingPlaceholder), this value is nil. Otherwise, this value is the attributed text the user has entered. This value can be modified regardless of whether the receiver is the first responder (and thus, editing) or not. Changing this value from nil to non-nil will result in the placeholder being hidden, and the new value being displayed.
*/
@property (nonatomic, readwrite, copy) NSAttributedString *attributedText;
@property (nonatomic, readwrite, copy, nullable) NSAttributedString *attributedText;
#pragma mark - Managing The Keyboard
//! @abstract The text input mode used by the receiver's keyboard, if it is visible. This value is undefined if the receiver is not the first responder.
@@ -144,3 +145,5 @@
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,6 +8,7 @@
#import <AsyncDisplayKit/ASControlNode.h>
NS_ASSUME_NONNULL_BEGIN
/**
* Image modification block. Use to transform an image before display.
@@ -16,7 +17,7 @@
*
* @returns A transformed image.
*/
typedef UIImage *(^asimagenode_modification_block_t)(UIImage *image);
typedef UIImage * _Nullable (^asimagenode_modification_block_t)(UIImage *image);
/**
@@ -32,12 +33,12 @@ typedef UIImage *(^asimagenode_modification_block_t)(UIImage *image);
* the layer's contentsCenter property. Non-stretchable images work too, of
* course.
*/
@property (atomic, retain) UIImage *image;
@property (nullable, atomic, retain) UIImage *image;
/**
@abstract The placeholder color.
*/
@property (nonatomic, strong) UIColor *placeholderColor;
@property (nullable, nonatomic, strong) UIColor *placeholderColor;
/**
* @abstract Indicates whether efficient cropping of the receiver is enabled.
@@ -85,7 +86,7 @@ typedef UIImage *(^asimagenode_modification_block_t)(UIImage *image);
* @discussion Can be used to add image effects (such as rounding, adding
* borders, or other pattern overlays) without extraneous display calls.
*/
@property (nonatomic, readwrite, copy) asimagenode_modification_block_t imageModificationBlock;
@property (nullable, nonatomic, readwrite, copy) asimagenode_modification_block_t imageModificationBlock;
/**
* @abstract Marks the receiver as needing display and performs a block after
@@ -99,7 +100,7 @@ typedef UIImage *(^asimagenode_modification_block_t)(UIImage *image);
* `displaySuspended` is YES, `displayCompletionBlock` is will be
* performed immediately and `YES` will be passed for `canceled`.
*/
- (void)setNeedsDisplayWithCompletion:(void (^)(BOOL canceled))displayCompletionBlock;
- (void)setNeedsDisplayWithCompletion:(void (^ _Nullable)(BOOL canceled))displayCompletionBlock;
@end
@@ -131,3 +132,4 @@ asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(CGFloat
asimagenode_modification_block_t ASImageNodeTintColorModificationBlock(UIColor *color);
ASDISPLAYNODE_EXTERN_C_END
NS_ASSUME_NONNULL_END

View File

@@ -267,7 +267,7 @@
}
#pragma mark -
- (void)setNeedsDisplayWithCompletion:(void (^)(BOOL canceled))displayCompletionBlock
- (void)setNeedsDisplayWithCompletion:(void (^ _Nullable)(BOOL canceled))displayCompletionBlock
{
if (self.displaySuspended) {
if (displayCompletionBlock)

View File

@@ -6,23 +6,25 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASImageNode.h>
#import <MapKit/MapKit.h>
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;
/**
This is the MKMapView that is the live map part of ASMapNode. This will be nil if .liveMap = NO. Note, MKMapView is *not* thread-safe.
*/
@property (nonatomic, readonly) MKMapView *mapView;
@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;
@@ -41,6 +43,8 @@
* @discussion This method set the annotations of the static map view and also to the live map view. Passing an empty array clears the map of any annotations.
* @param annotations An array of objects that conform to the MKAnnotation protocol
*/
- (void)setAnnotations:(NSArray *)annotations;
- (void)setAnnotations:(NSArray<id<MKAnnotation>> *)annotations;
@end
NS_ASSUME_NONNULL_END

View File

@@ -7,6 +7,8 @@
*/
#import "ASMapNode.h"
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
#import <AsyncDisplayKit/ASCenterLayoutSpec.h>
#import <AsyncDisplayKit/ASThread.h>
@@ -26,6 +28,7 @@
@synthesize needsMapReloadOnBoundsChange = _needsMapReloadOnBoundsChange;
@synthesize mapDelegate = _mapDelegate;
@synthesize region = _region;
@synthesize liveMap = _liveMap;
#pragma mark - Lifecycle
- (instancetype)init
@@ -37,9 +40,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 +55,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 +89,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 +141,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 +207,6 @@
if (CLLocationCoordinate2DIsValid(_centerCoordinateOfMap)) {
[_mapView setCenterCoordinate:_centerCoordinateOfMap];
} else {
_centerCoordinateOfMap = _options.region.center;
}
}
}

View File

@@ -11,9 +11,13 @@
#import <Photos/Photos.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASMultiplexImageNodeDelegate;
@protocol ASMultiplexImageNodeDataSource;
typedef id<NSCopying, NSObject> ASImageIdentifier;
extern NSString *const ASMultiplexImageNodeErrorDomain;
/**
@@ -50,8 +54,7 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) {
* @abstract ASMultiplexImageNode is an image node that can load and display multiple versions of an image. For
* example, it can display a low-resolution version of an image while the high-resolution version is loading.
*
* @discussion ASMultiplexImageNode begins loading images when its <imageIdentifiers> property is set. For each image
* identifier, the data source can either return a UIImage directly, or a URL the image node should load.
* @discussion ASMultiplexImageNode begins loading images when its <esource can either return a UIImage directly, or a URL the image node should load.
*/
@interface ASMultiplexImageNode : ASImageNode
@@ -62,7 +65,7 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) {
* @discussion If `cache` is nil, the receiver will not attempt to retrieve images from a cache before downloading them.
* @returns An initialized ASMultiplexImageNode.
*/
- (instancetype)initWithCache:(id<ASImageCacheProtocol>)cache downloader:(id<ASImageDownloaderProtocol>)downloader NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithCache:(nullable id<ASImageCacheProtocol>)cache downloader:(nullable id<ASImageDownloaderProtocol>)downloader NS_DESIGNATED_INITIALIZER;
/**
* @abstract The delegate, which must conform to the <ASMultiplexImageNodeDelegate> protocol.
@@ -92,10 +95,10 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) {
*
* @see <downloadsIntermediateImages> for more information on the image loading process.
*/
@property (nonatomic, readwrite, copy) NSArray *imageIdentifiers;
@property (nonatomic, readwrite, copy) NSArray<ASImageIdentifier> *imageIdentifiers;
/**
* @abstract Notify the receiver that its data source has new UIImages or NSURLs available for <imageIdentifiers>.
* @abstract Notify the receiver SSAAthat its data source has new UIImages or NSURLs available for <imageIdentifiers>.
*
* @discussion If a higher-quality image than is currently displayed is now available, it will be loaded.
*/
@@ -106,12 +109,12 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) {
*
* @discussion This value may differ from <displayedImageIdentifier> if the image hasn't yet been displayed.
*/
@property (nonatomic, readonly) id loadedImageIdentifier;
@property (nullable, nonatomic, readonly) ASImageIdentifier loadedImageIdentifier;
/**
* @abstract The identifier for the image that the receiver is currently displaying, or nil.
*/
@property (nonatomic, readonly) id displayedImageIdentifier;
@property (nullable, nonatomic, readonly) ASImageIdentifier displayedImageIdentifier;
/**
* @abstract The image manager that this image node should use when requesting images from the Photos framework. If this is `nil` (the default), then `PHImageManager.defaultManager` is used.
@@ -146,7 +149,7 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) {
*/
- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode
didUpdateDownloadProgress:(CGFloat)downloadProgress
forImageWithIdentifier:(id)imageIdentifier;
forImageWithIdentifier:(ASImageIdentifier)imageIdentifier;
/**
* @abstract Notification that the image node's download has finished.
@@ -155,8 +158,8 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) {
* @param error The error that occurred while downloading, if one occurred; nil otherwise.
*/
- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode
didFinishDownloadingImageWithIdentifier:(id)imageIdentifier
error:(NSError *)error;
didFinishDownloadingImageWithIdentifier:(ASImageIdentifier)imageIdentifier
error:(nullable NSError *)error;
/**
* @abstract Notification that the image node's image was updated.
@@ -169,10 +172,10 @@ didFinishDownloadingImageWithIdentifier:(id)imageIdentifier
* @see <[ASMultiplexImageNodeDelegate multiplexImageNode:didDisplayUpdatedImage:withIdentifier:]>.
*/
- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode
didUpdateImage:(UIImage *)image
withIdentifier:(id)imageIdentifier
fromImage:(UIImage *)previousImage
withIdentifier:(id)previousImageIdentifier;
didUpdateImage:(nullable UIImage *)image
withIdentifier:(nullable ASImageIdentifier)imageIdentifier
fromImage:(nullable UIImage *)previousImage
withIdentifier:(nullable ASImageIdentifier)previousImageIdentifier;
/**
* @abstract Notification that the image node displayed a new image.
@@ -182,8 +185,8 @@ didFinishDownloadingImageWithIdentifier:(id)imageIdentifier
* @discussion This method is only called when `image` changes, and not on subsequent redisplays of the same image.
*/
- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode
didDisplayUpdatedImage:(UIImage *)image
withIdentifier:(id)imageIdentifier;
didDisplayUpdatedImage:(nullable UIImage *)image
withIdentifier:(nullable ASImageIdentifier)imageIdentifier;
/**
* @abstract Notification that the image node finished displaying an image.
@@ -211,7 +214,7 @@ didFinishDownloadingImageWithIdentifier:(id)imageIdentifier
* URL to the image via -multiplexImageNode:URLForImageIdentifier:.
* @returns A UIImage corresponding to `imageIdentifier`, or nil if none is available.
*/
- (UIImage *)multiplexImageNode:(ASMultiplexImageNode *)imageNode imageForImageIdentifier:(id)imageIdentifier;
- (nullable UIImage *)multiplexImageNode:(ASMultiplexImageNode *)imageNode imageForImageIdentifier:(ASImageIdentifier)imageIdentifier;
/**
* @abstract An image URL for the specified identifier.
@@ -224,7 +227,7 @@ didFinishDownloadingImageWithIdentifier:(id)imageIdentifier
* @return An NSURL for the image identified by `imageIdentifier`, or nil if none is available.
* @see `+[NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:]` below.
*/
- (NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode URLForImageIdentifier:(id)imageIdentifier;
- (nullable NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode URLForImageIdentifier:(ASImageIdentifier)imageIdentifier;
/**
* @abstract A PHAsset for the specific asset local identifier
@@ -236,7 +239,7 @@ didFinishDownloadingImageWithIdentifier:(id)imageIdentifier
* @note This method may be called from any thread.
* @return A PHAsset corresponding to `assetLocalIdentifier`, or nil if none is available.
*/
- (PHAsset *)multiplexImageNode:(ASMultiplexImageNode *)imageNode assetForLocalIdentifier:(NSString *)assetLocalIdentifier;
- (nullable PHAsset *)multiplexImageNode:(ASMultiplexImageNode *)imageNode assetForLocalIdentifier:(NSString *)assetLocalIdentifier;
@end
@@ -257,4 +260,6 @@ didFinishDownloadingImageWithIdentifier:(id)imageIdentifier
contentMode:(PHImageContentMode)contentMode
options:(PHImageRequestOptions *)options;
@end
@end
NS_ASSUME_NONNULL_END

View File

@@ -9,6 +9,7 @@
#import <AsyncDisplayKit/ASImageNode.h>
#import <AsyncDisplayKit/ASImageProtocols.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASNetworkImageNodeDelegate;
@@ -32,7 +33,7 @@
*
* @returns An initialized ASNetworkImageNode.
*/
- (instancetype)initWithCache:(id<ASImageCacheProtocol>)cache downloader:(id<ASImageDownloaderProtocol>)downloader;
- (instancetype)initWithCache:(nullable id<ASImageCacheProtocol>)cache downloader:(id<ASImageDownloaderProtocol>)downloader NS_DESIGNATED_INITIALIZER;
/**
* Convenience initialiser.
@@ -49,14 +50,14 @@
/**
* A placeholder image to display while the URL is loading.
*/
@property (atomic, strong, readwrite) UIImage *defaultImage;
@property (nullable, atomic, strong, readwrite) UIImage *defaultImage;
/**
* The URL of a new image to download and display.
*
* @discussion Changing this property will reset the displayed image to a placeholder (<defaultImage>) while loading.
*/
@property (atomic, strong, readwrite) NSURL *URL;
@property (nullable, atomic, strong, readwrite) NSURL *URL;
/**
* Download and display a new image.
@@ -65,7 +66,7 @@
*
* @param reset Whether to display a placeholder (<defaultImage>) while loading the new image.
*/
- (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset;
- (void)setURL:(nullable NSURL *)URL resetToDefault:(BOOL)reset;
/**
* If <URL> is a local file, set this property to YES to take advantage of UIKit's image cacheing. Defaults to YES.
@@ -102,3 +103,5 @@
- (void)imageNodeDidFinishDecoding:(ASNetworkImageNode *)imageNode;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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

View File

@@ -7,41 +7,76 @@
//
#import "ASPagerNode.h"
#import "ASDelegateProxy.h"
#import "ASDisplayNode+Subclasses.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 +96,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

View File

@@ -10,6 +10,7 @@
#import <AsyncDisplayKit/ASDisplayNode.h>
NS_ASSUME_NONNULL_BEGIN
/**
* Simple node that wraps UIScrollView.
@@ -22,3 +23,5 @@
@property (nonatomic, readonly, strong) UIScrollView *view;
@end
NS_ASSUME_NONNULL_END

View File

@@ -6,7 +6,7 @@
// Copyright © 2015 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASTableView.h>
/**
* ASTableNode is a node based class that wraps an ASTableView. It can be used
@@ -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

View File

@@ -6,20 +6,109 @@
// Copyright © 2015 Facebook. All rights reserved.
//
#import "ASFlowLayoutController.h"
#import "ASTableNode.h"
#import "ASDisplayNode+Subclasses.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];

View File

@@ -7,17 +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;
@protocol ASTableDataSource;
@protocol ASTableDelegate;
/**
* Node-based table view.
@@ -27,8 +26,8 @@
*/
@interface ASTableView : UITableView
@property (nonatomic, weak) id<ASTableViewDelegate> asyncDelegate; // must not be nil
@property (nonatomic, weak) id<ASTableViewDataSource> asyncDataSource;
@property (nonatomic, weak) id<ASTableDelegate> asyncDelegate;
@property (nonatomic, weak) id<ASTableDataSource> asyncDataSource;
/**
* Initializer.
@@ -84,7 +83,7 @@
* the main thread.
* @warning This method is substantially more expensive than UITableView's version.
*/
-(void)reloadDataWithCompletion:(void (^)())completion;
-(void)reloadDataWithCompletion:(void (^ _Nullable)())completion;
/**
* Reload everything from scratch, destroying the working range and all cached nodes.
@@ -128,7 +127,7 @@
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion;
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL completed))completion;
/**
* Inserts one or more sections, with an option to animate the insertion.
@@ -188,7 +187,7 @@
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
* before this method is called.
*/
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
/**
* Deletes the rows specified by an array of index paths, with an option to animate the deletion.
@@ -200,7 +199,7 @@
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
* before this method is called.
*/
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
/**
* Reloads the specified rows using a given animation effect.
@@ -212,7 +211,7 @@
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
* before this method is called.
*/
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
/**
* Moves the row at a specified location to a destination location.
@@ -242,14 +241,14 @@
*
* @returns an indexPath for this cellNode
*/
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode;
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode;
/**
* Similar to -visibleCells.
*
* @returns an array containing the nodes being displayed on screen.
*/
- (NSArray *)visibleNodes;
- (NSArray<ASDisplayNode *> *)visibleNodes;
/**
* YES to automatically adjust the contentOffset when cells are inserted or deleted "before"
@@ -280,7 +279,7 @@
/**
* This is a node-based UITableViewDataSource.
*/
@protocol ASTableViewDataSource <ASCommonTableViewDataSource, NSObject>
@protocol ASTableDataSource <ASCommonTableViewDataSource, NSObject>
/**
* Similar to -tableView:cellForRowAtIndexPath:.
@@ -317,6 +316,8 @@
@end
@protocol ASTableViewDataSource <ASTableDataSource>
@end
/**
* This is a node-based UITableViewDelegate.
@@ -324,12 +325,12 @@
* 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
- (void)tableView:(ASTableView *)tableView willDisplayNodeForRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(ASTableView *)tableView didEndDisplayingNodeForRowAtIndexPath:(NSIndexPath*)indexPath;
- (void)tableView:(ASTableView *)tableView didEndDisplayingNodeForRowAtIndexPath:(NSIndexPath *)indexPath;
/**
* Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary.
@@ -358,4 +359,9 @@
*/
- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView;
@end
@end
@protocol ASTableViewDelegate <ASTableDelegate>
@end
NS_ASSUME_NONNULL_END

View File

@@ -6,105 +6,26 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ASTableView.h"
#import "ASTableViewInternal.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 +77,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 +104,7 @@ static BOOL _isInterceptedSelector(SEL sel)
CGFloat _nodesConstrainedWidth;
BOOL _ignoreNodesConstrainedWidthChange;
BOOL _queuedNodeHeightUpdate;
BOOL _isDeallocating;
}
@property (atomic, assign) BOOL asyncDataSourceLocked;
@@ -224,6 +149,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 +164,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 +225,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 +245,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];
}
}

View File

@@ -6,7 +6,7 @@
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import "ASTableView.h"
#import "ASTableNode.h"
@class ASDataController;

View File

@@ -6,6 +6,8 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
NS_ASSUME_NONNULL_BEGIN
/**
* This is a subset of UITableViewDataSource.
*
@@ -21,14 +23,14 @@
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath;
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView;
- (nullable NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView;
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index;
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath;
@@ -55,8 +57,8 @@
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;
- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath;
@@ -64,14 +66,14 @@
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath;
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath;
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath;
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath;
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath;
- (nullable NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath;
- (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath;
@@ -83,7 +85,9 @@
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender;
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender;
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,6 +8,8 @@
#import <AsyncDisplayKit/ASControlNode.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASTextNodeDelegate;
/**
@@ -36,7 +38,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
@discussion Defaults to nil, no text is shown.
For inline image attachments, add an attribute of key NSAttachmentAttributeName, with a value of an NSTextAttachment.
*/
@property (nonatomic, copy) NSAttributedString *attributedString;
@property (nullable, nonatomic, copy) NSAttributedString *attributedString;
#pragma mark - Truncation
@@ -44,14 +46,14 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
@abstract The attributedString to use when the text must be truncated.
@discussion Defaults to a localized ellipsis character.
*/
@property (nonatomic, copy) NSAttributedString *truncationAttributedString;
@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedString;
/**
@summary The second attributed string appended for truncation.
@discussion This string will be highlighted on touches.
@default nil
*/
@property (nonatomic, copy) NSAttributedString *additionalTruncationMessage;
@property (nullable, nonatomic, copy) NSAttributedString *additionalTruncationMessage;
/**
@abstract Determines how the text is truncated to fit within the receiver's maximum size.
@@ -75,7 +77,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
*/
@property (nonatomic, readonly, assign) NSUInteger lineCount;
@property (nonatomic, strong) NSArray *exclusionPaths;
@property (nullable, nonatomic, strong) NSArray<UIBezierPath *> *exclusionPaths;
#pragma mark - Placeholders
@@ -91,7 +93,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
/**
@abstract The placeholder color.
*/
@property (nonatomic, strong) UIColor *placeholderColor;
@property (nullable, nonatomic, strong) UIColor *placeholderColor;
/**
@abstract Inset each line of the placeholder.
@@ -127,7 +129,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
a line break, the rects returned will be on opposite sides and different lines). The rects returned
are in the coordinate system of the receiver.
*/
- (NSArray *)rectsForTextRange:(NSRange)textRange;
- (NSArray<NSValue *> *)rectsForTextRange:(NSRange)textRange;
/**
@abstract Returns an array of rects used for highlighting the characters in a given text range.
@@ -138,7 +140,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
are in the coordinate system of the receiver. This method is useful for visual coordination with a
highlighted range of text.
*/
- (NSArray *)highlightRectsForTextRange:(NSRange)textRange;
- (NSArray<NSValue *> *)highlightRectsForTextRange:(NSRange)textRange;
/**
@abstract Returns a bounding rect for the given text range.
@@ -162,7 +164,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
/**
@abstract The set of attribute names to consider links. Defaults to NSLinkAttributeName.
*/
@property (nonatomic, copy) NSArray *linkAttributeNames;
@property (nonatomic, copy) NSArray<NSString *> *linkAttributeNames;
/**
@abstract Indicates whether the receiver has an entity at a given point.
@@ -171,7 +173,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
@param rangeOut The ultimate range of the found text. Can be NULL.
@result YES if an entity exists at `point`; NO otherwise.
*/
- (id)linkAttributeValueAtPoint:(CGPoint)point attributeName:(out NSString **)attributeNameOut range:(out NSRange *)rangeOut;
- (nullable id)linkAttributeValueAtPoint:(CGPoint)point attributeName:(out NSString * _Nullable * _Nullable)attributeNameOut range:(out NSRange * _Nullable)rangeOut;
/**
@abstract The style to use when highlighting text.
@@ -267,3 +269,5 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
- (BOOL)textNode:(ASTextNode *)textNode shouldLongPressLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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;
}

View File

@@ -9,16 +9,27 @@
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
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.
@@ -30,3 +41,5 @@
- (ASSizeRange)nodeConstrainedSize;
@end
NS_ASSUME_NONNULL_END

View File

@@ -16,6 +16,18 @@
BOOL _ensureDisplayed;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
ASDisplayNodeAssert(NO, @"ASViewController requires using -initWithNode:");
return [self initWithNode:[[ASDisplayNode alloc] init]];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
ASDisplayNodeAssert(NO, @"ASViewController requires using -initWithNode:");
return [self initWithNode:[[ASDisplayNode alloc] init]];
}
- (instancetype)initWithNode:(ASDisplayNode *)node
{
if (!(self = [super initWithNibName:nil bundle:nil])) {
@@ -65,4 +77,9 @@
return ASSizeRangeMake(viewSize, viewSize);
}
- (ASInterfaceState)interfaceState
{
return _node.interfaceState;
}
@end

View File

@@ -52,8 +52,6 @@
#import <AsyncDisplayKit/_ASAsyncTransaction.h>
#import <AsyncDisplayKit/_ASAsyncTransactionContainer+Private.h>
#import <AsyncDisplayKit/_ASAsyncTransactionGroup.h>
#import <AsyncDisplayKit/_ASDisplayLayer.h>
#import <AsyncDisplayKit/_ASDisplayView.h>
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASCollectionViewLayoutController.h>
#import <AsyncDisplayKit/ASControlNode+Subclasses.h>

View File

@@ -9,6 +9,8 @@
#import <AsyncDisplayKit/ASLayoutController.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
@interface ASAbstractLayoutController : NSObject <ASLayoutController>
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
@@ -16,3 +18,5 @@
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,6 +8,7 @@
#import <AsyncDisplayKit/ASImageProtocols.h>
NS_ASSUME_NONNULL_BEGIN
/**
* @abstract Simple NSURLSession-based image downloader.
@@ -20,3 +21,5 @@
- (instancetype)init __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used.")));
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,6 +8,8 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* @abstract A context object to notify when batch fetches are finished or cancelled.
*/
@@ -58,3 +60,5 @@
- (void)beginBatchFetching;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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

View File

@@ -9,7 +9,6 @@
#import <UIKit/UIKit.h>
#import "ASCollectionViewFlowLayoutInspector.h"
#import "ASCollectionView.h"
#import "ASAssert.h"
#import "ASEqualityHelpers.h"
@@ -36,7 +35,7 @@
return self;
}
- (void)didChangeCollectionViewDelegate:(id<ASCollectionViewDelegate>)delegate;
- (void)didChangeCollectionViewDelegate:(id<ASCollectionDelegate>)delegate;
{
if (delegate == nil) {
_delegateImplementsReferenceSizeForHeader = NO;

View File

@@ -9,6 +9,8 @@
#import <AsyncDisplayKit/ASAbstractLayoutController.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
@class ASCollectionView;
@interface ASCollectionViewLayoutController : ASAbstractLayoutController
@@ -16,3 +18,5 @@
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView;
@end
NS_ASSUME_NONNULL_END

View File

@@ -6,7 +6,10 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ASDataController.h"
#ifndef ASDataControllerSubclasses_Included
#define ASDataControllerSubclasses_Included
//#import "ASDataController.h"
@interface ASDataController (Subclasses)
@@ -157,3 +160,5 @@
- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection;
@end
#endif

View File

@@ -6,17 +6,21 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#ifndef ASDataController_Included
#define ASDataController_Included
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDealloc2MainObject.h>
#import <AsyncDisplayKit/ASDimension.h>
#import "ASFlowLayoutController.h"
#import <AsyncDisplayKit/ASFlowLayoutController.h>
NS_ASSUME_NONNULL_BEGIN
@class ASCellNode;
@class ASDataController;
FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind;
typedef NSUInteger ASDataControllerAnimationOptions;
FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind;
/**
Data source for data controller
@@ -68,22 +72,22 @@ typedef NSUInteger ASDataControllerAnimationOptions;
Called for batch update.
*/
- (void)dataControllerBeginUpdates:(ASDataController *)dataController;
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion;
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL))completion;
/**
Called for insertion of elements.
*/
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray<ASCellNode *> *)nodes atIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
Called for deletion of elements.
*/
- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray<ASCellNode *> *)nodes atIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
Called for insertion of sections.
*/
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray<NSArray<ASCellNode *> *> *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
Called for deletion of sections.
@@ -140,7 +144,7 @@ typedef NSUInteger ASDataControllerAnimationOptions;
- (void)endUpdates;
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion;
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL))completion;
- (void)insertSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
@@ -150,11 +154,11 @@ typedef NSUInteger ASDataControllerAnimationOptions;
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
* Re-measures all loaded nodes in the backing store.
@@ -166,7 +170,7 @@ typedef NSUInteger ASDataControllerAnimationOptions;
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion;
- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^ _Nullable)())completion;
- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
@@ -176,15 +180,19 @@ typedef NSUInteger ASDataControllerAnimationOptions;
- (NSUInteger)numberOfRowsInSection:(NSUInteger)section;
- (ASCellNode *)nodeAtIndexPath:(NSIndexPath *)indexPath;
- (nullable ASCellNode *)nodeAtIndexPath:(NSIndexPath *)indexPath;
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode;
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode;
- (NSArray *)nodesAtIndexPaths:(NSArray *)indexPaths;
- (NSArray<ASCellNode *> *)nodesAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
/**
* Direct access to the nodes that have completed calculation and layout
*/
- (NSArray *)completedNodes;
- (NSArray<NSArray <ASCellNode *> *> *)completedNodes;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -17,6 +17,7 @@
#import "ASMultidimensionalArrayUtils.h"
#import "ASInternalHelpers.h"
#import "ASLayout.h"
#import "ASFlowLayoutController.h"
//#define LOG(...) NSLog(__VA_ARGS__)
#define LOG(...)

View File

@@ -8,6 +8,7 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* _OBJC_SUPPORTED_INLINE_REFCNT_WITH_DEALLOC2MAIN permits classes to implement their own reference counting and enforce
@@ -19,3 +20,5 @@
- (BOOL)_isDeallocating;
@end
NS_ASSUME_NONNULL_END

View 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

View 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

View File

@@ -9,6 +9,9 @@
#import <AsyncDisplayKit/ASAbstractLayoutController.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
@class ASCellNode;
typedef NS_ENUM(NSUInteger, ASFlowLayoutDirection) {
ASFlowLayoutDirectionVertical,
@@ -17,7 +20,7 @@ typedef NS_ENUM(NSUInteger, ASFlowLayoutDirection) {
@protocol ASFlowLayoutControllerDataSource
- (NSArray *)completedNodes; // This provides access to ASDataController's _completedNodes multidimensional array.
- (NSArray<NSArray <ASCellNode *> *> *)completedNodes; // This provides access to ASDataController's _completedNodes multidimensional array.
@end
@@ -33,3 +36,5 @@ typedef NS_ENUM(NSUInteger, ASFlowLayoutDirection) {
- (instancetype)initWithScrollOption:(ASFlowLayoutDirection)layoutDirection;
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,6 +8,7 @@
#import <QuartzCore/QuartzCore.h>
NS_ASSUME_NONNULL_BEGIN
@interface ASHighlightOverlayLayer : CALayer
@@ -19,16 +20,16 @@
@param rects Array containing CGRects wrapped in NSValue.
@param targetLayer The layer that the rects are relative to. The rects will be translated to the receiver's coordinate space when rendering.
*/
- (id)initWithRects:(NSArray *)rects targetLayer:(id)targetLayer;
- (id)initWithRects:(NSArray<NSValue *> *)rects targetLayer:(nullable CALayer *)targetLayer;
/**
@summary Initializes with CGRects for the highlighting, in the receiver's coordinate space.
@param rects Array containing CGRects wrapped in NSValue.
*/
- (id)initWithRects:(NSArray *)rects;
- (id)initWithRects:(NSArray<NSValue *> *)rects;
@property (atomic, strong) __attribute__((NSObject)) CGColorRef highlightColor;
@property (nullable, atomic, strong) __attribute__((NSObject)) CGColorRef highlightColor;
@property (atomic, weak) CALayer *targetLayer;
@end
@@ -42,3 +43,5 @@
@property (nonatomic, assign, setter=as_setAllowsHighlightDrawing:) BOOL as_allowsHighlightDrawing;
@end
NS_ASSUME_NONNULL_END

View File

@@ -9,6 +9,7 @@
#import <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASImageCacheProtocol <NSObject>
@@ -23,9 +24,9 @@
@discussion If `URL` is nil, `completion` will be invoked immediately with a nil image. This method should not block
the calling thread as it is likely to be called from the main thread.
*/
- (void)fetchCachedImageWithURL:(NSURL *)URL
callbackQueue:(dispatch_queue_t)callbackQueue
completion:(void (^)(CGImageRef imageFromCache))completion;
- (void)fetchCachedImageWithURL:(nullable NSURL *)URL
callbackQueue:(nullable dispatch_queue_t)callbackQueue
completion:(void (^)(CGImageRef _Nullable imageFromCache))completion;
@end
@@ -42,16 +43,14 @@
@param completion The block to be invoked when the download has completed, or has failed.
@param image The image that was downloaded, if the image could be successfully downloaded; nil otherwise.
@param error An error describing why the download of `URL` failed, if the download failed; nil otherwise.
@discussion If `URL` is nil, `completion` will be invoked immediately with a nil image and an error describing why the
download failed. This method is likely to be called on the main thread, so any custom implementations should make
sure to background any expensive download operations.
@discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations.
@result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must
retain the identifier if you wish to use it later.
*/
- (id)downloadImageWithURL:(NSURL *)URL
callbackQueue:(dispatch_queue_t)callbackQueue
downloadProgressBlock:(void (^)(CGFloat progress))downloadProgressBlock
completion:(void (^)(CGImageRef image, NSError *error))completion;
- (nullable id)downloadImageWithURL:(NSURL *)URL
callbackQueue:(nullable dispatch_queue_t)callbackQueue
downloadProgressBlock:(void (^ _Nullable)(CGFloat progress))downloadProgressBlock
completion:(void (^ _Nullable)(CGImageRef _Nullable image, NSError * _Nullable error))completion;
/**
@abstract Cancels an image download.
@@ -59,6 +58,8 @@
`downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`.
@discussion This method has no effect if `downloadIdentifier` is nil.
*/
- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier;
- (void)cancelImageDownloadForIdentifier:(nullable id)downloadIdentifier;
@end
NS_ASSUME_NONNULL_END

View File

@@ -19,8 +19,11 @@ typedef struct {
ASIndexPath end;
} ASIndexPathRange;
NS_ASSUME_NONNULL_BEGIN
ASDISPLAYNODE_EXTERN_C_BEGIN
extern ASIndexPath ASIndexPathMake(NSInteger section, NSInteger row);
extern BOOL ASIndexPathEqualToIndexPath(ASIndexPath first, ASIndexPath second);
@@ -41,3 +44,5 @@ ASDISPLAYNODE_EXTERN_C_END
- (ASIndexPath)ASIndexPathValue;
@end
NS_ASSUME_NONNULL_END

View File

@@ -12,6 +12,10 @@
#import <AsyncDisplayKit/ASLayoutRangeType.h>
#import <AsyncDisplayKit/ASScrollDirection.h>
NS_ASSUME_NONNULL_BEGIN
@class ASCellNode;
typedef struct {
CGFloat leadingBufferScreenfuls;
CGFloat trailingBufferScreenfuls;
@@ -26,20 +30,22 @@ typedef struct {
*/
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType;
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray<NSIndexPath *> *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType;
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType;
@optional
- (void)insertNodesAtIndexPaths:(NSArray *)indexPaths withSizes:(NSArray *)nodeSizes;
- (void)insertNodesAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withSizes:(NSArray<NSValue *> *)nodeSizes;
- (void)deleteNodesAtIndexPaths:(NSArray *)indexPaths;
- (void)deleteNodesAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
- (void)insertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet;
- (void)insertSections:(NSArray<NSArray <ASCellNode *>*> *)sections atIndexSet:(NSIndexSet *)indexSet;
- (void)deleteSectionsAtIndexSet:(NSIndexSet *)indexSet;
- (void)setVisibleNodeIndexPaths:(NSArray *)indexPaths;
- (void)setVisibleNodeIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,6 +8,7 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/*
* Use this class to compose new attributed strings. You may use the normal
@@ -38,14 +39,14 @@
*/
@interface ASMutableAttributedStringBuilder : NSMutableAttributedString
- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary *)attrs;
- (instancetype)initWithString:(NSString *)str attributes:(nullable NSDictionary<NSString *, id> *)attrs;
- (instancetype)initWithAttributedString:(NSAttributedString *)attrStr;
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str;
- (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range;
- (void)setAttributes:(nullable NSDictionary<NSString *, id> *)attrs range:(NSRange)range;
- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range;
- (void)addAttributes:(NSDictionary *)attrs range:(NSRange)range;
- (void)addAttributes:(NSDictionary<NSString *, id> *)attrs range:(NSRange)range;
- (void)removeAttribute:(NSString *)name range:(NSRange)range;
- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString;
@@ -57,3 +58,5 @@
- (NSMutableAttributedString *)composedAttributedString;
@end
NS_ASSUME_NONNULL_END

View File

@@ -10,9 +10,10 @@
#import <AsyncDisplayKit/ASCellNode.h>
#import <AsyncDisplayKit/ASDataController.h>
#import <AsyncDisplayKit/ASFlowLayoutController.h>
#import <AsyncDisplayKit/ASLayoutController.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASRangeControllerDataSource;
@protocol ASRangeControllerDelegate;
@@ -78,7 +79,7 @@
*
* @returns an array of index paths corresponding to the nodes currently visible onscreen (i.e., the visible range).
*/
- (NSArray *)visibleNodeIndexPathsForRangeController:(ASRangeController *)rangeController;
- (NSArray<NSIndexPath *> *)visibleNodeIndexPathsForRangeController:(ASRangeController *)rangeController;
/**
* @param rangeController Sender.
@@ -130,7 +131,7 @@
*
* @param animationOptions Animation options. See ASDataControllerAnimationOptions.
*/
- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray<ASCellNode *> *)nodes atIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
* Called for nodes deletion.
@@ -143,7 +144,7 @@
*
* @param animationOptions Animation options. See ASDataControllerAnimationOptions.
*/
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray<ASCellNode *> *)nodes atIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
* Called for section insertion.
@@ -168,3 +169,5 @@
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
@end
NS_ASSUME_NONNULL_END

View File

@@ -10,6 +10,8 @@
#import <AsyncDisplayKit/ASLayoutRangeType.h>
NS_ASSUME_NONNULL_BEGIN
@class ASDisplayNode;
@protocol ASRangeHandler <NSObject>
@@ -20,3 +22,5 @@
- (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType;
@end
NS_ASSUME_NONNULL_END

View File

@@ -10,6 +10,10 @@
#import <AsyncDisplayKit/ASRangeHandler.h>
NS_ASSUME_NONNULL_BEGIN
@interface ASRangeHandlerPreload : NSObject <ASRangeHandler>
@end
NS_ASSUME_NONNULL_END

View File

@@ -10,6 +10,10 @@
#import <AsyncDisplayKit/ASRangeHandler.h>
NS_ASSUME_NONNULL_BEGIN
@interface ASRangeHandlerRender : NSObject <ASRangeHandler>
@end
NS_ASSUME_NONNULL_END

View File

@@ -10,6 +10,8 @@
#import "ASBaseDefines.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_OPTIONS(NSInteger, ASScrollDirection) {
ASScrollDirectionNone = 0,
ASScrollDirectionRight = 1 << 0,
@@ -32,3 +34,5 @@ BOOL ASScrollDirectionContainsUp(ASScrollDirection scrollDirection);
BOOL ASScrollDirectionContainsDown(ASScrollDirection scrollDirection);
ASDISPLAYNODE_EXTERN_C_END
NS_ASSUME_NONNULL_END

View File

@@ -6,13 +6,18 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import "ASBaseDefines.h"
NS_ASSUME_NONNULL_BEGIN
ASDISPLAYNODE_EXTERN_C_BEGIN
CGRect asdk_CGRectExpandHorizontally(CGRect rect, CGFloat negativeMultiplier, CGFloat positiveMultiplier);
CGRect asdk_CGRectExpandVertically(CGRect rect, CGFloat negativeMultiplier, CGFloat positiveMultiplier);
ASDISPLAYNODE_EXTERN_C_END
NS_ASSUME_NONNULL_END

View File

@@ -8,6 +8,7 @@
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSMutableAttributedString (TextKitAdditions)
@@ -20,3 +21,5 @@
- (void)attributeTextInRange:(NSRange)range withTextKitParagraphStyle:(NSParagraphStyle *)paragraphStyle;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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
{

View File

@@ -8,8 +8,12 @@
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UICollectionViewLayout (ASConvenience)
- (BOOL)asdk_isFlowLayout;
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,6 +8,8 @@
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
These are the properties we support from CALayer (implemented in the pending state)
@@ -18,7 +20,7 @@
@property (nonatomic, assign) CGPoint position;
@property (nonatomic, assign) CGFloat zPosition;
@property (nonatomic, assign) CGPoint anchorPoint;
@property (nonatomic, retain) id contents;
@property (nullable, nonatomic, retain) id contents;
@property (nonatomic, assign) CGFloat cornerRadius;
@property (nonatomic, assign) CGFloat contentsScale;
@property (nonatomic, assign) CATransform3D transform;
@@ -45,15 +47,15 @@
*/
@protocol ASDisplayNodeViewProperties
@property (nonatomic, assign) BOOL clipsToBounds;
@property (nonatomic, getter=isHidden) BOOL hidden;
@property (nonatomic, assign) BOOL autoresizesSubviews;
@property (nonatomic, assign) UIViewAutoresizing autoresizingMask;
@property (nonatomic, retain) UIColor *tintColor;
@property (nonatomic, assign) CGFloat alpha;
@property (nonatomic, assign) CGRect bounds;
@property (nonatomic, assign) CGRect frame; // Only for use with nodes wrapping synchronous views
@property (nonatomic, assign) UIViewContentMode contentMode;
@property (nonatomic, assign) BOOL clipsToBounds;
@property (nonatomic, getter=isHidden) BOOL hidden;
@property (nonatomic, assign) BOOL autoresizesSubviews;
@property (nonatomic, assign) UIViewAutoresizing autoresizingMask;
@property (nonatomic, retain, null_resettable) UIColor *tintColor;
@property (nonatomic, assign) CGFloat alpha;
@property (nonatomic, assign) CGRect bounds;
@property (nonatomic, assign) CGRect frame; // Only for use with nodes wrapping synchronous views
@property (nonatomic, assign) UIViewContentMode contentMode;
@property (nonatomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled;
@property (nonatomic, assign, getter=isExclusiveTouch) BOOL exclusiveTouch;
@property (nonatomic, assign, getter=asyncdisplaykit_isAsyncTransactionContainer, setter = asyncdisplaykit_setAsyncTransactionContainer:) BOOL asyncdisplaykit_asyncTransactionContainer;
@@ -76,6 +78,8 @@
*/
// Accessibility identification support
@property (nonatomic, copy) NSString *accessibilityIdentifier;
@property (nullable, nonatomic, copy) NSString *accessibilityIdentifier;
@end
NS_ASSUME_NONNULL_END

View File

@@ -10,6 +10,8 @@
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASLayoutableAsciiArtProtocol <NSObject>
/**
* Returns an ascii-art representation of this object and its children.
@@ -41,7 +43,7 @@
* | ASTextNode ASTextNode ASTextNode |
* ----------------------------------------
*/
+ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent;
+ (NSString *)horizontalBoxStringForChildren:(NSArray<NSString *> *)children parent:(NSString *)parent;
/**
* Renders an ascii art box with the children aligned vertically.
@@ -52,8 +54,8 @@
* | ASTextNode |
* ---------------------
*/
+ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent;
+ (NSString *)verticalBoxStringForChildren:(NSArray<NSString *> *)children parent:(NSString *)parent;
@end
NS_ASSUME_NONNULL_END

View File

@@ -10,18 +10,21 @@
#import <AsyncDisplayKit/ASLayoutSpec.h>
NS_ASSUME_NONNULL_BEGIN
/**
Lays out a single layoutable child, then lays out a background layoutable instance behind it stretched to its size.
*/
@interface ASBackgroundLayoutSpec : ASLayoutSpec
@property (nonatomic, strong) id<ASLayoutable> background;
@property (nullable, nonatomic, strong) id<ASLayoutable> background;
/**
@param child A child that is laid out to determine the size of this spec. If this is nil, then this method
returns nil.
@param background A layoutable object that is laid out behind the child. May be nil, in which case the background is omitted.
@param child A child that is laid out to determine the size of this spec.
@param background A layoutable object that is laid out behind the child. If this is nil, the background is omitted.
*/
+ (instancetype)backgroundLayoutSpecWithChild:(id<ASLayoutable>)child background:(id<ASLayoutable>)background;
+ (instancetype)backgroundLayoutSpecWithChild:(id<ASLayoutable>)child background:(nullable id<ASLayoutable>)background;
@end
NS_ASSUME_NONNULL_END

View File

@@ -34,6 +34,8 @@ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) {
ASCenterLayoutSpecSizingOptionMinimumXY = ASCenterLayoutSpecSizingOptionMinimumX | ASCenterLayoutSpecSizingOptionMinimumY,
};
NS_ASSUME_NONNULL_BEGIN
/** Lays out a single layoutable child and position it so that it is centered into the layout bounds. */
@interface ASCenterLayoutSpec : ASLayoutSpec
@@ -54,3 +56,5 @@ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) {
child:(id<ASLayoutable>)child;
@end
NS_ASSUME_NONNULL_END

View File

@@ -35,6 +35,7 @@ typedef struct {
extern ASRelativeDimension const ASRelativeDimensionUnconstrained;
ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_BEGIN
#pragma mark ASRelativeDimension
@@ -70,4 +71,5 @@ extern BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs);
extern NSString *NSStringFromASSizeRange(ASSizeRange sizeRange);
NS_ASSUME_NONNULL_END
ASDISPLAYNODE_EXTERN_C_END

View File

@@ -10,6 +10,8 @@
#import <AsyncDisplayKit/ASLayoutSpec.h>
NS_ASSUME_NONNULL_BEGIN
/**
A layout spec that wraps another layoutable child, applying insets around it.
@@ -33,8 +35,10 @@
/**
@param insets The amount of space to inset on each side.
@param child The wrapped child to inset. If nil, this method returns nil.
@param child The wrapped child to inset.
*/
+ (instancetype)insetLayoutSpecWithInsets:(UIEdgeInsets)insets child:(id<ASLayoutable>)child;
@end
NS_ASSUME_NONNULL_END

View File

@@ -12,6 +12,8 @@
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASLayoutable.h>
NS_ASSUME_NONNULL_BEGIN
extern CGPoint const CGPointNull;
extern BOOL CGPointIsNull(CGPoint point);
@@ -30,7 +32,7 @@ extern BOOL CGPointIsNull(CGPoint point);
/**
* Array of ASLayouts. Each must have a valid non-null position.
*/
@property (nonatomic, readonly) NSArray *sublayouts;
@property (nonatomic, readonly) NSArray<ASLayout *> *sublayouts;
/**
* Initializer.
@@ -46,7 +48,7 @@ extern BOOL CGPointIsNull(CGPoint point);
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
size:(CGSize)size
position:(CGPoint)position
sublayouts:(NSArray *)sublayouts;
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
/**
* Convenience initializer that has CGPointNull position.
@@ -62,7 +64,7 @@ extern BOOL CGPointIsNull(CGPoint point);
*/
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
size:(CGSize)size
sublayouts:(NSArray *)sublayouts;
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
/**
* Convenience that has CGPointNull position and no sublayouts.
@@ -89,3 +91,5 @@ extern BOOL CGPointIsNull(CGPoint point);
- (ASLayout *)flattenedLayoutUsingPredicateBlock:(BOOL (^)(ASLayout *evaluatedLayout))predicateBlock;
@end
NS_ASSUME_NONNULL_END

View File

@@ -9,7 +9,10 @@
*/
#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASStaticLayoutable.h>
#import <AsyncDisplayKit/ASStackLayoutable.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASLayoutable;
@@ -52,7 +55,7 @@
*
* @return a new instance of ASLayoutOptions
*/
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable;
- (instancetype)initWithLayoutable:(nullable id<ASLayoutable>)layoutable NS_DESIGNATED_INITIALIZER;
/**
* Copies the values of layoutOptions into self. This is useful when placing a layoutable inside of another. Consider
@@ -83,3 +86,6 @@
@property (nonatomic, readwrite) CGPoint layoutPosition;
@end
NS_ASSUME_NONNULL_END

View File

@@ -11,6 +11,8 @@
#import <AsyncDisplayKit/ASLayoutable.h>
#import <AsyncDisplayKit/ASAsciiArtBoxCreator.h>
NS_ASSUME_NONNULL_BEGIN
/** A layout spec is an immutable object that describes a layout, loosely inspired by React. */
@interface ASLayoutSpec : NSObject <ASLayoutable>
@@ -70,7 +72,7 @@
* For good measure, in these layout specs it probably makes sense to define
* setChild: and setChild:forIdentifier: methods to do something appropriate or to assert.
*/
- (void)setChildren:(NSArray *)children;
- (void)setChildren:(NSArray<id<ASLayoutable>> *)children;
/**
* Get child methods
@@ -84,17 +86,18 @@
*/
/** Returns the child added to this layout spec using the default identifier. */
- (id<ASLayoutable>)child;
- (nullable id<ASLayoutable>)child;
/**
* Returns the child added to this layout spec using the given identifier.
*
* @param identifier An identifier associated withe the child.
*/
- (id<ASLayoutable>)childForIdentifier:(NSString *)identifier;
- (nullable id<ASLayoutable>)childForIdentifier:(NSString *)identifier;
/** Returns all children added to this layout spec. */
- (NSArray *)children;
- (NSArray<id<ASLayoutable>> *)children;
@end
@interface ASLayoutSpec (Debugging) <ASLayoutableAsciiArtProtocol>
@@ -105,3 +108,6 @@
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName;
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,7 +8,7 @@
*
*/
#import "ASLayoutSpec.h"
#import "ASLayoutOptionsPrivate.h"
#import "ASAssert.h"
#import "ASBaseDefines.h"
@@ -16,7 +16,6 @@
#import "ASInternalHelpers.h"
#import "ASLayout.h"
#import "ASLayoutOptions.h"
#import "ASLayoutOptionsPrivate.h"
#import "ASThread.h"
#import <objc/runtime.h>

View File

@@ -19,6 +19,8 @@
@class ASLayout;
@class ASLayoutSpec;
NS_ASSUME_NONNULL_BEGIN
/**
* The ASLayoutable protocol declares a method for measuring the layout of an object. A layout
* is defined by an ASLayout return value, and must specify 1) the size (but not position) of the
@@ -107,3 +109,5 @@
@property (nonatomic, assign) CGPoint layoutPosition;
@end
NS_ASSUME_NONNULL_END

View File

@@ -10,13 +10,17 @@
#import <AsyncDisplayKit/ASLayoutSpec.h>
NS_ASSUME_NONNULL_BEGIN
/**
This layout spec lays out a single layoutable child and then overlays a layoutable object on top of it streched to its size
*/
@interface ASOverlayLayoutSpec : ASLayoutSpec
@property (nonatomic, strong) id<ASLayoutable> overlay;
@property (nullable, nonatomic, strong) id<ASLayoutable> overlay;
+ (instancetype)overlayLayoutSpecWithChild:(id<ASLayoutable>)child overlay:(id<ASLayoutable>)overlay;
+ (instancetype)overlayLayoutSpecWithChild:(id<ASLayoutable>)child overlay:(nullable id<ASLayoutable>)overlay;
@end
NS_ASSUME_NONNULL_END

View File

@@ -11,6 +11,8 @@
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASLayoutable.h>
NS_ASSUME_NONNULL_BEGIN
/**
Ratio layout spec
For when the content should respect a certain inherent ratio but can be scaled (think photos or videos)
@@ -36,3 +38,5 @@
+ (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id<ASLayoutable>)child;
@end
NS_ASSUME_NONNULL_END

View File

@@ -33,6 +33,7 @@ typedef struct {
extern ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained;
ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_BEGIN
#pragma mark -
#pragma mark ASRelativeSize
@@ -70,4 +71,5 @@ extern BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs,
extern ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange,
CGSize parentSize);
NS_ASSUME_NONNULL_END
ASDISPLAYNODE_EXTERN_C_END

View File

@@ -11,6 +11,7 @@
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
NS_ASSUME_NONNULL_BEGIN
/**
A simple layout spec that stacks a list of children vertically or horizontally.
@@ -70,7 +71,7 @@
@param alignItems Orientation of the children along the cross axis
@param children ASLayoutable children to be positioned.
*/
+ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children;
+ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray<id<ASLayoutable>> *)children;
/**
* @return A stack layout spec with direction of ASStackLayoutDirectionVertical
@@ -83,3 +84,5 @@
+ (instancetype)horizontalStackLayoutSpec;
@end
NS_ASSUME_NONNULL_END

View File

@@ -8,8 +8,6 @@
*
*/
#import "ASStackLayoutSpec.h"
#import <numeric>
#import <vector>
@@ -19,7 +17,6 @@
#import "ASLayoutSpecUtilities.h"
#import "ASStackBaselinePositionedLayout.h"
#import "ASStackLayoutSpecUtilities.h"
#import "ASStackPositionedLayout.h"
#import "ASStackUnpositionedLayout.h"
#import "ASThread.h"

View File

@@ -11,6 +11,8 @@
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
NS_ASSUME_NONNULL_BEGIN
/**
* Layout options that can be defined for an ASLayoutable being added to a ASStackLayoutSpec.
*/
@@ -64,3 +66,5 @@
@property (nonatomic, readwrite) CGFloat descender;
@end
NS_ASSUME_NONNULL_END

View File

@@ -11,6 +11,8 @@
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASRelativeSize.h>
NS_ASSUME_NONNULL_BEGIN
/**
* A layout spec that positions children at fixed positions.
*
@@ -21,6 +23,8 @@
/**
@param children Children to be positioned at fixed positions, each conforms to ASStaticLayoutable
*/
+ (instancetype)staticLayoutSpecWithChildren:(NSArray *)children;
+ (instancetype)staticLayoutSpecWithChildren:(NSArray<id<ASStaticLayoutable>> *)children;
@end
NS_ASSUME_NONNULL_END

View File

@@ -14,7 +14,6 @@
#import "ASLayoutOptions.h"
#import "ASInternalHelpers.h"
#import "ASLayout.h"
#import "ASStaticLayoutable.h"
@implementation ASStaticLayoutSpec
@@ -39,33 +38,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 +75,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;
}

View File

@@ -10,6 +10,8 @@
#import <AsyncDisplayKit/ASRelativeSize.h>
NS_ASSUME_NONNULL_BEGIN
/**
* Layout options that can be defined for an ASLayoutable being added to a ASStaticLayoutSpec.
*/
@@ -24,3 +26,5 @@
@property (nonatomic, assign) CGPoint layoutPosition;
@end
NS_ASSUME_NONNULL_END

View File

@@ -6,9 +6,6 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ASThread.h"
@interface ASBasicImageDownloaderContext : NSObject
+ (ASBasicImageDownloaderContext *)contextForURL:(NSURL *)URL;

View File

@@ -8,10 +8,14 @@
#import "_ASCoreAnimationExtras.h"
#import "_ASAsyncTransaction.h"
#import "_ASDisplayLayer.h"
#import "ASAssert.h"
#import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+FrameworkPrivate.h"
@interface ASDisplayNode () <_ASDisplayLayerDelegate>
@end
@implementation ASDisplayNode (AsyncDisplay)
/**
@@ -175,8 +179,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 +237,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 +267,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) {

View File

@@ -11,7 +11,6 @@
// These methods must never be called or overridden by other classes.
//
#import "_ASDisplayLayer.h"
#import "_AS-objc-internal.h"
#import "ASDisplayNodeExtraIvars.h"
#import "ASDisplayNode.h"
@@ -19,6 +18,8 @@
#import "ASThread.h"
#import "ASLayoutOptions.h"
NS_ASSUME_NONNULL_BEGIN
// Project-wide control for whether the offscreen UIWindow is used for display, or if
// ASDK's internal system for coalescing and triggering display events is used.
#define USE_WORKING_WINDOW 1
@@ -50,13 +51,16 @@ typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
ASHierarchyStateTransitioningSupernodes = 1 << 2
};
@interface ASDisplayNode () <_ASDisplayLayerDelegate>
@interface ASDisplayNode ()
{
@protected
ASInterfaceState _interfaceState;
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;
@@ -108,9 +112,11 @@ typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
@end
@interface UIView (ASDisplayNodeInternal)
@property (nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node;
@property (nullable, nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node;
@end
@interface CALayer (ASDisplayNodeInternal)
@property (nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node;
@property (nullable, nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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;
}
}

View File

@@ -11,7 +11,6 @@
// These methods must never be called or overridden by other classes.
//
#import "_ASDisplayLayer.h"
#import "_AS-objc-internal.h"
#import "ASDisplayNodeExtraIvars.h"
#import "ASDisplayNode.h"
@@ -19,6 +18,9 @@
#import "ASThread.h"
#import "ASLayoutOptions.h"
@protocol _ASDisplayLayerDelegate;
@class _ASDisplayLayer;
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector);
void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)());
@@ -39,7 +41,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
#define TIME_DISPLAYNODE_OPS (DEBUG || PROFILE)
@interface ASDisplayNode () <_ASDisplayLayerDelegate>
@interface ASDisplayNode ()
{
@protected
// Protects access to _view, _layer, _pendingViewState, _subnodes, _supernode, and other properties which are accessed from multiple threads.
@@ -144,6 +146,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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -10,7 +10,6 @@
#import "ASLayout.h"
#import "ASDimension.h"
#import "ASStackLayoutSpec.h"
#import "ASStackUnpositionedLayout.h"
/** Represents a set of laid out and positioned stack layout children. */

View File

@@ -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

View File

@@ -7,7 +7,9 @@
//
#import <Foundation/Foundation.h>
#import "ASDataController.h"
#import <AsyncDisplayKit/ASInternalHelpers.h>
typedef NSUInteger ASDataControllerAnimationOptions;
typedef NS_ENUM(NSInteger, _ASHierarchyChangeType) {
_ASHierarchyChangeTypeReload,

View File

@@ -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

View File

@@ -11,6 +11,7 @@
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
ASDISPLAYNODE_EXTERN_C_BEGIN
/**
@@ -81,3 +82,5 @@ ASDISPLAYNODE_EXTERN_C_END
+ (instancetype)paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)coreTextParagraphStyle;
@end
NS_ASSUME_NONNULL_END

Some files were not shown because too many files have changed in this diff Show More