diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index f11d0dfc..861b362e 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -360,6 +360,10 @@ AEEC47E41C21D3D200EC1693 /* ASVideoNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AEEC47E31C21D3D200EC1693 /* ASVideoNodeTests.m */; }; B0F8805A1BEAEC7500D17647 /* ASTableNode.h in Headers */ = {isa = PBXBuildFile; fileRef = B0F880581BEAEC7500D17647 /* ASTableNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; B0F8805B1BEAEC7500D17647 /* ASTableNode.m in Sources */ = {isa = PBXBuildFile; fileRef = B0F880591BEAEC7500D17647 /* ASTableNode.m */; }; + B13CA0F71C519E9400E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = B13CA0F61C519E9400E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h */; }; + B13CA0F81C519EBA00E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = B13CA0F61C519E9400E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h */; }; + B13CA1001C52004900E031AB /* ASCollectionNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = B13CA0FF1C52004900E031AB /* ASCollectionNode+Beta.h */; }; + B13CA1011C52004900E031AB /* ASCollectionNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = B13CA0FF1C52004900E031AB /* ASCollectionNode+Beta.h */; }; B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; B35061F61B010EFD0018CF92 /* ASCollectionView.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A501A1139C100143C57 /* ASCollectionView.mm */; }; @@ -771,6 +775,8 @@ AEEC47E31C21D3D200EC1693 /* ASVideoNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASVideoNodeTests.m; sourceTree = ""; }; B0F880581BEAEC7500D17647 /* ASTableNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableNode.h; sourceTree = ""; }; B0F880591BEAEC7500D17647 /* ASTableNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableNode.m; sourceTree = ""; }; + B13CA0F61C519E9400E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewLayoutFacilitatorProtocol.h; sourceTree = ""; }; + B13CA0FF1C52004900E031AB /* ASCollectionNode+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCollectionNode+Beta.h"; sourceTree = ""; }; B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B35061DD1B010EDF0018CF92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../AsyncDisplayKit-iOS/Info.plist"; sourceTree = ""; }; CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = ""; }; @@ -913,9 +919,11 @@ AC6456071B0A335000CF11B8 /* ASCellNode.m */, 18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */, 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.mm */, + B13CA0FF1C52004900E031AB /* ASCollectionNode+Beta.h */, AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */, AC3C4A501A1139C100143C57 /* ASCollectionView.mm */, AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */, + B13CA0F61C519E9400E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h */, DEC146B41C37A16A004A0EE7 /* ASCollectionInternal.h */, DEC146B51C37A16A004A0EE7 /* ASCollectionInternal.m */, 058D09D5195D050800B7D73C /* ASControlNode.h */, @@ -1285,6 +1293,7 @@ 058D0A72195D05F800B7D73C /* _ASCoreAnimationExtras.h in Headers */, 058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */, 058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */, + B13CA0F71C519E9400E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */, 058D0A74195D05F800B7D73C /* _ASPendingState.h in Headers */, 9C5586691BD549CB00B50E3A /* ASAsciiArtBoxCreator.h in Headers */, 058D0A76195D05F900B7D73C /* _ASScopeTimer.h in Headers */, @@ -1356,6 +1365,7 @@ AC026B6F1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */, 0516FA3D1A15563400B4EBED /* ASLog.h in Headers */, 257754AA1BEE44CD00737CA5 /* ASTextKitEntityAttribute.h in Headers */, + B13CA1001C52004900E031AB /* ASCollectionNode+Beta.h in Headers */, 0442850D1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */, 0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */, 058D0A59195D05DC00B7D73C /* ASMutableAttributedStringBuilder.h in Headers */, @@ -1410,6 +1420,7 @@ B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */, B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */, + B13CA1011C52004900E031AB /* ASCollectionNode+Beta.h in Headers */, DECC2ECE1C35C1C600388446 /* ASRangeControllerBeta.h in Headers */, 254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, @@ -1484,6 +1495,7 @@ B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */, DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */, B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */, + B13CA0F81C519EBA00E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */, B35062061B010EFD0018CF92 /* ASNetworkImageNode.h in Headers */, 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */, @@ -2038,6 +2050,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; @@ -2078,6 +2091,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; @@ -2152,6 +2166,7 @@ baseConfigurationReference = FB07EABBCF28656C6297BC2D /* Pods-AsyncDisplayKitTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_CODE_COVERAGE = YES; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", @@ -2181,6 +2196,7 @@ baseConfigurationReference = D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_CODE_COVERAGE = YES; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", @@ -2207,6 +2223,7 @@ B35061EE1B010EDF0018CF92 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_CODE_COVERAGE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CURRENT_PROJECT_VERSION = 1; @@ -2239,6 +2256,7 @@ B35061EF1B010EDF0018CF92 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_CODE_COVERAGE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; diff --git a/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit-iOS.xcscheme b/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit-iOS.xcscheme index c40014ed..50760cce 100644 --- a/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit-iOS.xcscheme +++ b/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit-iOS.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/AsyncDisplayKit/ASButtonNode.mm b/AsyncDisplayKit/ASButtonNode.mm index 500d8627..ae101c97 100644 --- a/AsyncDisplayKit/ASButtonNode.mm +++ b/AsyncDisplayKit/ASButtonNode.mm @@ -72,6 +72,13 @@ [self updateTitle]; } +- (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously +{ + [super setDisplaysAsynchronously:displaysAsynchronously]; + [self.imageNode setDisplaysAsynchronously:displaysAsynchronously]; + [self.titleNode setDisplaysAsynchronously:displaysAsynchronously]; +} + - (void)updateImage { ASDN::MutexLocker l(_propertyLock); diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index 575200df..df7040d9 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -20,7 +20,6 @@ @interface ASCellNode () { - ASDisplayNodeDidLoadBlock _nodeLoadedBlock; UIViewController *_viewController; ASDisplayNode *_viewControllerNode; } @@ -49,15 +48,13 @@ ASDisplayNodeAssertNotNil(viewControllerBlock, @"should initialize with a valid block that returns a UIViewController"); if (viewControllerBlock) { - _viewController = viewControllerBlock(); - __weak UIViewController *weakViewController = _viewController; _viewControllerNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView *{ - return weakViewController.view; + _viewController = viewControllerBlock(); + return _viewController.view; } didLoadBlock:didLoadBlock]; [self addSubnode:_viewControllerNode]; - _nodeLoadedBlock = didLoadBlock; } return self; diff --git a/AsyncDisplayKit/ASCollectionNode+Beta.h b/AsyncDisplayKit/ASCollectionNode+Beta.h new file mode 100644 index 00000000..eeac22b3 --- /dev/null +++ b/AsyncDisplayKit/ASCollectionNode+Beta.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2014-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. + */ + +@protocol ASCollectionViewLayoutFacilitatorProtocol; + +NS_ASSUME_NONNULL_BEGIN + +@interface ASCollectionNode (Beta) + +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id)layoutFacilitator; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/ASCollectionNode.h b/AsyncDisplayKit/ASCollectionNode.h index 7f9b6e31..1b140873 100644 --- a/AsyncDisplayKit/ASCollectionNode.h +++ b/AsyncDisplayKit/ASCollectionNode.h @@ -8,6 +8,8 @@ #import +@protocol ASCollectionViewLayoutFacilitatorProtocol; + NS_ASSUME_NONNULL_BEGIN /** diff --git a/AsyncDisplayKit/ASCollectionNode.mm b/AsyncDisplayKit/ASCollectionNode.mm index f184a969..5bed1a1b 100644 --- a/AsyncDisplayKit/ASCollectionNode.mm +++ b/AsyncDisplayKit/ASCollectionNode.mm @@ -8,6 +8,7 @@ #import "ASCollectionNode.h" #import "ASCollectionInternal.h" +#import "ASCollectionViewLayoutFacilitatorProtocol.h" #import "ASDisplayNode+Subclasses.h" #import "ASRangeController.h" #include @@ -83,9 +84,14 @@ } - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout +{ + return [self initWithFrame:frame collectionViewLayout:layout layoutFacilitator:nil]; +} + +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id)layoutFacilitator { ASDisplayNodeViewBlock collectionViewBlock = ^UIView *{ - return [[ASCollectionView alloc] _initWithFrame:frame collectionViewLayout:layout ownedByNode:YES]; + return [[ASCollectionView alloc] _initWithFrame:CGRectZero collectionViewLayout:layout layoutFacilitator:layoutFacilitator ownedByNode:YES]; }; if (self = [super initWithViewBlock:collectionViewBlock]) { diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 774a44a7..418d22c2 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -19,6 +19,7 @@ @protocol ASCollectionDataSource; @protocol ASCollectionDelegate; @protocol ASCollectionViewLayoutInspecting; +@protocol ASCollectionViewLayoutFacilitatorProtocol; NS_ASSUME_NONNULL_BEGIN diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index a5f45469..2b588d08 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -13,6 +13,7 @@ #import "ASCollectionDataController.h" #import "ASCollectionViewLayoutController.h" #import "ASCollectionViewFlowLayoutInspector.h" +#import "ASCollectionViewLayoutFacilitatorProtocol.h" #import "ASDisplayNode+FrameworkPrivate.h" #import "ASDisplayNode+Beta.h" #import "ASInternalHelpers.h" @@ -67,6 +68,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; ASRangeController *_rangeController; ASCollectionViewLayoutController *_layoutController; ASCollectionViewFlowLayoutInspector *_flowLayoutInspector; + + id _layoutFacilitator; BOOL _performingBatchUpdates; NSMutableArray *_batchUpdateBlocks; @@ -144,6 +147,11 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; } - (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout ownedByNode:(BOOL)ownedByNode +{ + return [self _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:nil ownedByNode:ownedByNode]; +} + +- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id)layoutFacilitator ownedByNode:(BOOL)ownedByNode { if (!(self = [super initWithFrame:frame collectionViewLayout:layout])) return nil; @@ -195,6 +203,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; if ([layout asdk_isFlowLayout]) { _layoutInspector = [self flowLayoutInspector]; } + _layoutFacilitator = layoutFacilitator; _proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self]; super.delegate = (id)_proxyDelegate; @@ -252,6 +261,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)reloadDataImmediately { ASDisplayNodeAssertMainThread(); + _superIsPendingDataLoad = YES; [_dataController reloadDataImmediatelyWithAnimationOptions:kASCollectionViewAnimationNone]; [super reloadData]; } @@ -800,7 +810,15 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (ASInterfaceState)interfaceStateForRangeController:(ASRangeController *)rangeController { - return self.collectionNode.interfaceState; + ASCollectionNode *collectionNode = self.collectionNode; + if (collectionNode) { + return self.collectionNode.interfaceState; + } else { + // Until we can always create an associated ASCollectionNode without a retain cycle, + // we might be on our own to try to guess if we're visible. The node normally + // handles this even if it is the root / directly added to the view hierarchy. + return (self.window != nil ? ASInterfaceStateVisible : ASInterfaceStateNone); + } } - (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths @@ -824,7 +842,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)rangeController:(ASRangeController *)rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion { ASDisplayNodeAssertMainThread(); - + if (!self.asyncDataSource || _superIsPendingDataLoad) { if (completion) { completion(NO); @@ -847,7 +865,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - + [_layoutFacilitator collectionViewEditingCellsAtIndexPaths:indexPaths]; if (!self.asyncDataSource || _superIsPendingDataLoad) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } @@ -866,7 +884,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - + [_layoutFacilitator collectionViewEditingCellsAtIndexPaths:indexPaths]; if (!self.asyncDataSource || _superIsPendingDataLoad) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } @@ -885,7 +903,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - + [_layoutFacilitator collectionViewEditingSectionsAtIndexSet:indexSet]; if (!self.asyncDataSource || _superIsPendingDataLoad) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } @@ -904,7 +922,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - + [_layoutFacilitator collectionViewEditingSectionsAtIndexSet:indexSet]; if (!self.asyncDataSource || _superIsPendingDataLoad) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } diff --git a/AsyncDisplayKit/ASCollectionViewLayoutFacilitatorProtocol.h b/AsyncDisplayKit/ASCollectionViewLayoutFacilitatorProtocol.h new file mode 100644 index 00000000..719c9841 --- /dev/null +++ b/AsyncDisplayKit/ASCollectionViewLayoutFacilitatorProtocol.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2014-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. + */ + +#ifndef ASCollectionViewLayoutFacilitatorProtocol_h +#define ASCollectionViewLayoutFacilitatorProtocol_h + +/** + * This facilitator protocol is intended to help Layout to better + * gel with the CollectionView + */ +@protocol ASCollectionViewLayoutFacilitatorProtocol + +/** + * Inform that the collectionView is editing the cells at a list of indexPaths + */ +- (void)collectionViewEditingCellsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Inform that the collectionView is editing the sections at a set of indexes + */ +- (void)collectionViewEditingSectionsAtIndexSet:(NSIndexSet *)indexes; + +@end + +#endif /* ASCollectionViewLayoutFacilitatorProtocol_h */ diff --git a/AsyncDisplayKit/ASEditableTextNode.h b/AsyncDisplayKit/ASEditableTextNode.h index 983bfcd6..832611c0 100644 --- a/AsyncDisplayKit/ASEditableTextNode.h +++ b/AsyncDisplayKit/ASEditableTextNode.h @@ -23,6 +23,12 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Configuration +/** + @abstract Enable scrolling on the textView + @default true + */ +@property (nonatomic) BOOL scrollEnabled; + /** @abstract Access to underlying UITextView for more configuration options. @warning This property should only be used on the main thread and should not be accessed before the editable text node's view is created. diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index 13554d88..06accae2 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -16,16 +16,42 @@ #import "ASTextNodeWordKerner.h" #import "ASThread.h" -//! @abstract This subclass exists solely to ensure the text view's panGestureRecognizer never begins, because it's sporadically enabled by UITextView. It will be removed pending rdar://14729288. -@interface _ASDisabledPanUITextView : UITextView +/** + @abstract As originally reported in rdar://14729288, when scrollEnabled = NO, + UITextView does not calculate its contentSize. This makes it difficult + for a client to embed a UITextView inside a different scroll view with + other content (setting scrollEnabled = NO on the UITextView itself, + because the containing scroll view will handle the gesture)... + because accessing contentSize is typically necessary to perform layout. + Apple later closed the issue as expected behavior. This works around + the issue by ensuring that contentSize is always calculated, while + still providing control over the UITextView's scrolling. + + See issue: https://github.com/facebook/AsyncDisplayKit/issues/1063 + */ +@interface ASPanningOverriddenUITextView : UITextView +{ + BOOL _shouldBlockPanGesture; +} @end -@implementation _ASDisabledPanUITextView +@implementation ASPanningOverriddenUITextView + +- (BOOL)scrollEnabled +{ + return _shouldBlockPanGesture; +} + +- (void)setScrollEnabled:(BOOL)scrollEnabled +{ + _shouldBlockPanGesture = !scrollEnabled; + [super setScrollEnabled:YES]; +} - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { - // Never allow our pans to begin. - if (gestureRecognizer == self.panGestureRecognizer) + // Never allow our pans to begin when _shouldBlockPanGesture is true. + if (_shouldBlockPanGesture && gestureRecognizer == self.panGestureRecognizer) return NO; // Otherwise, proceed as usual. @@ -207,11 +233,18 @@ #pragma mark - Configuration @synthesize delegate = _delegate; +- (void)setScrollEnabled:(BOOL)scrollEnabled +{ + ASDN::MutexLocker l(_textKitLock); + _scrollEnabled = scrollEnabled; + [_textKitComponents.textView setScrollEnabled:_scrollEnabled]; +} + - (UITextView *)textView { ASDisplayNodeAssertMainThread(); if (!_textKitComponents.textView) { - _textKitComponents.textView = [[_ASDisabledPanUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; + _textKitComponents.textView = [[ASPanningOverriddenUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; } return _textKitComponents.textView; } diff --git a/AsyncDisplayKit/ASNetworkImageNode.h b/AsyncDisplayKit/ASNetworkImageNode.h index 1d7134ca..82513a8c 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.h +++ b/AsyncDisplayKit/ASNetworkImageNode.h @@ -95,6 +95,16 @@ NS_ASSUME_NONNULL_BEGIN @optional +/** + * Notification that the image node failed to download the image. + * + * @param imageNode The sender. + * @param error The error with details. + * + * @discussion Called on a background queue. + */ +- (void)imageNode:(ASNetworkImageNode *)imageNode didFailWithError:(NSError *)error; + /** * Notification that the image node finished decoding an image. * diff --git a/AsyncDisplayKit/ASNetworkImageNode.mm b/AsyncDisplayKit/ASNetworkImageNode.mm index cbba35af..5c5c8f37 100755 --- a/AsyncDisplayKit/ASNetworkImageNode.mm +++ b/AsyncDisplayKit/ASNetworkImageNode.mm @@ -168,14 +168,14 @@ _cacheUUID = nil; } -- (void)_downloadImageWithCompletion:(void (^)(CGImageRef))finished +- (void)_downloadImageWithCompletion:(void (^)(CGImageRef, NSError*))finished { _imageDownload = [_downloader downloadImageWithURL:_URL callbackQueue:dispatch_get_main_queue() downloadProgressBlock:NULL completion:^(CGImageRef responseImage, NSError *error) { if (finished != NULL) { - finished(responseImage); + finished(responseImage, error); } }]; } @@ -210,7 +210,7 @@ } } else { __weak __typeof__(self) weakSelf = self; - void (^finished)(CGImageRef) = ^(CGImageRef responseImage) { + void (^finished)(CGImageRef, NSError *) = ^(CGImageRef responseImage, NSError *error) { __typeof__(self) strongSelf = weakSelf; if (strongSelf == nil) { return; @@ -232,6 +232,9 @@ if (responseImage != NULL) { [strongSelf->_delegate imageNode:strongSelf didLoadImage:strongSelf.image]; } + else if (error && [strongSelf->_delegate respondsToSelector:@selector(imageNode:didFailWithError:)]) { + [strongSelf->_delegate imageNode:strongSelf didFailWithError:error]; + } }; if (_cache != nil) { @@ -247,7 +250,7 @@ if (image == NULL && _downloader != nil) { [self _downloadImageWithCompletion:finished]; } else { - finished(image); + finished(image, NULL); } }; diff --git a/AsyncDisplayKit/ASPagerNode.m b/AsyncDisplayKit/ASPagerNode.m index a69a963d..075d0f07 100644 --- a/AsyncDisplayKit/ASPagerNode.m +++ b/AsyncDisplayKit/ASPagerNode.m @@ -15,7 +15,7 @@ { UICollectionViewFlowLayout *_flowLayout; ASPagerNodeProxy *_proxy; - id _pagerDataSource; + __weak id _pagerDataSource; } @end diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index af0c9ddd..6d5c40e8 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -34,7 +34,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; @class _ASTableViewCell; @protocol _ASTableViewCellDelegate -- (void)willLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell; +- (void)didLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell; @end @interface _ASTableViewCell : UITableViewCell @@ -47,8 +47,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)layoutSubviews { - [_delegate willLayoutSubviewsOfTableViewCell:self]; [super layoutSubviews]; + [_delegate didLayoutSubviewsOfTableViewCell:self]; } - (void)didTransitionToState:(UITableViewCellStateMask)state @@ -722,7 +722,15 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (ASInterfaceState)interfaceStateForRangeController:(ASRangeController *)rangeController { - return self.tableNode.interfaceState; + ASTableNode *tableNode = self.tableNode; + if (tableNode) { + return self.tableNode.interfaceState; + } else { + // Until we can always create an associated ASTableNode without a retain cycle, + // we might be on our own to try to guess if we're visible. The node normally + // handles this even if it is the root / directly added to the view hierarchy. + return (self.window != nil ? ASInterfaceStateVisible : ASInterfaceStateNone); + } } #pragma mark - ASRangeControllerDelegate @@ -895,7 +903,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; #pragma mark - _ASTableViewCellDelegate -- (void)willLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell +- (void)didLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell { CGFloat contentViewWidth = tableViewCell.contentView.bounds.size.width; ASCellNode *node = tableViewCell.node; diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 9ad5437b..7eac0593 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -336,14 +336,14 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; - (void)setAttributedString:(NSAttributedString *)attributedString { - if (ASObjectIsEqual(attributedString, _attributedString)) { - return; - } - if (attributedString == nil) { attributedString = [[NSAttributedString alloc] initWithString:@"" attributes:nil]; } + if (ASObjectIsEqual(attributedString, _attributedString)) { + return; + } + _attributedString = ASCleanseAttributedStringOfCoreTextAttributes(attributedString); // Sync the truncation string with attributes from the updated _attributedString diff --git a/AsyncDisplayKit/Details/ASCollectionInternal.h b/AsyncDisplayKit/Details/ASCollectionInternal.h index 9b72548f..a0aff1e5 100644 --- a/AsyncDisplayKit/Details/ASCollectionInternal.h +++ b/AsyncDisplayKit/Details/ASCollectionInternal.h @@ -11,7 +11,7 @@ #import "ASRangeController.h" @interface ASCollectionView () -- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout ownedByNode:(BOOL)ownedByNode; +- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id)layoutFacilitator ownedByNode:(BOOL)ownedByNode; @property (nonatomic, weak, readwrite) ASCollectionNode *collectionNode; @property (nonatomic, strong, readonly) ASRangeController *rangeController; diff --git a/AsyncDisplayKit/Details/ASThread.h b/AsyncDisplayKit/Details/ASThread.h index 121756bc..38b534d3 100644 --- a/AsyncDisplayKit/Details/ASThread.h +++ b/AsyncDisplayKit/Details/ASThread.h @@ -195,39 +195,6 @@ namespace ASDN { typedef Locker StaticMutexLocker; typedef Unlocker StaticMutexUnlocker; - struct SpinLock - { - SpinLock &operator= (bool value) { - _l = value ? ~0 : 0; return *this; - } - - SpinLock() { _l = OS_SPINLOCK_INIT; } - SpinLock(const SpinLock&) = delete; - SpinLock &operator=(const SpinLock&) = delete; - - bool try_lock () { - return OSSpinLockTry (&_l); - } - - void lock () { - OSSpinLockLock(&_l); - } - - void unlock () { - OSSpinLockUnlock(&_l); - } - - OSSpinLock *spinlock () { - return &_l; - } - - private: - OSSpinLock _l; - }; - - typedef Locker SpinLocker; - typedef Unlocker SpinUnlocker; - struct Condition { Condition () { diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm index 7630c105..a978b0fc 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm @@ -111,7 +111,7 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() // to make sure our width calculations aren't being offset by glyphs going beyond the constrained rect. boundingRect = CGRectIntersection(boundingRect, {.size = constrainedRect.size}); - _calculatedSize = [_shadower outsetSizeWithInsetSize:boundingRect.size]; + _calculatedSize = [_shadower outsetSizeWithInsetSize:CGSizeMake(boundingRect.size.width + boundingRect.origin.x, boundingRect.size.height + boundingRect.origin.y)]; } - (CGSize)size diff --git a/README.md b/README.md index 4594f409..68c98c51 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ [![Build Status](https://travis-ci.org/facebook/AsyncDisplayKit.svg)](https://travis-ci.org/facebook/AsyncDisplayKit) [![Coverage Status](https://coveralls.io/repos/facebook/AsyncDisplayKit/badge.svg?branch=master)](https://coveralls.io/r/facebook/AsyncDisplayKit?branch=master) - [![Version](http://img.shields.io/cocoapods/v/AsyncDisplayKit.svg)](http://cocoapods.org/?q=AsyncDisplayKit) - [![Platform](http://img.shields.io/cocoapods/p/AsyncDisplayKit.svg)]() - [![License](http://img.shields.io/cocoapods/l/AsyncDisplayKit.svg)](https://github.com/facebook/AsyncDisplayKit/blob/master/LICENSE) +[![Version](https://img.shields.io/cocoapods/v/AsyncDisplayKit.svg)](http://cocoapods.org/pods/AsyncDisplayKit) +[![Platform](https://img.shields.io/cocoapods/p/AsyncDisplayKit.svg)]() +[![License](https://img.shields.io/cocoapods/l/AsyncDisplayKit.svg)](https://github.com/facebook/AsyncDisplayKit/blob/master/LICENSE) +[![Downloads](https://img.shields.io/badge/downloads-%3E120k-green.svg)](http://cocoapods.org/pods/AsyncDisplayKit) AsyncDisplayKit is an iOS framework that keeps even the most complex user interfaces smooth and responsive. It was originally built to make Facebook's