From 3080ee33cf83c667f98004e9e70a8c2ca00672f9 Mon Sep 17 00:00:00 2001 From: Hannah Trosi Date: Sat, 2 Jul 2016 11:45:40 -0700 Subject: [PATCH] Revert "update from master/" This reverts commit d7fde61e168b6594ca5d74836597b0aac0e83e9b. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 34 ++-- AsyncDisplayKit/ASButtonNode.mm | 9 +- AsyncDisplayKit/ASCollectionView.mm | 14 +- AsyncDisplayKit/ASDisplayNode+Subclasses.h | 7 - AsyncDisplayKit/ASDisplayNode.mm | 22 +-- AsyncDisplayKit/ASPagerNode.h | 47 ++--- AsyncDisplayKit/ASPagerNode.m | 5 - AsyncDisplayKit/ASTableView.h | 12 -- AsyncDisplayKit/ASTableView.mm | 36 +--- AsyncDisplayKit/ASTableViewInternal.h | 3 - AsyncDisplayKit/ASTextNode.mm | 42 ++--- AsyncDisplayKit/ASVideoNode.mm | 8 +- AsyncDisplayKit/ASVideoPlayerNode.h | 1 - AsyncDisplayKit/ASVideoPlayerNode.mm | 13 -- .../Details/ASChangeSetDataController.m | 26 +-- .../Details/ASCollectionDataController.mm | 48 +++++ .../Details/ASDataController+Subclasses.h | 44 +++++ AsyncDisplayKit/Details/ASDataController.mm | 90 ++++++++- AsyncDisplayKit/Details/ASDelegateProxy.m | 9 - AsyncDisplayKit/Details/ASEnvironment.h | 4 - .../Details/ASFlowLayoutController.mm | 4 +- .../Details/NSIndexSet+ASHelpers.h | 25 --- .../Details/NSIndexSet+ASHelpers.m | 76 -------- .../Private/_ASHierarchyChangeSet.h | 28 +-- .../Private/_ASHierarchyChangeSet.m | 175 ++++++++---------- AsyncDisplayKitTests/ASCollectionViewTests.m | 40 ---- AsyncDisplayKitTests/ASTableViewTests.m | 41 +--- AsyncDisplayKitTests/ASTableViewThrashTests.m | 27 ++- .../TestResources/ASThrashTestRecordedCase | 2 +- README.md | 8 +- build.sh | 40 ---- 31 files changed, 354 insertions(+), 586 deletions(-) delete mode 100644 AsyncDisplayKit/Details/NSIndexSet+ASHelpers.h delete mode 100644 AsyncDisplayKit/Details/NSIndexSet+ASHelpers.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 0c518f3cab..62c3baffc7 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -256,9 +256,9 @@ 68355B3A1CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B361CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m */; }; 68355B3B1CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = 68355B371CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h */; settings = {ATTRIBUTES = (Public, ); }; }; 68355B3C1CB57A5A001D4E68 /* ASImageContainerProtocolCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B381CB57A5A001D4E68 /* ASImageContainerProtocolCategories.m */; }; - 68355B3D1CB57A5A001D4E68 /* ASPINRemoteImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 68355B391CB57A5A001D4E68 /* ASPINRemoteImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 68355B3D1CB57A5A001D4E68 /* ASPINRemoteImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 68355B391CB57A5A001D4E68 /* ASPINRemoteImageDownloader.h */; }; 68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B361CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m */; }; - 68355B3F1CB57A64001D4E68 /* ASPINRemoteImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 68355B391CB57A5A001D4E68 /* ASPINRemoteImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 68355B3F1CB57A64001D4E68 /* ASPINRemoteImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 68355B391CB57A5A001D4E68 /* ASPINRemoteImageDownloader.h */; }; 68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B381CB57A5A001D4E68 /* ASImageContainerProtocolCategories.m */; }; 68355B411CB57A6C001D4E68 /* ASImageContainerProtocolCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = 68355B371CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h */; settings = {ATTRIBUTES = (Public, ); }; }; 68AF37DB1CBEF4D80077BF76 /* ASImageNode+AnimatedImagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */; }; @@ -368,7 +368,6 @@ 9C8898BB1C738B9800D6B02E /* ASTextKitFontSizeAdjuster.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C8898BA1C738B9800D6B02E /* ASTextKitFontSizeAdjuster.mm */; }; 9C8898BC1C738BA800D6B02E /* ASTextKitFontSizeAdjuster.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C8898BA1C738B9800D6B02E /* ASTextKitFontSizeAdjuster.mm */; }; 9C8898BD1C738BB800D6B02E /* ASTextKitFontSizeAdjuster.h in Headers */ = {isa = PBXBuildFile; fileRef = A32FEDD31C501B6A004F642A /* ASTextKitFontSizeAdjuster.h */; }; - 9CC606651D24DF9E006581A0 /* NSIndexSet+ASHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4981BB1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m */; }; 9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9CFFC6BE1CCAC52B006A6476 /* ASEnvironment.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFFC6BD1CCAC52B006A6476 /* ASEnvironment.mm */; }; @@ -547,13 +546,10 @@ CC3B208E1C3F7D0A00798563 /* ASWeakSetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3B208D1C3F7D0A00798563 /* ASWeakSetTests.m */; }; CC3B20901C3F892D00798563 /* ASBridgedPropertiesTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC3B208F1C3F892D00798563 /* ASBridgedPropertiesTests.mm */; }; CC4981B31D1A02BE004E13CC /* ASTableViewThrashTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4981B21D1A02BE004E13CC /* ASTableViewThrashTests.m */; }; - CC4981BC1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */; }; - CC4981BD1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4981BB1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m */; }; CC7FD9DE1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; CC7FD9DF1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */; }; CC7FD9E11BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */; }; CC7FD9E21BB603FF005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CCF18FF41D2575E300DF5895 /* NSIndexSet+ASHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */; }; 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 */; }; DB55C2611C6408D6004EDCF5 /* _ASTransitionContext.h in Headers */ = {isa = PBXBuildFile; fileRef = DB55C25F1C6408D6004EDCF5 /* _ASTransitionContext.h */; }; @@ -941,8 +937,6 @@ CC3B208D1C3F7D0A00798563 /* ASWeakSetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASWeakSetTests.m; sourceTree = ""; }; CC3B208F1C3F892D00798563 /* ASBridgedPropertiesTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBridgedPropertiesTests.mm; sourceTree = ""; }; CC4981B21D1A02BE004E13CC /* ASTableViewThrashTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableViewThrashTests.m; sourceTree = ""; }; - CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+ASHelpers.h"; sourceTree = ""; }; - CC4981BB1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+ASHelpers.m"; sourceTree = ""; }; CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = ""; }; CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequest.m; sourceTree = ""; }; CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequestTests.m; sourceTree = ""; }; @@ -1235,8 +1229,6 @@ 058D09E1195D050800B7D73C /* Details */ = { isa = PBXGroup; children = ( - CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */, - CC4981BB1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m */, 9CFFC6BD1CCAC52B006A6476 /* ASEnvironment.mm */, 058D09E2195D050800B7D73C /* _ASDisplayLayer.h */, 058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */, @@ -1642,7 +1634,6 @@ ACF6ED2D1B17843500DA7C62 /* ASRatioLayoutSpec.h in Headers */, AC47D9451B3BB41900AAEE9D /* ASRelativeSize.h in Headers */, 291B63FB1AA53A7A000A71B3 /* ASScrollDirection.h in Headers */, - CC4981BC1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h in Headers */, D785F6621A74327E00291744 /* ASScrollNode.h in Headers */, 058D0A7F195D05F900B7D73C /* ASSentinel.h in Headers */, 9C8221951BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */, @@ -1801,7 +1792,6 @@ 254C6B741BF94DF4003EC431 /* ASTextNodeWordKerner.h in Headers */, DB55C2671C641AE4004EDCF5 /* ASContextTransitioning.h in Headers */, 68B0277B1C1A79D60041016B /* ASDisplayNode+Beta.h in Headers */, - CCF18FF41D2575E300DF5895 /* NSIndexSet+ASHelpers.h in Headers */, B350622D1B010EFD0018CF92 /* ASScrollDirection.h in Headers */, 254C6B751BF94DF4003EC431 /* ASTextKitComponents.h in Headers */, B35062081B010EFD0018CF92 /* ASScrollNode.h in Headers */, @@ -1884,12 +1874,12 @@ isa = PBXNativeTarget; buildConfigurationList = 058D09D2195D04C000B7D73C /* Build configuration list for PBXNativeTarget "AsyncDisplayKitTests" */; buildPhases = ( - 2E61B6A0DB0F436A9DDBE86F /* [CP] Check Pods Manifest.lock */, + 2E61B6A0DB0F436A9DDBE86F /* 📦 Check Pods Manifest.lock */, 058D09B8195D04C000B7D73C /* Sources */, 058D09B9195D04C000B7D73C /* Frameworks */, 058D09BA195D04C000B7D73C /* Resources */, - 3B9D88CDF51B429C8409E4B6 /* [CP] Copy Pods Resources */, - B130AB1AC0A1E5162E211C19 /* [CP] Embed Pods Frameworks */, + 3B9D88CDF51B429C8409E4B6 /* 📦 Copy Pods Resources */, + B130AB1AC0A1E5162E211C19 /* 📦 Embed Pods Frameworks */, ); buildRules = ( ); @@ -1990,14 +1980,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 2E61B6A0DB0F436A9DDBE86F /* [CP] Check Pods Manifest.lock */ = { + 2E61B6A0DB0F436A9DDBE86F /* 📦 Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "[CP] Check Pods Manifest.lock"; + name = "📦 Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -2005,14 +1995,14 @@ shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; - 3B9D88CDF51B429C8409E4B6 /* [CP] Copy Pods Resources */ = { + 3B9D88CDF51B429C8409E4B6 /* 📦 Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "[CP] Copy Pods Resources"; + name = "📦 Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -2020,14 +2010,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - B130AB1AC0A1E5162E211C19 /* [CP] Embed Pods Frameworks */ = { + B130AB1AC0A1E5162E211C19 /* 📦 Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "[CP] Embed Pods Frameworks"; + name = "📦 Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -2111,7 +2101,6 @@ ACF6ED4C1B17847A00DA7C62 /* ASInternalHelpers.mm in Sources */, 68FC85DF1CE29AB700EDD713 /* ASNavigationController.m in Sources */, ACF6ED251B17843500DA7C62 /* ASLayout.mm in Sources */, - CC4981BD1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m in Sources */, DB55C2631C6408D6004EDCF5 /* _ASTransitionContext.m in Sources */, 92074A631CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */, 251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */, @@ -2277,7 +2266,6 @@ CC3B208C1C3F7A5400798563 /* ASWeakSet.m in Sources */, B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */, B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */, - 9CC606651D24DF9E006581A0 /* NSIndexSet+ASHelpers.m in Sources */, 92074A641CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */, B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */, 68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */, diff --git a/AsyncDisplayKit/ASButtonNode.mm b/AsyncDisplayKit/ASButtonNode.mm index 54c30ad47e..5638e7c125 100644 --- a/AsyncDisplayKit/ASButtonNode.mm +++ b/AsyncDisplayKit/ASButtonNode.mm @@ -15,7 +15,6 @@ #import "ASBackgroundLayoutSpec.h" #import "ASInsetLayoutSpec.h" #import "ASDisplayNode+Beta.h" -#import "ASStaticLayoutSpec.h" @interface ASButtonNode () { @@ -492,13 +491,9 @@ spec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:contentEdgeInsets child:spec]; } - if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) { - stack.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(self.preferredFrameSize); - spec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[stack]]; - } - if (_backgroundImageNode.image) { - spec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:spec background:_backgroundImageNode]; + spec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:spec + background:_backgroundImageNode]; } return spec; diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index e5546436ac..e90d6e398b 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -430,22 +430,22 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType { - [_rangeController setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; + [_collectionNode setTuningParameters:tuningParameters forRangeType:rangeType]; } - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType { - return [_rangeController tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; + return [_collectionNode tuningParametersForRangeType:rangeType]; } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - [_rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + [_collectionNode setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; } - (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - return [_rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + return [_collectionNode tuningParametersForRangeMode:rangeMode rangeType:rangeType]; } - (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath @@ -517,21 +517,18 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)insertSections:(NSIndexSet *)sections { ASDisplayNodeAssertMainThread(); - if (sections.count == 0) { return; } [_dataController insertSections:sections withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)deleteSections:(NSIndexSet *)sections { ASDisplayNodeAssertMainThread(); - if (sections.count == 0) { return; } [_dataController deleteSections:sections withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)reloadSections:(NSIndexSet *)sections { ASDisplayNodeAssertMainThread(); - if (sections.count == 0) { return; } [_dataController reloadSections:sections withAnimationOptions:kASCollectionViewAnimationNone]; } @@ -544,21 +541,18 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths { ASDisplayNodeAssertMainThread(); - if (indexPaths.count == 0) { return; } [_dataController insertRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths { ASDisplayNodeAssertMainThread(); - if (indexPaths.count == 0) { return; } [_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths { ASDisplayNodeAssertMainThread(); - if (indexPaths.count == 0) { return; } [_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone]; } diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index 9711355757..967b82270d 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -465,13 +465,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSString *)descriptionForRecursiveDescription; -/** - * @abstract Called when the node's ASTraitCollection changes - * - * @discussion Subclasses can override this method to react to a trait collection change. - */ -- (void)asyncTraitCollectionDidChange; - @end #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") diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index cf38ddd804..e38731e7f8 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1100,14 +1100,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // measureWithSizeRange: on subnodes to assert. return; } - - // Handle placeholder layer creation in case the size of the node changed after the initial placeholder layer - // was created - if ([self _shouldHavePlaceholderLayer]) { - [self _setupPlaceholderLayerIfNeeded]; - } _placeholderLayer.frame = bounds; - [self layout]; [self layoutDidFinish]; } @@ -2762,12 +2755,7 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; - (void)setEnvironmentState:(ASEnvironmentState)environmentState { - ASEnvironmentTraitCollection oldTraitCollection = _environmentState.environmentTraitCollection; _environmentState = environmentState; - - if (ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection(oldTraitCollection, _environmentState.environmentTraitCollection) == NO) { - [self asyncTraitCollectionDidChange]; - } } - (ASDisplayNode *)parent @@ -2797,10 +2785,7 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; - (void)setEnvironmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection { - if (ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection(environmentTraitCollection, _environmentState.environmentTraitCollection) == NO) { - _environmentState.environmentTraitCollection = environmentTraitCollection; - [self asyncTraitCollectionDidChange]; - } + _environmentState.environmentTraitCollection = environmentTraitCollection; } ASEnvironmentLayoutOptionsForwarding @@ -2812,11 +2797,6 @@ ASEnvironmentLayoutExtensibilityForwarding return [ASTraitCollection traitCollectionWithASEnvironmentTraitCollection:self.environmentTraitCollection]; } -- (void)asyncTraitCollectionDidChange -{ - -} - #if TARGET_OS_TV #pragma mark - UIFocusEnvironment Protocol (tvOS) diff --git a/AsyncDisplayKit/ASPagerNode.h b/AsyncDisplayKit/ASPagerNode.h index cbef217812..2f9809da79 100644 --- a/AsyncDisplayKit/ASPagerNode.h +++ b/AsyncDisplayKit/ASPagerNode.h @@ -22,6 +22,8 @@ * This method replaces -collectionView:numberOfItemsInSection: * * @param pagerNode The sender. + * + * * @returns The total number of pages that can display in the pagerNode. */ - (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode; @@ -32,7 +34,9 @@ * This method replaces -collectionView:nodeForItemAtIndexPath: * * @param pagerNode The sender. - * @param index The index of the requested node. + * + * @param index The index of the requested node. + * * @returns a node for display at this index. This will be called on the main thread and should * not implement reuse (it will be called once per row). Unlike UICollectionView's version, * this method is not called when the row is about to display. @@ -44,7 +48,9 @@ * This method takes precedence over pagerNode:nodeAtIndex: if implemented. * * @param pagerNode The sender. - * @param index The index of the requested node. + * + * @param index The index of the requested node. + * * @returns a block that creates the node for display at this index. * Must be thread-safe (can be called on the main thread or a background * queue) and should not implement reuse (it will be called once per row). @@ -55,7 +61,9 @@ * Provides the constrained size range for measuring the node at the index path. * * @param pagerNode The sender. + * * @param indexPath The index path of the node. + * * @returns A constrained size range for layout the node at this index path. */ - (ASSizeRange)pagerNode:(ASPagerNode *)pagerNode constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath; @@ -68,46 +76,27 @@ @interface ASPagerNode : ASCollectionNode -/** - * Configures a default horizontal, paging flow layout with 0 inter-item spacing. - */ +/// Configures a default horizontal, paging flow layout with 0 inter-item spacing. - (instancetype)init; -/** - * Initializer with custom-configured flow layout properties. - */ +/// Initializer with custom-configured flow layout properties. - (instancetype)initWithCollectionViewLayout:(ASPagerFlowLayout *)flowLayout; -/** - * Data Source is required, and uses a different protocol from ASCollectionNode. - */ +/// Data Source is required, and uses a different protocol from ASCollectionNode. - (void)setDataSource:(id )dataSource; - (id )dataSource; -/** - * Delegate is optional, and uses the same protocol as ASCollectionNode. - * This includes UIScrollViewDelegate as well as most methods from UICollectionViewDelegate, like willDisplay... - */ +// Delegate is optional, and uses the same protocol as ASCollectionNode. +// This includes UIScrollViewDelegate as well as most methods from UICollectionViewDelegate, like willDisplay... @property (nonatomic, weak) id delegate; -/** - * The underlying ASCollectionView object. - */ +/// The underlying ASCollectionView object. @property (nonatomic, readonly) ASCollectionView *view; -/** - * Returns the current page index - */ +/// Returns the current page index @property (nonatomic, assign, readonly) NSInteger currentPageIndex; -/** - * Scroll the contents of the receiver to ensure that the page is visible - */ +/// Scroll the contents of the receiver to ensure that the page is visible - (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated; -/** - * Returns the node for the passed page index - */ -- (ASCellNode *)nodeForPageAtIndex:(NSInteger)index; - @end diff --git a/AsyncDisplayKit/ASPagerNode.m b/AsyncDisplayKit/ASPagerNode.m index c266ef18f3..2c6cd4b32e 100644 --- a/AsyncDisplayKit/ASPagerNode.m +++ b/AsyncDisplayKit/ASPagerNode.m @@ -98,11 +98,6 @@ [self.view scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:animated]; } -- (ASCellNode *)nodeForPageAtIndex:(NSInteger)index -{ - return [self.view nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]]; -} - #pragma mark - ASCollectionViewDataSource - (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index 06ce862606..7d36cc3d4d 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -433,18 +433,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView; -/** - * Provides the constrained size range for measuring the row at the index path. - * Note: the widths in the returned size range are ignored! - * - * @param tableView The sender. - * - * @param indexPath The index path of the node. - * - * @returns A constrained size range for layout the node at this index path. - */ -- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath; - /** * Informs the delegate that the table view did remove the node which was previously * at the given index path from the view hierarchy. diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index db73c8049c..8c62f23aa4 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -28,7 +28,6 @@ #import -static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero}; static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; //#define LOG(...) NSLog(__VA_ARGS__) @@ -145,7 +144,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; unsigned int asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset:1; unsigned int asyncDelegateTableViewWillBeginBatchFetchWithContext:1; unsigned int asyncDelegateShouldBatchFetchForTableView:1; - unsigned int asyncDelegateTableViewConstrainedSizeForRowAtIndexPath:1; } _asyncDelegateFlags; struct { @@ -166,7 +164,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; // Always set, whether ASCollectionView is created directly or via ASCollectionNode. @property (nonatomic, weak) ASTableNode *tableNode; -@property (nonatomic) BOOL test_enableSuperUpdateCallLogging; @end @implementation ASTableView @@ -338,8 +335,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _asyncDelegateFlags.asyncDelegateShouldBatchFetchForTableView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)]; _asyncDelegateFlags.asyncDelegateScrollViewWillBeginDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]; _asyncDelegateFlags.asyncDelegateScrollViewDidEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]; - _asyncDelegateFlags.asyncDelegateTableViewConstrainedSizeForRowAtIndexPath = [_asyncDelegate respondsToSelector:@selector(tableView:constrainedSizeForRowAtIndexPath:)]; - } super.delegate = (id)_proxyDelegate; @@ -479,21 +474,18 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { ASDisplayNodeAssertMainThread(); - if (sections.count == 0) { return; } [_dataController insertSections:sections withAnimationOptions:animation]; } - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { ASDisplayNodeAssertMainThread(); - if (sections.count == 0) { return; } [_dataController deleteSections:sections withAnimationOptions:animation]; } - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { ASDisplayNodeAssertMainThread(); - if (sections.count == 0) { return; } [_dataController reloadSections:sections withAnimationOptions:animation]; } @@ -506,21 +498,18 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { ASDisplayNodeAssertMainThread(); - if (indexPaths.count == 0) { return; } [_dataController insertRowsAtIndexPaths:indexPaths withAnimationOptions:animation]; } - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { ASDisplayNodeAssertMainThread(); - if (indexPaths.count == 0) { return; } [_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOptions:animation]; } - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { ASDisplayNodeAssertMainThread(); - if (indexPaths.count == 0) { return; } [_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:animation]; } @@ -1004,9 +993,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ - if (self.test_enableSuperUpdateCallLogging) { - NSLog(@"-[super insertRowsAtIndexPaths]: %@", indexPaths); - } [super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions]; [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; }); @@ -1027,9 +1013,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ - if (self.test_enableSuperUpdateCallLogging) { - NSLog(@"-[super deleteRowsAtIndexPaths]: %@", indexPaths); - } [super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions]; [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; }); @@ -1051,9 +1034,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ - if (self.test_enableSuperUpdateCallLogging) { - NSLog(@"-[super insertSections]: %@", indexSet); - } [super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions]; [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; }); @@ -1070,9 +1050,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ - if (self.test_enableSuperUpdateCallLogging) { - NSLog(@"-[super deleteSections]: %@", indexSet); - } [super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions]; [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; }); @@ -1111,17 +1088,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { - ASSizeRange constrainedSize = kInvalidSizeRange; - if (_asyncDelegateFlags.asyncDelegateTableViewConstrainedSizeForRowAtIndexPath) { - ASSizeRange delegateConstrainedSize = [_asyncDelegate tableView:self constrainedSizeForRowAtIndexPath:indexPath]; - // ignore widths in the returned size range (for TableView) - constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.min.height), - CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height)); - } else { - constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, 0), - CGSizeMake(_nodesConstrainedWidth, FLT_MAX)); - } - return constrainedSize; + return ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, 0), + CGSizeMake(_nodesConstrainedWidth, FLT_MAX)); } - (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section diff --git a/AsyncDisplayKit/ASTableViewInternal.h b/AsyncDisplayKit/ASTableViewInternal.h index 22ac11ff7e..93cdca0fcb 100644 --- a/AsyncDisplayKit/ASTableViewInternal.h +++ b/AsyncDisplayKit/ASTableViewInternal.h @@ -34,7 +34,4 @@ */ - (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass ownedByNode:(BOOL)ownedByNode; -/// Set YES and we'll log every time we call [super insertRows…] etc -@property (nonatomic) BOOL test_enableSuperUpdateCallLogging; - @end diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 3151437f55..a1e3d6c3e7 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -366,36 +366,32 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; - (void)setAttributedText:(NSAttributedString *)attributedText { + std::lock_guard l(_textLock); if (attributedText == nil) { attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil]; } - - // Don't hold textLock for too long. - { - std::lock_guard l(_textLock); - if (ASObjectIsEqual(attributedText, _attributedText)) { - return; - } - _attributedText = ASCleanseAttributedStringOfCoreTextAttributes(attributedText); - - // Sync the truncation string with attributes from the updated _attributedString - // Without this, the size calculation of the text with truncation applied will - // not take into account the attributes of attributedText in the last line - [self _updateComposedTruncationText]; - - // We need an entirely new renderer - [self _invalidateRenderer]; + if (ASObjectIsEqual(attributedText, _attributedText)) { + return; } - - NSUInteger length = attributedText.length; - if (length > 0) { + + _attributedText = ASCleanseAttributedStringOfCoreTextAttributes(attributedText); + + if (_attributedText.length > 0) { CGFloat screenScale = ASScreenScale(); - self.ascender = round([[attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale; - self.descender = round([[attributedText attribute:NSFontAttributeName atIndex:length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale; + self.ascender = round([[_attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale; + self.descender = round([[_attributedText attribute:NSFontAttributeName atIndex:_attributedText.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale; } + // Sync the truncation string with attributes from the updated _attributedString + // Without this, the size calculation of the text with truncation applied will + // not take into account the attributes of attributedText in the last line + [self _updateComposedTruncationText]; + + // We need an entirely new renderer + [self _invalidateRenderer]; + // Tell the display node superclasses that the cached layout is incorrect now [self invalidateCalculatedLayout]; @@ -403,8 +399,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; // Accessiblity - self.accessibilityLabel = attributedText.string; - self.isAccessibilityElement = (length != 0); // We're an accessibility element by default if there is a string. + self.accessibilityLabel = _attributedText.string; + self.isAccessibilityElement = (_attributedText.length != 0); // We're an accessibility element by default if there is a string. } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/ASVideoNode.mm b/AsyncDisplayKit/ASVideoNode.mm index 13d469ea89..379a70955b 100644 --- a/AsyncDisplayKit/ASVideoNode.mm +++ b/AsyncDisplayKit/ASVideoNode.mm @@ -164,10 +164,6 @@ static NSString * const kStatus = @"status"; - (void)addPlayerItemObservers:(AVPlayerItem *)playerItem { - if (playerItem == nil) { - return; - } - [playerItem addObserver:self forKeyPath:kStatus options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:ASVideoNodeContext]; [playerItem addObserver:self forKeyPath:kPlaybackLikelyToKeepUpKey options:NSKeyValueObservingOptionNew context:ASVideoNodeContext]; [playerItem addObserver:self forKeyPath:kplaybackBufferEmpty options:NSKeyValueObservingOptionNew context:ASVideoNodeContext]; @@ -645,9 +641,7 @@ static NSString * const kStatus = @"status"; _currentPlayerItem = currentItem; - if (currentItem != nil) { - [self addPlayerItemObservers:currentItem]; - } + [self addPlayerItemObservers:currentItem]; } - (ASDisplayNode *)playerNode diff --git a/AsyncDisplayKit/ASVideoPlayerNode.h b/AsyncDisplayKit/ASVideoPlayerNode.h index c8f65fcb80..de07dada8e 100644 --- a/AsyncDisplayKit/ASVideoPlayerNode.h +++ b/AsyncDisplayKit/ASVideoPlayerNode.h @@ -49,7 +49,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, readwrite) BOOL muted; @property (nonatomic, assign, readonly) ASVideoNodePlayerState playerState; @property (nonatomic, assign, readwrite) BOOL shouldAggressivelyRecoverFromStall; -@property (nullable, atomic, strong, readwrite) NSURL *placeholderImageURL; //! Defaults to 100 @property (nonatomic, assign) int32_t periodicTimeObserverTimescale; diff --git a/AsyncDisplayKit/ASVideoPlayerNode.mm b/AsyncDisplayKit/ASVideoPlayerNode.mm index fd76f79d30..cd880bcd4d 100644 --- a/AsyncDisplayKit/ASVideoPlayerNode.mm +++ b/AsyncDisplayKit/ASVideoPlayerNode.mm @@ -76,9 +76,6 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; @end @implementation ASVideoPlayerNode - -@dynamic placeholderImageURL; - - (instancetype)init { if (!(self = [super init])) { @@ -774,16 +771,6 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; return _videoNode.shouldAggressivelyRecoverFromStall; } -- (void) setPlaceholderImageURL:(NSURL *)placeholderImageURL -{ - _videoNode.URL = placeholderImageURL; -} - -- (NSURL*) placeholderImageURL -{ - return _videoNode.URL; -} - - (void)setShouldAggressivelyRecoverFromStall:(BOOL)shouldAggressivelyRecoverFromStall { if (_shouldAggressivelyRecoverFromStall == shouldAggressivelyRecoverFromStall) { diff --git a/AsyncDisplayKit/Details/ASChangeSetDataController.m b/AsyncDisplayKit/Details/ASChangeSetDataController.m index f6615d1bdb..efce00ad31 100644 --- a/AsyncDisplayKit/Details/ASChangeSetDataController.m +++ b/AsyncDisplayKit/Details/ASChangeSetDataController.m @@ -14,7 +14,6 @@ #import "ASInternalHelpers.h" #import "_ASHierarchyChangeSet.h" #import "ASAssert.h" -#import "NSIndexSet+ASHelpers.h" #import "ASDataController+Subclasses.h" @@ -48,9 +47,6 @@ [super beginUpdates]; - NSAssert([_changeSet itemChangesOfType:_ASHierarchyChangeTypeReload].count == 0, @"Expected reload item changes to have been converted into insert/deletes."); - NSAssert([_changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload].count == 0, @"Expected reload section changes to have been converted into insert/deletes."); - for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeDelete]) { [super deleteRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions]; } @@ -58,7 +54,17 @@ for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) { [super deleteSections:change.indexSet withAnimationOptions:change.animationOptions]; } - + + // TODO: Shouldn't reloads be processed before deletes, since deletes affect + // the index space and reloads don't? + for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) { + [super reloadSections:change.indexSet withAnimationOptions:change.animationOptions]; + } + + for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) { + [super reloadRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions]; + } + for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) { [super insertSections:change.indexSet withAnimationOptions:change.animationOptions]; } @@ -109,10 +115,7 @@ if ([self batchUpdating]) { [_changeSet reloadSections:sections animationOptions:animationOptions]; } else { - [self beginUpdates]; - [super deleteSections:sections withAnimationOptions:animationOptions]; - [super insertSections:sections withAnimationOptions:animationOptions]; - [self endUpdates]; + [super reloadSections:sections withAnimationOptions:animationOptions]; } } @@ -155,10 +158,7 @@ if ([self batchUpdating]) { [_changeSet reloadItems:indexPaths animationOptions:animationOptions]; } else { - [self beginUpdates]; - [super deleteRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; - [super insertRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; - [self endUpdates]; + [super reloadRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; } } diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index df0e7d592b..b54583c2a2 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -115,6 +115,30 @@ } } +- (void)prepareForReloadSections:(NSIndexSet *)sections +{ + for (NSString *kind in [self supplementaryKinds]) { + NSMutableArray *contexts = [NSMutableArray array]; + [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableContexts:contexts]; + _pendingContexts[kind] = contexts; + } +} + +- (void)willReloadSections:(NSIndexSet *)sections +{ + NSArray *keys = _pendingContexts.allKeys; + for (NSString *kind in keys) { + NSMutableArray *contexts = _pendingContexts[kind]; + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], sections); + [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; + // reinsert the elements + [self batchLayoutNodesFromContexts:contexts ofKind:kind completion:^(NSArray *nodes, NSArray *indexPaths) { + [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; + }]; + [_pendingContexts removeObjectForKey:kind]; + } +} + - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection { for (NSString *kind in [self supplementaryKinds]) { @@ -163,6 +187,30 @@ } } +- (void)prepareForReloadRowsAtIndexPaths:(NSArray *)indexPaths +{ + for (NSString *kind in [self supplementaryKinds]) { + NSMutableArray *contexts = [NSMutableArray array]; + [self _populateSupplementaryNodesOfKind:kind atIndexPaths:indexPaths mutableContexts:contexts]; + _pendingContexts[kind] = contexts; + } +} + +- (void)willReloadRowsAtIndexPaths:(NSArray *)indexPaths +{ + NSArray *keys = _pendingContexts.allKeys; + for (NSString *kind in keys) { + NSMutableArray *contexts = _pendingContexts[kind]; + + [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; + // reinsert the elements + [self batchLayoutNodesFromContexts:contexts ofKind:kind completion:^(NSArray *nodes, NSArray *indexPaths) { + [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; + }]; + [_pendingContexts removeObjectForKey:kind]; + } +} + - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableContexts:(NSMutableArray *)contexts { id environment = [self.environmentDelegate dataControllerEnvironment]; diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 099a9bfe45..d837540362 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -128,6 +128,28 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray *nodes, NS */ - (void)willDeleteSections:(NSIndexSet *)sections; +/** + * Notifies the subclass to perform any work needed before the given sections will be reloaded. + * + * @discussion This method will be performed before the data controller enters its editing queue, usually on the main + * thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or + * data stores before entering into editing the backing store on a background thread. + * + * @param sections Indices of sections to be reloaded + */ +- (void)prepareForReloadSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass that the data controller will reload the sections in the given index set + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform any additional transformations like supplementary views + * or header/footer nodes. + * + * @param sections Indices of sections to be reloaded + */ +- (void)willReloadSections:(NSIndexSet *)sections; + /** * Notifies the subclass that the data controller will move a section to a new position * @@ -184,4 +206,26 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray *nodes, NS */ - (void)willDeleteRowsAtIndexPaths:(NSArray *)indexPaths; +/** + * Notifies the subclass to perform any work needed before the given rows will be reloaded. + * + * @discussion This method will be performed before the data controller enters its editing queue, usually on the main + * thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or + * data stores before entering into editing the backing store on a background thread. + * + * @param indexPaths Index paths for the rows to be reloaded. + */ +- (void)prepareForReloadRowsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Notifies the subclass that the data controller will reload the rows at the given index paths. + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform any additional transformations like supplementary views + * or header/footer nodes. + * + * @param indexPaths Index paths for the rows to be reloaded. + */ +- (void)willReloadRowsAtIndexPaths:(NSArray *)indexPaths; + @end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index f9a890e609..86967fc99a 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -65,7 +65,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; if (!(self = [super init])) { return nil; } - ASDisplayNodeAssert(![self isMemberOfClass:[ASDataController class]], @"ASDataController is an abstract class and should not be instantiated. Instantiate a subclass instead."); _completedNodes = [NSMutableDictionary dictionary]; _editingNodes = [NSMutableDictionary dictionary]; @@ -143,9 +142,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize { - CGRect frame = CGRectZero; - frame.size = [node measureWithSizeRange:constrainedSize].size; - node.frame = frame; + CGSize size = [node measureWithSizeRange:constrainedSize].size; + node.frame = { .size = size }; } /** @@ -663,7 +661,29 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - ASDisplayNodeAssert(NO, @"ASDataController does not support %@. Call this on ASChangeSetDataController the reload will be broken into delete & insert.", NSStringFromSelector(_cmd)); + [self performEditCommandWithBlock:^{ + ASDisplayNodeAssertMainThread(); + LOG(@"Edit Command - reloadSections: %@", sections); + + [_editingTransactionQueue waitUntilAllOperationsAreFinished]; + + NSArray *contexts= [self _populateFromDataSourceWithSectionIndexSet:sections]; + + [self prepareForReloadSections:sections]; + + [_editingTransactionQueue addOperationWithBlock:^{ + [self willReloadSections:sections]; + + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections); + + LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForTwoDimensionalArray(_editingNodes[ASDataControllerRowNodeKind])); + + [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; + + // reinsert the elements + [self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions]; + }]; + }]; } - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions @@ -726,6 +746,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Optional template hook for subclasses (See ASDataController+Subclasses.h) } +- (void)prepareForReloadSections:(NSIndexSet *)sections +{ + // Optional template hook for subclasses (See ASDataController+Subclasses.h) +} + +- (void)willReloadSections:(NSIndexSet *)sections +{ + // Optional template hook for subclasses (See ASDataController+Subclasses.h) +} + - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection { // Optional template hook for subclasses (See ASDataController+Subclasses.h) @@ -751,6 +781,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Optional template hook for subclasses (See ASDataController+Subclasses.h) } +- (void)prepareForReloadRowsAtIndexPaths:(NSArray *)indexPaths +{ + // Optional template hook for subclasses (See ASDataController+Subclasses.h) +} + +- (void)willReloadRowsAtIndexPaths:(NSArray *)indexPaths +{ + // Optional template hook for subclasses (See ASDataController+Subclasses.h) +} + #pragma mark - Row Editing (External API) - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions @@ -813,7 +853,40 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - ASDisplayNodeAssert(NO, @"ASDataController does not support %@. Call this on ASChangeSetDataController and the reload will be broken into delete & insert.", NSStringFromSelector(_cmd)); + [self performEditCommandWithBlock:^{ + ASDisplayNodeAssertMainThread(); + LOG(@"Edit Command - reloadRows: %@", indexPaths); + + [_editingTransactionQueue waitUntilAllOperationsAreFinished]; + + NSMutableArray *contexts = [[NSMutableArray alloc] initWithCapacity:indexPaths.count]; + + // Sort indexPath to avoid messing up the index when deleting + // FIXME: Shouldn't deletes be sorted in descending order? + NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; + + id environment = [self.environmentDelegate dataControllerEnvironment]; + ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection; + + for (NSIndexPath *indexPath in sortedIndexPaths) { + ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath]; + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath]; + [contexts addObject:[[ASIndexedNodeContext alloc] initWithNodeBlock:nodeBlock + indexPath:indexPath + constrainedSize:constrainedSize + environmentTraitCollection:environmentTraitCollection]]; + } + + [self prepareForReloadRowsAtIndexPaths:indexPaths]; + + [_editingTransactionQueue addOperationWithBlock:^{ + [self willReloadRowsAtIndexPaths:indexPaths]; + + LOG(@"Edit Transaction - reloadRows: %@", indexPaths); + [self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions]; + [self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions]; + }]; + }]; } - (void)relayoutAllNodes @@ -850,9 +923,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (ASCellNode *node in section) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; - CGRect frame = CGRectZero; - frame.size = [node measureWithSizeRange:constrainedSize].size; - node.frame = frame; + CGSize size = [node measureWithSizeRange:constrainedSize].size; + node.frame = { .size = size }; rowIndex += 1; } sectionIndex += 1; diff --git a/AsyncDisplayKit/Details/ASDelegateProxy.m b/AsyncDisplayKit/Details/ASDelegateProxy.m index 13c0d211c9..b1f3310736 100644 --- a/AsyncDisplayKit/Details/ASDelegateProxy.m +++ b/AsyncDisplayKit/Details/ASDelegateProxy.m @@ -118,15 +118,6 @@ return self; } -- (BOOL)conformsToProtocol:(Protocol *)aProtocol -{ - if (_target) { - return [_target conformsToProtocol:aProtocol]; - } else { - return [super conformsToProtocol:aProtocol]; - } -} - - (BOOL)respondsToSelector:(SEL)aSelector { if ([self interceptsSelector:aSelector]) { diff --git a/AsyncDisplayKit/Details/ASEnvironment.h b/AsyncDisplayKit/Details/ASEnvironment.h index dd95c2e107..e267de1e8a 100644 --- a/AsyncDisplayKit/Details/ASEnvironment.h +++ b/AsyncDisplayKit/Details/ASEnvironment.h @@ -157,10 +157,6 @@ ASDISPLAYNODE_EXTERN_C_END ASDN::MutexLocker l(lock);\ ASEnvironmentTraitCollection oldTraits = self.environmentState.environmentTraitCollection;\ [super setEnvironmentState:environmentState];\ -\ - /* Extra Trait Collection Handling */\ - /* If the node is not loaded yet don't do anything as otherwise the access of the view will trigger a load*/\ - if (!self.isNodeLoaded) { return; } \ ASEnvironmentTraitCollection currentTraits = environmentState.environmentTraitCollection;\ if (ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection(currentTraits, oldTraits) == NO) {\ /* Must dispatch to main for self.view && [self.view.dataController completedNodes]*/ \ diff --git a/AsyncDisplayKit/Details/ASFlowLayoutController.mm b/AsyncDisplayKit/Details/ASFlowLayoutController.mm index ffd553ba96..cb754bb9bf 100644 --- a/AsyncDisplayKit/Details/ASFlowLayoutController.mm +++ b/AsyncDisplayKit/Details/ASFlowLayoutController.mm @@ -92,12 +92,12 @@ currPath.row++; // Once we reach the end of the section, advance to the next one. Keep advancing if the next section is zero-sized. - while (currPath.row >= [(NSArray *)completedNodes[currPath.section] count] && currPath.section < endPath.section) { + while (currPath.row >= [(NSArray *)completedNodes[currPath.section] count] && currPath.section < completedNodes.count - 1) { currPath.row = 0; currPath.section++; + ASDisplayNodeAssert(currPath.section <= endPath.section, @"currPath should never reach a further section than endPath"); } } - ASDisplayNodeAssert(currPath.section <= endPath.section, @"currPath should never reach a further section than endPath"); [indexPathSet addObject:[NSIndexPath indexPathWithASIndexPath:endPath]]; diff --git a/AsyncDisplayKit/Details/NSIndexSet+ASHelpers.h b/AsyncDisplayKit/Details/NSIndexSet+ASHelpers.h deleted file mode 100644 index 179e685639..0000000000 --- a/AsyncDisplayKit/Details/NSIndexSet+ASHelpers.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// NSIndexSet+ASHelpers.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 6/23/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import - -@interface NSIndexSet (ASHelpers) - -- (NSIndexSet *)as_indexesByMapping:(NSUInteger (^)(NSUInteger idx))block; - -- (NSIndexSet *)as_intersectionWithIndexes:(NSIndexSet *)indexes; - -/// Returns all the item indexes from the given index paths that are in the given section. -+ (NSIndexSet *)as_indexSetFromIndexPaths:(NSArray *)indexPaths inSection:(NSUInteger)section; - -/// If you've got an old index, and you insert items using this index set, this returns the change to get to the new index. -- (NSUInteger)as_indexChangeByInsertingItemsBelowIndex:(NSUInteger)index; - -- (NSString *)as_smallDescription; - -@end diff --git a/AsyncDisplayKit/Details/NSIndexSet+ASHelpers.m b/AsyncDisplayKit/Details/NSIndexSet+ASHelpers.m deleted file mode 100644 index fa636ad8c8..0000000000 --- a/AsyncDisplayKit/Details/NSIndexSet+ASHelpers.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// NSIndexSet+ASHelpers.m -// AsyncDisplayKit -// -// Created by Adlai Holler on 6/23/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import - -#import "NSIndexSet+ASHelpers.h" - -@implementation NSIndexSet (ASHelpers) - -- (NSIndexSet *)as_indexesByMapping:(NSUInteger (^)(NSUInteger))block -{ - NSMutableIndexSet *result = [NSMutableIndexSet indexSet]; - [self enumerateIndexesUsingBlock:^(NSUInteger idx, __unused BOOL * _Nonnull stop) { - NSUInteger newIndex = block(idx); - if (newIndex != NSNotFound) { - [result addIndex:newIndex]; - } - }]; - return result; -} - -- (NSIndexSet *)as_intersectionWithIndexes:(NSIndexSet *)indexes -{ - NSMutableIndexSet *result = [NSMutableIndexSet indexSet]; - [self enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { - [indexes enumerateRangesInRange:range options:kNilOptions usingBlock:^(NSRange range, BOOL * _Nonnull stop) { - [result addIndexesInRange:range]; - }]; - }]; - return result; -} - -+ (NSIndexSet *)as_indexSetFromIndexPaths:(NSArray *)indexPaths inSection:(NSUInteger)section -{ - NSMutableIndexSet *result = [NSMutableIndexSet indexSet]; - for (NSIndexPath *indexPath in indexPaths) { - if (indexPath.section == section) { - [result addIndex:indexPath.item]; - } - } - return result; -} - -- (NSUInteger)as_indexChangeByInsertingItemsBelowIndex:(NSUInteger)index -{ - __block NSUInteger newIndex = index; - [self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { - if (idx <= newIndex) { - newIndex += 1; - } else { - *stop = YES; - } - }]; - return newIndex - index; -} - -- (NSString *)as_smallDescription -{ - NSMutableString *result = [NSMutableString stringWithString:@"{ "]; - [self enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { - if (range.length == 1) { - [result appendFormat:@"%lu ", (unsigned long)range.location]; - } else { - [result appendFormat:@"%lu-%lu ", (unsigned long)range.location, (unsigned long)NSMaxRange(range)]; - } - }]; - [result appendString:@"}"]; - return result; -} - -@end diff --git a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.h b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.h index 755d82004d..6ec3b4ca75 100644 --- a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.h +++ b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.h @@ -13,8 +13,6 @@ #import #import -NS_ASSUME_NONNULL_BEGIN - typedef NSUInteger ASDataControllerAnimationOptions; typedef NS_ENUM(NSInteger, _ASHierarchyChangeType) { @@ -23,8 +21,6 @@ typedef NS_ENUM(NSInteger, _ASHierarchyChangeType) { _ASHierarchyChangeTypeInsert }; -NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); - @interface _ASHierarchySectionChange : NSObject // FIXME: Generalize this to `changeMetadata` dict? @@ -38,11 +34,11 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); @property (nonatomic, readonly) ASDataControllerAnimationOptions animationOptions; /// Index paths are sorted descending for changeType .Delete, ascending otherwise -@property (nonatomic, strong, readonly) NSArray *indexPaths; +@property (nonatomic, strong, readonly) NSArray *indexPaths; @property (nonatomic, readonly) _ASHierarchyChangeType changeType; -+ (NSDictionary *)sectionToIndexSetMapFromChanges:(NSArray<_ASHierarchyItemChange *> *)changes ofType:(_ASHierarchyChangeType)changeType; ++ (NSDictionary *)sectionToIndexSetMapFromChanges:(NSArray *)changes ofType:(_ASHierarchyChangeType)changeType; @end @interface _ASHierarchyChangeSet : NSObject @@ -51,6 +47,8 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); @property (nonatomic, strong, readonly) NSIndexSet *deletedSections; /// @precondition The change set must be completed. @property (nonatomic, strong, readonly) NSIndexSet *insertedSections; +/// @precondition The change set must be completed. +@property (nonatomic, strong, readonly) NSIndexSet *reloadedSections; /** Get the section index after the update for the given section before the update. @@ -58,12 +56,11 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); @precondition The change set must be completed. @returns The new section index, or NSNotFound if the given section was deleted. */ -- (NSUInteger)newSectionForOldSection:(NSUInteger)oldSection; +- (NSInteger)newSectionForOldSection:(NSInteger)oldSection; @property (nonatomic, readonly) BOOL completed; /// Call this once the change set has been constructed to prevent future modifications to the changeset. Calling this more than once is a programmer error. -/// NOTE: Calling this method will cause the changeset to convert all reloads into delete/insert pairs. - (void)markCompleted; /** @@ -80,18 +77,13 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); - Inserted sections, ascending order - Inserted items, ascending order */ -- (nullable NSArray <_ASHierarchySectionChange *> *)sectionChangesOfType:(_ASHierarchyChangeType)changeType; -- (nullable NSArray <_ASHierarchyItemChange *> *)itemChangesOfType:(_ASHierarchyChangeType)changeType; - -/// Returns all item indexes affected by changes of the given type in the given section. -- (NSIndexSet *)indexesForItemChangesOfType:(_ASHierarchyChangeType)changeType inSection:(NSUInteger)section; +- (NSArray /*<_ASHierarchySectionChange *>*/ *)sectionChangesOfType:(_ASHierarchyChangeType)changeType; +- (NSArray /*<_ASHierarchyItemChange *>*/ *)itemChangesOfType:(_ASHierarchyChangeType)changeType; - (void)deleteSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; - (void)insertSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; - (void)reloadSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; -- (void)insertItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; -- (void)deleteItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; -- (void)reloadItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; +- (void)insertItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; +- (void)deleteItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; +- (void)reloadItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; @end - -NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m index d1b54438a0..c89fc99332 100644 --- a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m +++ b/AsyncDisplayKit/Private/_ASHierarchyChangeSet.m @@ -12,22 +12,6 @@ #import "_ASHierarchyChangeSet.h" #import "ASInternalHelpers.h" -#import "NSIndexSet+ASHelpers.h" -#import "ASAssert.h" - -NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) -{ - switch (changeType) { - case _ASHierarchyChangeTypeInsert: - return @"Insert"; - case _ASHierarchyChangeTypeDelete: - return @"Delete"; - case _ASHierarchyChangeTypeReload: - return @"Reload"; - default: - return @"(invalid)"; - } -} @interface _ASHierarchySectionChange () - (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexSet:(NSIndexSet *)indexSet animationOptions:(ASDataControllerAnimationOptions)animationOptions; @@ -39,7 +23,7 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) + (void)sortAndCoalesceChanges:(NSMutableArray *)changes; /// Returns all the indexes from all the `indexSet`s of the given `_ASHierarchySectionChange` objects. -+ (NSMutableIndexSet *)allIndexesInSectionChanges:(NSArray *)changes; ++ (NSMutableIndexSet *)allIndexesInChanges:(NSArray *)changes; @end @interface _ASHierarchyItemChange () @@ -54,12 +38,12 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) @interface _ASHierarchyChangeSet () -@property (nonatomic, strong, readonly) NSMutableArray<_ASHierarchyItemChange *> *insertItemChanges; -@property (nonatomic, strong, readonly) NSMutableArray<_ASHierarchyItemChange *> *deleteItemChanges; -@property (nonatomic, strong, readonly) NSMutableArray<_ASHierarchyItemChange *> *reloadItemChanges; -@property (nonatomic, strong, readonly) NSMutableArray<_ASHierarchySectionChange *> *insertSectionChanges; -@property (nonatomic, strong, readonly) NSMutableArray<_ASHierarchySectionChange *> *deleteSectionChanges; -@property (nonatomic, strong, readonly) NSMutableArray<_ASHierarchySectionChange *> *reloadSectionChanges; +@property (nonatomic, strong, readonly) NSMutableArray *insertItemChanges; +@property (nonatomic, strong, readonly) NSMutableArray *deleteItemChanges; +@property (nonatomic, strong, readonly) NSMutableArray *reloadItemChanges; +@property (nonatomic, strong, readonly) NSMutableArray *insertSectionChanges; +@property (nonatomic, strong, readonly) NSMutableArray *deleteSectionChanges; +@property (nonatomic, strong, readonly) NSMutableArray *reloadSectionChanges; @end @@ -119,27 +103,21 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) } } -- (NSIndexSet *)indexesForItemChangesOfType:(_ASHierarchyChangeType)changeType inSection:(NSUInteger)section +- (NSInteger)newSectionForOldSection:(NSInteger)oldSection { - [self _ensureCompleted]; - NSMutableIndexSet *result = [NSMutableIndexSet indexSet]; - for (_ASHierarchyItemChange *change in [self itemChangesOfType:changeType]) { - [result addIndexes:[NSIndexSet as_indexSetFromIndexPaths:change.indexPaths inSection:section]]; - } - return result; -} - -- (NSUInteger)newSectionForOldSection:(NSUInteger)oldSection -{ - ASDisplayNodeAssertNotNil(_deletedSections, @"Cannot call %@ before `markCompleted` returns.", NSStringFromSelector(_cmd)); - ASDisplayNodeAssertNotNil(_insertedSections, @"Cannot call %@ before `markCompleted` returns.", NSStringFromSelector(_cmd)); [self _ensureCompleted]; if ([_deletedSections containsIndex:oldSection]) { return NSNotFound; } - NSUInteger newIndex = oldSection - [_deletedSections countOfIndexesInRange:NSMakeRange(0, oldSection)]; - newIndex += [_insertedSections as_indexChangeByInsertingItemsBelowIndex:newIndex]; + __block NSInteger newIndex = oldSection - [_deletedSections countOfIndexesInRange:NSMakeRange(0, oldSection)]; + [_insertedSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + if (idx <= newIndex) { + newIndex += 1; + } else { + *stop = YES; + } + }]; return newIndex; } @@ -202,42 +180,42 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) - (void)_sortAndCoalesceChangeArrays { @autoreleasepool { - - // Split reloaded sections into [delete(oldIndex), insert(newIndex)] - - // Give these their "pre-reloads" values. Once we add in the reloads we'll re-process them. - _deletedSections = [_ASHierarchySectionChange allIndexesInSectionChanges:_deleteSectionChanges]; - _insertedSections = [_ASHierarchySectionChange allIndexesInSectionChanges:_insertSectionChanges]; - - for (_ASHierarchySectionChange *change in _reloadSectionChanges) { - NSIndexSet *newSections = [change.indexSet as_indexesByMapping:^(NSUInteger idx) { - NSUInteger newSec = [self newSectionForOldSection:idx]; - NSAssert(newSec != NSNotFound, @"Request to reload deleted section %lu", (unsigned long)idx); - return newSec; - }]; - - _ASHierarchySectionChange *deleteChange = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeDelete indexSet:change.indexSet animationOptions:change.animationOptions]; - [_deleteSectionChanges addObject:deleteChange]; - - _ASHierarchySectionChange *insertChange = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeInsert indexSet:newSections animationOptions:change.animationOptions]; - [_insertSectionChanges addObject:insertChange]; - } - - _reloadSectionChanges = nil; - [_ASHierarchySectionChange sortAndCoalesceChanges:_deleteSectionChanges]; [_ASHierarchySectionChange sortAndCoalesceChanges:_insertSectionChanges]; - _deletedSections = [_ASHierarchySectionChange allIndexesInSectionChanges:_deleteSectionChanges]; - _insertedSections = [_ASHierarchySectionChange allIndexesInSectionChanges:_insertSectionChanges]; + [_ASHierarchySectionChange sortAndCoalesceChanges:_reloadSectionChanges]; - // Split reloaded items into [delete(oldIndexPath), insert(newIndexPath)] - + _deletedSections = [[_ASHierarchySectionChange allIndexesInChanges:_deleteSectionChanges] copy]; + _insertedSections = [[_ASHierarchySectionChange allIndexesInChanges:_insertSectionChanges] copy]; + _reloadedSections = [[_ASHierarchySectionChange allIndexesInChanges:_reloadSectionChanges] copy]; + + // These are invalid old section indexes. + NSMutableIndexSet *deletedOrReloaded = [_deletedSections mutableCopy]; + [deletedOrReloaded addIndexes:_reloadedSections]; + + // These are invalid new section indexes. + NSMutableIndexSet *insertedOrReloaded = [_insertedSections mutableCopy]; + + // Get the new section that each reloaded section index corresponds to. + // Coalesce reload sections' indexes into deletes and inserts + [_reloadedSections enumerateIndexesUsingBlock:^(NSUInteger oldIndex, __unused BOOL * stop) { + NSUInteger newIndex = [self newSectionForOldSection:oldIndex]; + if (newIndex != NSNotFound) { + [insertedOrReloaded addIndex:newIndex]; + } + [deletedOrReloaded addIndex:oldIndex]; + }]; + + _deletedSections = deletedOrReloaded; + _insertedSections = insertedOrReloaded; + _reloadedSections = nil; + + // reload items changes need to be adjusted so that we access the correct indexPaths in the datasource NSDictionary *insertedIndexPathsMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_insertItemChanges ofType:_ASHierarchyChangeTypeInsert]; NSDictionary *deletedIndexPathsMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_deleteItemChanges ofType:_ASHierarchyChangeTypeDelete]; for (_ASHierarchyItemChange *change in _reloadItemChanges) { NSAssert(change.changeType == _ASHierarchyChangeTypeReload, @"It must be a reload change to be in here"); - NSMutableArray *newIndexPaths = [NSMutableArray arrayWithCapacity:change.indexPaths.count]; + NSMutableArray *newIndexPaths = [NSMutableArray array]; // Every indexPaths in the change need to update its section and/or row // depending on all the deletions and insertions @@ -245,21 +223,39 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) // - delete/reload indexPaths that are passed in should all be their current indexPaths // - insert indexPaths that are passed in should all be their future indexPaths after deletions for (NSIndexPath *indexPath in change.indexPaths) { - NSUInteger section = [self newSectionForOldSection:indexPath.section]; - NSUInteger item = indexPath.item; + __block NSUInteger section = indexPath.section; + __block NSUInteger row = indexPath.row; + + + // Update section number based on section insertions/deletions that are above the current section + section -= [_deletedSections countOfIndexesInRange:NSMakeRange(0, section)]; + [_insertedSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + if (idx <= section) { + section += 1; + } else { + *stop = YES; + } + }]; // Update row number based on deletions that are above the current row in the current section NSIndexSet *indicesDeletedInSection = deletedIndexPathsMap[@(indexPath.section)]; - item -= [indicesDeletedInSection countOfIndexesInRange:NSMakeRange(0, item)]; + row -= [indicesDeletedInSection countOfIndexesInRange:NSMakeRange(0, row)]; // Update row number based on insertions that are above the current row in the future section NSIndexSet *indicesInsertedInSection = insertedIndexPathsMap[@(section)]; - item += [indicesInsertedInSection as_indexChangeByInsertingItemsBelowIndex:item]; + [indicesInsertedInSection enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + if (idx <= row) { + row += 1; + } else { + *stop = YES; + } + }]; - NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:item inSection:section]; + //TODO: reuse the old indexPath object if section and row aren't changed + NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:row inSection:section]; [newIndexPaths addObject:newIndexPath]; } - // All reload changes are translated into deletes and inserts + // All reload changes are coalesced into deletes and inserts // We delete the items that needs reload together with other deleted items, at their original index _ASHierarchyItemChange *deleteItemChangeFromReloadChange = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeDelete indexPaths:change.indexPaths animationOptions:change.animationOptions presorted:NO]; [_deleteItemChanges addObject:deleteItemChangeFromReloadChange]; @@ -267,20 +263,16 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) _ASHierarchyItemChange *insertItemChangeFromReloadChange = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeInsert indexPaths:newIndexPaths animationOptions:change.animationOptions presorted:NO]; [_insertItemChanges addObject:insertItemChangeFromReloadChange]; } - _reloadItemChanges = nil; + [_reloadItemChanges removeAllObjects]; // Ignore item deletes in reloaded/deleted sections. - [_ASHierarchyItemChange sortAndCoalesceChanges:_deleteItemChanges ignoringChangesInSections:_deletedSections]; + [_ASHierarchyItemChange sortAndCoalesceChanges:_deleteItemChanges ignoringChangesInSections:deletedOrReloaded]; // Ignore item inserts in reloaded(new)/inserted sections. - [_ASHierarchyItemChange sortAndCoalesceChanges:_insertItemChanges ignoringChangesInSections:_insertedSections]; + [_ASHierarchyItemChange sortAndCoalesceChanges:_insertItemChanges ignoringChangesInSections:insertedOrReloaded]; } } -- (NSString *)description -{ - return [NSString stringWithFormat:@"<%@ %p: deletedSections=%@, insertedSections=%@, deletedItems=%@, insertedItems=%@>", NSStringFromClass(self.class), self, _deletedSections, _insertedSections, _deleteItemChanges, _insertItemChanges]; -} @end @@ -291,7 +283,6 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) { self = [super init]; if (self) { - ASDisplayNodeAssert(indexSet.count > 0, @"Request to create _ASHierarchySectionChange with no sections!"); _changeType = changeType; _indexSet = indexSet; _animationOptions = animationOptions; @@ -355,7 +346,7 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) [changes setArray:result]; } -+ (NSMutableIndexSet *)allIndexesInSectionChanges:(NSArray<_ASHierarchySectionChange *> *)changes ++ (NSMutableIndexSet *)allIndexesInChanges:(NSArray *)changes { NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet]; for (_ASHierarchySectionChange *change in changes) { @@ -364,11 +355,6 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) return indexes; } -- (NSString *)description -{ - return [NSString stringWithFormat:@"<%@: anim=%lu, type=%@, indexes=%@>", NSStringFromClass(self.class), (unsigned long)_animationOptions, NSStringFromASHierarchyChangeType(_changeType), [self.indexSet as_smallDescription]]; -} - @end @implementation _ASHierarchyItemChange @@ -377,7 +363,6 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) { self = [super init]; if (self) { - ASDisplayNodeAssert(indexPaths.count > 0, @"Request to create _ASHierarchyItemChange with no items!"); _changeType = changeType; if (presorted) { _indexPaths = indexPaths; @@ -402,9 +387,9 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) NSNumber *sectionKey = @(indexPath.section); NSMutableIndexSet *indexSet = sectionToIndexSetMap[sectionKey]; if (indexSet) { - [indexSet addIndex:indexPath.item]; + [indexSet addIndex:indexPath.row]; } else { - indexSet = [NSMutableIndexSet indexSetWithIndex:indexPath.item]; + indexSet = [NSMutableIndexSet indexSetWithIndex:indexPath.row]; sectionToIndexSetMap[sectionKey] = indexSet; } } @@ -412,7 +397,7 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) return sectionToIndexSetMap; } -+ (void)sortAndCoalesceChanges:(NSMutableArray *)changes ignoringChangesInSections:(NSIndexSet *)ignoredSections ++ (void)sortAndCoalesceChanges:(NSMutableArray *)changes ignoringChangesInSections:(NSIndexSet *)sections { if (changes.count < 1) { return; @@ -426,9 +411,12 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) // All changed index paths, sorted NSMutableArray *allIndexPaths = [NSMutableArray new]; + NSPredicate *indexPathInValidSection = [NSPredicate predicateWithBlock:^BOOL(NSIndexPath *indexPath, __unused NSDictionary *_) { + return ![sections containsIndex:indexPath.section]; + }]; for (_ASHierarchyItemChange *change in changes) { for (NSIndexPath *indexPath in change.indexPaths) { - if (![ignoredSections containsIndex:indexPath.section]) { + if ([indexPathInValidSection evaluateWithObject:indexPath]) { animationOptions[indexPath] = @(change.animationOptions); [allIndexPaths addObject:indexPath]; } @@ -471,9 +459,4 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) [changes setArray:result]; } -- (NSString *)description -{ - return [NSString stringWithFormat:@"<%@: anim=%lu, type=%@, indexPaths=%@>", NSStringFromClass(self.class), (unsigned long)_animationOptions, NSStringFromASHierarchyChangeType(_changeType), self.indexPaths]; -} - @end diff --git a/AsyncDisplayKitTests/ASCollectionViewTests.m b/AsyncDisplayKitTests/ASCollectionViewTests.m index 5253562d6d..6b0d1b651b 100644 --- a/AsyncDisplayKitTests/ASCollectionViewTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewTests.m @@ -200,44 +200,4 @@ XCTAssertTrue([(ASTextCellNodeWithSetSelectedCounter *)node setSelectedCounter] == 6, @"setSelected: should not be called on node multiple times."); } -- (void)testTuningParametersWithExplicitRangeMode -{ - UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; - ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; - - ASRangeTuningParameters minimumRenderParams = { .leadingBufferScreenfuls = 0.1, .trailingBufferScreenfuls = 0.1 }; - ASRangeTuningParameters minimumPreloadParams = { .leadingBufferScreenfuls = 0.1, .trailingBufferScreenfuls = 0.1 }; - ASRangeTuningParameters fullRenderParams = { .leadingBufferScreenfuls = 0.5, .trailingBufferScreenfuls = 0.5 }; - ASRangeTuningParameters fullPreloadParams = { .leadingBufferScreenfuls = 1, .trailingBufferScreenfuls = 0.5 }; - - [collectionView setTuningParameters:minimumRenderParams forRangeMode:ASLayoutRangeModeMinimum rangeType:ASLayoutRangeTypeDisplay]; - [collectionView setTuningParameters:minimumPreloadParams forRangeMode:ASLayoutRangeModeMinimum rangeType:ASLayoutRangeTypeFetchData]; - [collectionView setTuningParameters:fullRenderParams forRangeMode:ASLayoutRangeModeFull rangeType:ASLayoutRangeTypeDisplay]; - [collectionView setTuningParameters:fullPreloadParams forRangeMode:ASLayoutRangeModeFull rangeType:ASLayoutRangeTypeFetchData]; - - XCTAssertTrue(ASRangeTuningParametersEqualToRangeTuningParameters(minimumRenderParams, - [collectionView tuningParametersForRangeMode:ASLayoutRangeModeMinimum rangeType:ASLayoutRangeTypeDisplay])); - XCTAssertTrue(ASRangeTuningParametersEqualToRangeTuningParameters(minimumPreloadParams, - [collectionView tuningParametersForRangeMode:ASLayoutRangeModeMinimum rangeType:ASLayoutRangeTypeFetchData])); - XCTAssertTrue(ASRangeTuningParametersEqualToRangeTuningParameters(fullRenderParams, - [collectionView tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:ASLayoutRangeTypeDisplay])); - XCTAssertTrue(ASRangeTuningParametersEqualToRangeTuningParameters(fullPreloadParams, - [collectionView tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:ASLayoutRangeTypeFetchData])); -} - -- (void)testTuningParameters -{ - UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; - ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; - - ASRangeTuningParameters renderParams = { .leadingBufferScreenfuls = 1.2, .trailingBufferScreenfuls = 3.2 }; - ASRangeTuningParameters preloadParams = { .leadingBufferScreenfuls = 4.3, .trailingBufferScreenfuls = 2.3 }; - - [collectionView setTuningParameters:renderParams forRangeType:ASLayoutRangeTypeDisplay]; - [collectionView setTuningParameters:preloadParams forRangeType:ASLayoutRangeTypeFetchData]; - - XCTAssertTrue(ASRangeTuningParametersEqualToRangeTuningParameters(renderParams, [collectionView tuningParametersForRangeType:ASLayoutRangeTypeDisplay])); - XCTAssertTrue(ASRangeTuningParametersEqualToRangeTuningParameters(preloadParams, [collectionView tuningParametersForRangeType:ASLayoutRangeTypeFetchData])); -} - @end diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index 98914d1bd4..7cb13fb8ca 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -128,6 +128,7 @@ return textCellNode; } + - (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath { return ^{ @@ -139,52 +140,12 @@ @end -@interface ASTableViewFilledDelegate : NSObject -@end - -@implementation ASTableViewFilledDelegate - -- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath -{ - return ASSizeRangeMakeExactSize(CGSizeMake(10, 42)); -} - -@end - @interface ASTableViewTests : XCTestCase @property (atomic, retain) ASTableView *testTableView; @end @implementation ASTableViewTests -- (void)testConstrainedSizeForRowAtIndexPath -{ - // Initial width of the table view is non-zero and all nodes are measured with this size. - // Any subsequence size change must trigger a relayout. - // Width and height are swapped so that a later size change will simulate a rotation - ASTestTableView *tableView = [[ASTestTableView alloc] __initWithFrame:CGRectMake(0, 0, 100, 400) - style:UITableViewStylePlain]; - - ASTableViewFilledDelegate *delegate = [ASTableViewFilledDelegate new]; - ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; - - tableView.asyncDelegate = delegate; - tableView.asyncDataSource = dataSource; - - [tableView reloadDataImmediately]; - [tableView setNeedsLayout]; - [tableView layoutIfNeeded]; - - for (int section = 0; section < NumberOfSections; section++) { - for (int row = 0; row < NumberOfRowsPerSection; row++) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; - CGRect rect = [tableView rectForRowAtIndexPath:indexPath]; - XCTAssertEqual(rect.size.width, 100); // specified width should be ignored for table - XCTAssertEqual(rect.size.height, 42); - } - } -} - // TODO: Convert this to ARC. - (void)DISABLED_testTableViewDoesNotRetainItselfAndDelegate { diff --git a/AsyncDisplayKitTests/ASTableViewThrashTests.m b/AsyncDisplayKitTests/ASTableViewThrashTests.m index 62ddffdd90..5d6fa8f637 100644 --- a/AsyncDisplayKitTests/ASTableViewThrashTests.m +++ b/AsyncDisplayKitTests/ASTableViewThrashTests.m @@ -8,7 +8,6 @@ @import XCTest; #import -#import "ASTableViewInternal.h" // Set to 1 to use UITableView and see if the issue still exists. #define USE_UIKIT_REFERENCE 0 @@ -20,8 +19,8 @@ #define TableView ASTableView #endif -#define kInitialSectionCount 10 -#define kInitialItemCount 10 +#define kInitialSectionCount 20 +#define kInitialItemCount 20 #define kMinimumItemCount 5 #define kMinimumSectionCount 3 #define kFickleness 0.1 @@ -146,7 +145,7 @@ static volatile int32_t ASThrashTestSectionNextID = 1; } - (NSString *)description { - return [NSString stringWithFormat:@"
", (unsigned long)_sectionID, (unsigned long)self.items.count, ASThrashArrayDescription(self.items)]; + return [NSString stringWithFormat:@"
", (unsigned long)_sectionID, (unsigned long)self.items.count]; } - (id)copyWithZone:(NSZone *)zone { @@ -446,22 +445,17 @@ static NSInteger ASThrashUpdateCurrentSerializationVersion = 1; @implementation ASTableViewThrashTests { // The current update, which will be logged in case of a failure. ASThrashUpdate *_update; - BOOL _failed; } #pragma mark Overrides - (void)tearDown { - if (_failed && _update != nil) { - NSLog(@"Failed update %@: %@", _update, _update.logFriendlyBase64Representation); - } - _failed = NO; _update = nil; } // NOTE: Despite the documentation, this is not always called if an exception is caught. - (void)recordFailureWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber expected:(BOOL)expected { - _failed = YES; + [self logCurrentUpdateIfNeeded]; [super recordFailureWithDescription:description inFile:filePath atLine:lineNumber expected:expected]; } @@ -483,12 +477,11 @@ static NSInteger ASThrashUpdateCurrentSerializationVersion = 1; } ASThrashDataSource *ds = [[ASThrashDataSource alloc] initWithData:_update.oldData]; - ds.tableView.test_enableSuperUpdateCallLogging = YES; [self applyUpdate:_update toDataSource:ds]; [self verifyDataSource:ds]; } -- (void)testThrashingWildly { +- (void)DISABLED_testThrashingWildly { for (NSInteger i = 0; i < kThrashingIterationCount; i++) { [self setUp]; ASThrashDataSource *ds = [[ASThrashDataSource alloc] initWithData:[ASThrashTestSection sectionsWithCount:kInitialSectionCount]]; @@ -502,6 +495,12 @@ static NSInteger ASThrashUpdateCurrentSerializationVersion = 1; #pragma mark Helpers +- (void)logCurrentUpdateIfNeeded { + if (_update != nil) { + NSLog(@"Failed update %@: %@", _update, _update.logFriendlyBase64Representation); + } +} + - (void)applyUpdate:(ASThrashUpdate *)update toDataSource:(ASThrashDataSource *)dataSource { TableView *tableView = dataSource.tableView; @@ -534,7 +533,7 @@ static NSInteger ASThrashUpdateCurrentSerializationVersion = 1; [tableView waitUntilAllUpdatesAreCommitted]; #endif } @catch (NSException *exception) { - _failed = YES; + [self logCurrentUpdateIfNeeded]; @throw exception; } } @@ -554,7 +553,7 @@ static NSInteger ASThrashUpdateCurrentSerializationVersion = 1; XCTAssertEqual([tableView rectForRowAtIndexPath:indexPath].size.height, item.rowHeight); #else ASThrashTestNode *node = (ASThrashTestNode *)[tableView nodeForRowAtIndexPath:indexPath]; - XCTAssertEqualObjects(node.item, item, @"Wrong node at index path %@", indexPath); + XCTAssertEqual(node.item, item); #endif } } diff --git a/AsyncDisplayKitTests/TestResources/ASThrashTestRecordedCase b/AsyncDisplayKitTests/TestResources/ASThrashTestRecordedCase index e9dc1e9cc0..9e8343590e 100644 --- a/AsyncDisplayKitTests/TestResources/ASThrashTestRecordedCase +++ b/AsyncDisplayKitTests/TestResources/ASThrashTestRecordedCase @@ -1 +1 @@ -YnBsaXN0MDDUAAEAAgADAAQABQAGAagBqVgkdmVyc2lvblgkb2JqZWN0c1kkYXJjaGl2ZXJUJHRvcBIAAYagrxBrAAcACAAPAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAPABEAEoATwBWAFkAXABeAGEAaQBsAG8AcgB1AHgAgACGAI4AkgCWAJkAnACfAKIApgCqALIAtQC4ALsAvgDBAMUAzQDQANMA1gDZANwA4ADoAOsA7gDxAPQA9wD7AQMBBgEJAQwBDwESARQBGwEeAScBKgEuATYBOQE8AT8BQgFFAUgBUAFTAVwBXwFhAWkBawFtAW8BcQFzAXsBfQF/AYEBgwGFAYwBkAGTAZYBmgGcAaABpFUkbnVsbNMACQAKAAsADAANAA5VX2RpY3RYX3ZlcnNpb25WJGNsYXNzgAIQAYBq0wAQABEACwASAB8ALFdOUy5rZXlzWk5TLm9iamVjdHOsABMAFAAVABYAFwAYABkAGgAbABwAHQAegAOABIAFgAaAB4AIgAmACoALgAyADYAOrAAgACEAIgAjACQAJQAmACcAKAApACoAK4APgBGAE4AYgB6ARYBVgFaAXIBigGeAaIBpXxAQaW5zZXJ0ZWRTZWN0aW9uc18QFnJlcGxhY2VkU2VjdGlvbkluZGV4ZXNfEBNpbnNlcnRlZEl0ZW1JbmRleGVzXnJlcGxhY2luZ0l0ZW1zV29sZERhdGFUZGF0YV8QFmluc2VydGVkU2VjdGlvbkluZGV4ZXNfEBJkZWxldGVkSXRlbUluZGV4ZXNfEBNyZXBsYWNlZEl0ZW1JbmRleGVzXWluc2VydGVkSXRlbXNfEBVkZWxldGVkU2VjdGlvbkluZGV4ZXNfEBFyZXBsYWNpbmdTZWN0aW9uc9IAEQALADoAO6CAENIAPQA+AD8AQFokY2xhc3NuYW1lWCRjbGFzc2VzXk5TTXV0YWJsZUFycmF5owBBAEIAQ15OU011dGFibGVBcnJheVdOU0FycmF5WE5TT2JqZWN01ABFAAsARgBHAEgASQANAA1aTlNMb2NhdGlvblxOU1JhbmdlQ291bnRYTlNMZW5ndGgQAoAS0gA9AD4ASwBMXxARTlNNdXRhYmxlSW5kZXhTZXSjAE0ATgBDXxARTlNNdXRhYmxlSW5kZXhTZXRaTlNJbmRleFNldNIAEQALAFAAO6QAUQBSAFMAVIAUgBWAFoAXgBDUAEUACwBGAEcAVwBJAA0ADRADgBLSAEYACwBaAEkQAIAS0gBGAAsAWgBJgBLUAEUACwBGAEcAXwBJAA0ADRAEgBLSABEACwBiADulAGMAZABlAGYAZ4AZgBqAG4AcgB2AENIAEQALAGoAO6CAENIAEQALAG0AO6CAENIAEQALAHAAO6CAENIAEQALAHMAO6CAENIAEQALAHYAO6CAENIAEQALAHkAf6UAegB7AHwAfQB+gB+AKIAvgDaAPYBE0wCBAIIACwCDAIQAhVVpdGVtc1lzZWN0aW9uSUSAIBEBhYAn0gARAAsAhwA7pQCIAIkAigCLAIyAIYAjgCSAJYAmgBDSAI8ACwCQAJFWaXRlbUlEEQJogCLSAD0APgCTAJRfEBBBU1RocmFzaFRlc3RJdGVtogCVAENfEBBBU1RocmFzaFRlc3RJdGVt0gCPAAsAlwCREQJpgCLSAI8ACwCaAJERAmqAItIAjwALAJ0AkRECa4Ai0gCPAAsAoACREQJsgCLSAD0APgCjAKRfEBNBU1RocmFzaFRlc3RTZWN0aW9uogClAENfEBNBU1RocmFzaFRlc3RTZWN0aW9u0wCBAIIACwCnAKgAhYApEQGGgCfSABEACwCrADulAKwArQCuAK8AsIAqgCuALIAtgC6AENIAjwALALMAkRECbYAi0gCPAAsAtgCREQJugCLSAI8ACwC5AJERAm+AItIAjwALALwAkRECcIAi0gCPAAsAvwCREQJxgCLTAIEAggALAMIAwwCFgDARAYeAJ9IAEQALAMYAO6UAxwDIAMkAygDLgDGAMoAzgDSANYAQ0gCPAAsAzgCREQJygCLSAI8ACwDRAJERAnOAItIAjwALANQAkRECdIAi0gCPAAsA1wCREQJ1gCLSAI8ACwDaAJERAnaAItMAgQCCAAsA3QDeAIWANxEBiIAn0gARAAsA4QA7pQDiAOMA5ADlAOaAOIA5gDqAO4A8gBDSAI8ACwDpAJERAneAItIAjwALAOwAkRECeIAi0gCPAAsA7wCREQJ5gCLSAI8ACwDyAJERAnqAItIAjwALAPUAkRECe4Ai0wCBAIIACwD4APkAhYA+EQGJgCfSABEACwD8ADulAP0A/gD/AQABAYA/gECAQYBCgEOAENIAjwALAQQAkRECfIAi0gCPAAsBBwCREQJ9gCLSAI8ACwEKAJERAn6AItIAjwALAQ0AkRECf4Ai0gCPAAsBEACREQKAgCLSAD0APgBCAROiAEIAQ9IAEQALARUAO6QBFgEXARgBGYBGgEmAUIBSgBDTAIEAggALARwAqACFgEeAJ9IAEQALAR8AO6YArACtAK4BIwCvALCAKoArgCyASIAtgC6AENIAjwALASgAkREChoAi0wCBAIIACwErASwAhYBKEQGZgCfSABEACwEvADulATABMQEyATMBNIBLgEyATYBOgE+AENIAjwALATcAkRECgYAi0gCPAAsBOgCREQKCgCLSAI8ACwE9AJERAoOAItIAjwALAUAAkREChIAi0gCPAAsBQwCREQKFgCLTAIEAggALAUYA3gCFgFGAJ9IAEQALAUkAO6UA4gDjAOQA5QDmgDiAOYA6gDuAPIAQ0wCBAIIACwFRAPkAhYBTgCfSABEACwFUADumAP0A/gD/AQABWQEBgD+AQIBBgEKAVIBDgBDSAI8ACwFdAJERAoeAItIARgALAFoASYAS0gARAAsBYgA7pQFjAWQBZQFmAWeAV4BYgFmAWoBbgBDSAEYACwBaAEmAEtIARgALAFoASYAS0gBGAAsAWgBJgBLSAEYACwBaAEmAEtIARgALAFoASYAS0gARAAsBdAA7pQF1AXYBdwF4AXmAXYBegF+AYIBhgBDSAEYACwBaAEmAEtIARgALAFoASYAS0gBGAAsAWgBJgBLSAEYACwBaAEmAEtIARgALAFoASYAS0gARAAsBhgA7pAGHAYgBiQGKgGOAZIBlgGaAENIAEQALAY0AO6EBI4BIgBDSABEACwGRAH+ggETSABEACwGUADuggBDSABEACwGXADuhAVmAVIAQ1ABFAAsARgBHAFoASQANAA2AEtIAEQALAZ0AO6EBF4BJgBDSAD0APgGhAaJcTlNEaWN0aW9uYXJ5ogGjAENcTlNEaWN0aW9uYXJ50gA9AD4BpQGmXkFTVGhyYXNoVXBkYXRlogGnAENeQVNUaHJhc2hVcGRhdGVfEA9OU0tleWVkQXJjaGl2ZXLRAaoBq1Ryb290gAEACAAZACIAKwA1ADoAPwEYAR4BKwExAToBQQFDAUUBRwFUAVwBZwGAAYIBhAGGAYgBigGMAY4BkAGSAZQBlgGYAbEBswG1AbcBuQG7Ab0BvwHBAcMBxQHHAckBywHeAfcCDQIcAiQCKQJCAlcCbQJ7ApMCpwKwArECswK8AscC0ALfAuYC9QL9AwYDFwMiAy8DOAM6AzwDRQNZA2ADdAN/A4gDkQOTA5UDlwOZA5sDrAOuA7ADuQO7A70DxgPIA9kD2wPdA+YD8QPzA/UD9wP5A/sD/QQGBAcECQQSBBMEFQQeBB8EIQQqBCsELQQ2BDcEOQRCBE0ETwRRBFMEVQRXBFkEZgRsBHYEeAR7BH0EhgSRBJMElQSXBJkEmwSdBKYErQSwBLIEuwTOBNME5gTvBPIE9AT9BQAFAgULBQ4FEAUZBRwFHgUnBT0FQgVYBWUFZwVqBWwFdQWABYIFhAWGBYgFigWMBZUFmAWaBaMFpgWoBbEFtAW2Bb8FwgXEBc0F0AXSBd8F4QXkBeYF7wX6BfwF/gYABgIGBAYGBg8GEgYUBh0GIAYiBisGLgYwBjkGPAY+BkcGSgZMBlkGWwZeBmAGaQZ0BnYGeAZ6BnwGfgaABokGjAaOBpcGmgacBqUGqAaqBrMGtga4BsEGxAbGBtMG1QbYBtoG4wbuBvAG8gb0BvYG+Ab6BwMHBgcIBxEHFAcWBx8HIgckBy0HMAcyBzsHPgdAB0kHTgdXB2AHYgdkB2YHaAdqB3cHeQd7B4QHkQeTB5UHlweZB5sHnQefB6gHqwetB7oHvAe/B8EHygfVB9cH2QfbB90H3wfhB+oH7QfvB/gH+wf9CAYICQgLCBQIFwgZCCIIJQgnCDQINgg4CEEITAhOCFAIUghUCFYIWAhlCGcIaQhyCH8IgQiDCIUIhwiJCIsIjQiWCJkImwikCKYIrwi6CLwIvgjACMIIxAjGCM8I0QjaCNwI5QjnCPAI8gj7CP0JBgkRCRMJFQkXCRkJGwkdCSYJKAkxCTMJPAk+CUcJSQlSCVQJXQlmCWgJaglsCW4JcAl5CXwJfgmACYkJigmMCZUJlgmYCaEJpAmmCagJuQm7CcQJxwnJCcsJ1AnhCeYJ8wn8CgsKEAofCjEKNgo7AAAAAAAAAgIAAAAAAAABrAAAAAAAAAAAAAAAAAAACj0= \ No newline at end of file +YnBsaXN0MDDUAAEAAgADAAQABQAGDfYN91gkdmVyc2lvblgkb2JqZWN0c1kkYXJjaGl2ZXJUJHRvcBIAAYagrxEDRQAHAAgADwAtAC4ALwAwADEAMgAzADQANQA2ADcAOAA5AEAARgBdAGEAaABrAG4AcQB0AHcAegB9AIAAgwCGAIkAjACPAJIAlQCYAJsAngChAKYAqgCuAMUAyADLAM4A0QDUANcA2gDdAOAA4wDmAOkA7ADvAPIA9QD4APsA/gEBAQUBHAEfASIBJQEoASsBLgExATQBNwE6AT0BQAFDAUYBSQFMAU8BUgFVAVgBXAFzAXYBeQF8AX8BggGFAYgBiwGOAZEBlAGXAZoBnQGgAaMBpgGpAawBrwG1AbkBvgHDAdsB4AHjAeYB6AHqAewB7wHyAfQB9gH5AfwB/wICAgUCBwIKAg0CDwITAhYCGAIaAhwCIAIjAiYCKQIsAkMCSAJLAk4CUwJWAlkCXQJgAmQCZwJsAm8CcgJ4AnsCfgKBAoUCiAKNApACkwKXApoCngKhAqYCqQKsArECtAK3Ar0CwALDAsYCywLOAtEC2ALbAt4C4QLkAu4C8QL0AvcC+gL9AwADAwMHAwoDEAMTAxYDGQMeAyEDJAMnAz4DQgNZA1wDXwNiA2UDaANrA24DcQN0A3cDegN9A4ADgwOGA4kDjAOPA5IDlQOZA7ADswO2A7kDvAO/A8IDxQPIA8sDzgPRA9QD1wPaA90D4APjA+YD6QPsA/AEBwQKBA0EEAQTBBYEGQQcBB8EIgQlBCgEKwQuBDEENAQ3BDoEPQRABEMERwReBGEEZARnBGoEbQRwBHMEdgR5BHwEfwSCBIUEiASLBI4EkQSUBJcEmgSeBLUEuAS7BL4EwQTEBMcEygTNBNAE0wTWBNkE3ATfBOIE5QToBOsE7gTxBPUFDAUPBRIFFQUYBRsFHgUhBSQFJwUqBS0FMAUzBTYFOQU8BT8FQgVFBUgFTAVjBWYFaQVsBW8FcgV1BXgFewV+BYEFhAWHBYoFjQWQBZMFlgWZBZwFnwWjBboFvQXABcMFxgXJBcwFzwXSBdUF2AXbBd4F4QXkBecF6gXtBfAF8wX2BfoGEQYUBhcGGgYdBiAGIwYmBikGLAYvBjIGNQY4BjsGPgZBBkQGRwZKBk0GUQZoBmsGbgZxBnQGdwZ6Bn0GgAaDBoYGiQaMBo8GkgaVBpgGmwaeBqEGpAaoBr8GwgbFBsgGywbOBtEG1AbXBtoG3QbgBuMG5gbpBuwG7wbyBvUG+Ab7Bv8HFgcZBxwHHwciByUHKAcrBy4HMQc0BzcHOgc9B0AHQwdGB0kHTAdPB1IHVgdtB3AHcwd2B3kHfAd/B4IHhQeIB4sHjgeRB5QHlweaB50HoAejB6YHqQetB8QHxwfKB80H0AfTB9YH2QfcB98H4gflB+gH6wfuB/EH9Af3B/oH/QgACAQIGwgeCCEIJAgnCCoILQgwCDMINgg5CDwIPwhCCEUISAhLCE4IUQhUCFcIWwhyCHUIeAh7CH4IgQiECIcIigiNCJAIkwiWCJkInAifCKIIpQioCKsIrgiyCMkIzAjPCNII1QjYCNsI3gjhCOQI5wjqCO0I8AjzCPYI+Qj8CP8JAgkFCQkJIAkjCSYJKQksCS8JMgk1CTgJOwk+CUEJRAlHCUoJTQlQCVMJVglZCVwJYAl3CXoJfQmACYMJhgmJCYwJjwmSCZUJmAmbCZ4JoQmkCacJqgmtCbAJswm3Cc4J0QnUCdcJ2gndCeAJ4wnmCekJ7AnvCfIJ9Qn4CfsJ/goBCgQKBwoKCiIKJQo8Cj8KQgpcCl8KYgplCmgKfQqACpUKmAqwCrMKtgq6Cs0K0ArTCtYK2QrcCt8K4grlCugK6wruCvEK9Ar3CvoK/QsBCxcLGgsdCyALIwsmCykLLAsvCzILNQs4CzsLPgtBC0QLRwtKC00LUAtTC2wLbwtyC3ULjAuPC5ILrAuvC7ILtQu4C8wLzwvmC+kL7AvvDAgMCwwODBEMFQwoDCsMLgwxDDQMNww6DD0MQAxDDEYMSQxMDE8MUgxVDFgMWwxyDHUMeAx7DH4MgQyWDJkMnAyfDLYMuQy8DL8M1gzYDNsM3QzgDOMM5gzpDOwM7gzwDPIM9Az2DPgM+gz9DQANAw0GDQkNCw0ODRENFA0XDRoNMQ0zDTYNOQ08DT8NQg1FDUgNSw1NDU8NUQ1TDVYNWQ1cDV8NYQ1kDWcNag1tDXANcw11DXgNew1+DYENgw2bDZ8NpQ2oDaoNrQ2wDbUNug2+DcQNxw3MDdIN2Q3eDeIN5Q3oDe4N8lUkbnVsbNMACQAKAAsADAANAA5VX2RpY3RYX3ZlcnNpb25WJGNsYXNzgAIQAYEDRNMAEAARAAsAEgA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/rxAUAV4BXwFgAWEBYgFjAWQBZQFmAWcBaAFpAWoBawFsAW0BbgFvAXABcYBXgFiAWYBagFuAXIBdgF6AX4BggGGAYoBjgGSAZYBmgGeAaIBpgGqAJ9IAXgALAXQAYBEEToAT0gBeAAsBdwBgEQRPgBPSAF4ACwF6AGARBFCAE9IAXgALAX0AYBEEUYAT0gBeAAsBgABgEQRSgBPSAF4ACwGDAGARBFOAE9IAXgALAYYAYBEEVIAT0gBeAAsBiQBgEQRVgBPSAF4ACwGMAGARBFaAE9IAXgALAY8AYBEEV4AT0gBeAAsBkgBgEQRYgBPSAF4ACwGVAGARBFmAE9IAXgALAZgAYBEEWoAT0gBeAAsBmwBgEQRbgBPSAF4ACwGeAGARBFyAE9IAXgALAaEAYBEEXYAT0gBeAAsBpABgEQRegBPSAF4ACwGnAGARBF+AE9IAXgALAaoAYBEEYIAT0gBeAAsBrQBgEQRhgBPTAbAACwGxAbIBswG0XE5TUmFuZ2VDb3VudFtOU1JhbmdlRGF0YRACgG6AbNIBtgALAbcBuFdOUy5kYXRhRAYCEAGAbdIAYgBjAboBu11OU011dGFibGVEYXRhowG8Ab0AZ11OU011dGFibGVEYXRhVk5TRGF0YdIAYgBjAb8BwF8QEU5TTXV0YWJsZUluZGV4U2V0owHBAcIAZ18QEU5TTXV0YWJsZUluZGV4U2V0Wk5TSW5kZXhTZXTSABEACwHEAD+vEBUBxQHGAccByAHJAcoBywHMAc0BzgHPAdAB0QHSAdMB1AHVAdYB1wHYAdmAcIBxgHOAdIB1gHaAeIB5gHqAfIB9gH+AgICCgIOAhYCGgIeAiICKgIyAJ9QB3AALAbAB3QHeAbMADQANWk5TTG9jYXRpb25YTlNMZW5ndGgQAIBu0wGwAAsBsQGyAbMB4oBugHLSAbYACwHkAbhEAgEUAoBt0gGwAAsB3gGzgG7SAbAACwHeAbOAbtIBsAALAd4Bs4Bu0wGwAAsBsQGyAbMB7oBugHfSAbYACwHwAbhEEAETAYBt0gGwAAsB3gGzgG7SAbAACwHeAbOAbtMBsAALAbEBsgGzAfiAboB70gG2AAsB+gG4RAMBDAGAbdQB3AALAbAB3QH9AbMADQANEAeAbtMBsAALAbEBsgGzAgGAboB+0gG2AAsCAwG4RAwCFAGAbdI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AE9IAXgALBRMAYBECgoAT0gBeAAsFFgBgEQKDgBPSAF4ACwUZAGARAoSAE9IAXgALBRwAYBEChYAT0gBeAAsFHwBgEQKGgBPSAF4ACwUiAGARAoeAE9IAXgALBSUAYBECiIAT0gBeAAsFKABgEQKJgBPSAF4ACwUrAGARAoqAE9IAXgALBS4AYBECi4AT0gBeAAsFMQBgEQKMgBPSAF4ACwU0AGARAo2AE9IAXgALBTcAYBECjoAT0gBeAAsFOgBgEQKPgBPSAF4ACwU9AGARApCAE9IAXgALBUAAYBECkYAT0gBeAAsFQwBgEQKSgBPSAF4ACwVGAGARApOAE9MAQQBCAAsFSQVKAEWBAVIQW4Ao0gARAAsFTQA/rxAUBU4FTwVQBVEFUgVTBVQFVQVWBVcFWAVZBVoFWwVcBV0FXgVfBWAFYYEBU4EBVIEBVYEBVoEBV4EBWIEBWYEBWoEBW4EBXIEBXYEBXoEBX4EBYIEBYYEBYoEBY4EBZIEBZYEBZoAn0gBeAAsFZABgEQKUgBPSAF4ACwVnAGARApWAE9IAXgALBWoAYBECloAT0gBeAAsFbQBgEQKXgBPSAF4ACwVwAGARApiAE9IAXgALBXMAYBECmYAT0gBeAAsFdgBgEQKagBPSAF4ACwV5AGARApuAE9IAXgALBXwAYBECnIAT0gBeAAsFfwBgEQKdgBPSAF4ACwWCAGARAp6AE9IAXgALBYUAYBECn4AT0gBeAAsFiABgEQKggBPSAF4ACwWLAGARAqGAE9IAXgALBY4AYBECooAT0gBeAAsFkQBgEQKjgBPSAF4ACwWUAGARAqSAE9IAXgALBZcAYBECpYAT0gBeAAsFmgBgEQKmgBPSAF4ACwWdAGARAqeAE9MAQQBCAAsFoAWhAEWBAWgQXIAo0gARAAsFpAA/rxAUBaUFpgWnBagFqQWqBasFrAWtBa4FrwWwBbEFsgWzBbQFtQW2BbcFuIEBaYEBaoEBa4EBbIEBbYEBboEBb4EBcIEBcYEBcoEBc4EBdIEBdYEBdoEBd4EBeIEBeYEBeoEBe4EBfIAn0gBeAAsFuwBgEQKogBPSAF4ACwW+AGARAqmAE9IAXgALBcEAYBECqoAT0gBeAAsFxABgEQKrgBPSAF4ACwXHAGARAqyAE9IAXgALBcoAYBECrYAT0gBeAAsFzQBgEQKugBP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CToECT4ECUIECUYDGgQJTgQJUgMeBAliAJ9IAXgALDHMAYBEEc4AT0gBeAAsMdgBgEQR0gBPSAF4ACwx5AGARBHWAE9IAXgALDHwAYBEEdoAT0wBBAEIACwx/CV4ARYEC7YAo0gARAAsMggA/rxASCWIJYwMbCWUJZglnCWgMiglqCWsMjQlsCW4JbwlxCXIJcwMcgQJbgQJcgMmBAl6BAl+BAmCBAmGBAu6BAmOBAmSBAu+BAmWBAmeBAmiBAmqBAmuBAmyAyoAn0gBeAAsMlwBgEQR3gBPSAF4ACwyaAGARBHiAE9MAQQBCAAsMnQm1AEWBAvGAKNIAEQALDKAAP68QFAm5CboJuwm8Cb0Mpgm+Cb8JwAnBCcIJwwnECcUJxgnHCcgJyQnLCcyBAnGBAnKBAnOBAnSBAnWBAvKBAnaBAneBAniBAnmBAnqBAnuBAnyBAn2BAn6BAn+BAoCBAoGBAoOBAoSAJ9IAXgALDLcAYBEEeYAT0wGwAAsBsQIQAbMMu4BugQL00gG2AAsMvQG4RgIBDQEQAoBt0gARAAsMwAA/rxAUDMEMwgzDDMQMxQzGDMcMyAzJDMoMywzMDM0MzgzPDNAM0QzSDNMM1IEC9oEC94EC+IEC+YEC+4EC/YEC/oEC/4EDAIEDAYEDAoEDA4EDBIEDBYEDB4EDCYEDCoEDC4EDDYEDD4An0gGwAAsB3gGzgG7UAdwACwGwAd0M2QGzAA0ADRANgG7SAbAACwHeAbOAbtMBsAALAbEBsgGzDN+AboEC+tIBtgALDOEBuEQDAQ0BgG3TAbAACwGxAbIBswzlgG6BAvzSAbYACwznAbhEAgEOAYBt1AHcAAsBsAHdDOoBswANAA0QDoBu0gGwAAsB3gGzgG7SAbAACwHeAbOAbtIBsAALAd4Bs4Bu0gGwAAsB3gGzgG7UAdwACwGwAd0CEAGzAA0ADYBu0gGwAAsB3gGzgG7SAbAACwHeAbOAbtMBsAALAbECEAGzDPyAboEDBtIBtgALDP4BuEYCAQoBDwGAbdMBsAALAbEBsgGzDQKAboEDCNIBtgALDQQBuEQEAQYBgG3UAdwACwGwAd0NBwGzAA0ADRATgG7SAbAACwHeAbOAbtMBsAALAbECEAGzDQ2AboEDDNIBtgALDQ8BuEYEAQYBEQKAbdMBsAALAbECHQGzDROAboEDDtI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9FUYVSBVKFUwVThVQFVkVXBVeFWcVahVsFXUVeBV6FYMVhhWIFZEVoBWiFaQVphWoFaoVrBWuFbAVuRW8Fb4VxxXKFcwV1RXYFdoV4xXmFegV8RX0FfYV/xYCFgQWDRYQFhIWGxYeFiAWIhYrFi4WMBY5FkAWQhZEFkYWSBZRFlQWVhZfFmIWZBZtFnAWchZ7FoAWghaEFoYWjxaSFpQWnRagFqIWqxasFq4WtxbiFuQW5hboFusW7hbxFvQW9xb6Fv0XABcDFwYXCRcMFw8XEhcVFxgXGxcdFyoXLBcuFzAXORdkF2YXaBdqF2wXbhdwF3IXdBd2F3gXehd8F34XgBeCF4QXhheIF4oXjBeOF5cXmhecF6UXqBeqF7MXthe4F8EXxBfGF88X0hfUF90X4BfiF+sX7hfwF/kX/Bf+GAcYChgMGBUYGBgaGCMYJhgoGDEYNBg2GD8YQhhEGE0YUBhSGFsYXhhgGGkYbBhuGHcYehh8GIUYiBiKGJMYlhiYGKEYpBimGLMYtRi3GLkYwhjtGO8Y8RjzGPUY9xj5GPsY/Rj/GQEZAxkFGQcZCRkLGQ0ZDxkRGRMZFRkXGSAZIxklGS4ZMRkzGTwZPxlBGUoZTRlPGVgZWxldGWYZaRlrGXQZdxl5GYIZhRmHGZAZkxmVGZ4ZoRmjGawZrxmxGboZvRm/GcgZyxnNGdYZ2RnbGeQZ5xnpGfIZ9Rn3GgAaAxoFGg4aERoTGhwaHxohGioaLRovGjwaPhpAGkIaSxp2Gngaehp8Gn4agBqDGoYaiRqMGo8akhqVGpgamxqeGqEapBqnGqoarRqvGrgauxq9GsYayRrLGtQa1xrZGuIa5RrnGvAa8xr1Gv4bARsDGwwbDxsRGxobHRsfGygbKxstGzYbORs7G0QbRxtJG1IbVRtXG2AbYxtlG24bcRtzG3wbfxuBG4objRuPG5gbmxudG6YbqRurG7Qbtxu5G8IbxRvHG9Qb1xvZG9sb5BwPHBIcFRwYHBscHhwhHCQcJxwqHC0cMBwzHDYcORw8HD8cQhxFHEgcSxxNHFYcWRxbHGQcZxxpHHIcdRx3HIAcgxyFHI4ckRyTHJwcnxyhHKocrRyvHLgcuxy9HMYcyRzLHNQc1xzZHOIc5RznHPAc8xz1HP4dAR0DHQwdDx0RHRodHR0fHSgdKx0tHTYdOR07HUQdRx1JHVIdVR1XHWAdYx1lHXIddR13HXkdgh2tHbAdsx22HbkdvB2/HcIdxR3IHcsdzh3RHdQd1x3aHd0d4B3jHeYd6R3rHfQd9x35HgIeBR4HHhAeEx4VHh4eIR4jHiweLx4xHjoePR4/HkgeSx5NHlYeWR5bHmQeZx5pHnIedR53HoAegx6FHo4ekR6THpwenx6hHqoerR6vHrgeux69HsYeyR7LHtQe1x7ZHuIe5R7nHvAe8x71Hv4fAR8DHxAfEx8VHxcfIB9LH04fUR9UH1cfWh9dH2AfYx9mH2kfbB9vH3IfdR94H3sffh+BH4Qfhx+JH5IflR+XH6Afox+lH64fsR+zH7wfvx/BH8ofzR/PH9gf2x/dH+Yf6R/rH/Qf9x/5IAIgBSAHIBAgEyAVIB4gISAjICwgLyAxIDogPSA/IEggSyBNIFYgWSBbIGQgZyBpIHIgdSB3IIAggyCFII4gkSCTIJwgnyChIK4gsSCzILUgviDpIOwg7yDyIPUg+CD7IP4hASEEIQchCiENIRAhEyEWIRkhHCEfISIhJSEnITAhMyE1IT4hQSFDIUwhTyFRIVohXSFfIWghayFtIXYheSF7IYQhhyGJIZIhlSGXIaAhoyGlIa4hsSGzIbwhvyHBIcohzSHPIdgh2yHdIeYh6SHrIfQh9yH5IgIiBSIHIhAiEyIVIh4iISIjIiwiLyIxIjoiPSI/IkwiTyJRIlMiXCKHIooijSKQIpMiliKZIpwinyKiIqUiqCKrIq4isSK0IrciuiK9IsAiwyLFIs4i0SLTItwi3yLhIuoi7SLvIvgi+yL9IwYjCSMLIxQjFyMZIyIjJSMnIzAjMyM1Iz4jQSNDI0wjTyNRI1ojXSNfI2gjayNtI3YjeSN7I4QjhyOJI5IjlSOXI6AjoyOlI64jsSOzI7wjvyPBI8ojzSPPI9gj2yPdI+oj7SPvI/Ej+iQlJCgkKyQuJDEkNCQ3JDokPSRAJEMkRiRJJEwkTyRSJFUkWCRbJF4kYSRjJGwkbyRxJHokfSR/JIgkiySNJJYkmSSbJKQkpySpJLIktSS3JMAkwyTFJM4k0STTJNwk3yThJOok7STvJPgk+yT9JQYlCSULJRQlFyUZJSIlJSUnJTAlMyU1JT4lQSVDJUwlTyVRJVolXSVfJWglayVtJXYleSV7JYgliyWNJY8lmCXDJcYlySXMJc8l0iXVJdgl2yXeJeEl5CXnJeol7SXwJfMl9iX5Jfwl/yYBJgomDSYPJhgmGyYdJiYmKSYrJjQmNyY5JkImRSZHJlAmUyZVJl4mYSZjJmwmbyZxJnomfSZ/JogmiyaNJpYmmSabJqQmpyapJrImtSa3JsAmwybFJs4m0SbTJtwm3ybhJuom7SbvJvgm+yb9JwYnCScLJxQnFycZJyYnKScrJy0nNidhJ2QnZydqJ20ncCdzJ3YneSd8J38ngieFJ4gniyeOJ5EnlCeXJ5onnSefJ6gnqyetJ7YnuSe7J8QnxyfJJ9In1SfXJ+An4yflJ+4n8SfzJ/wn/ygBKAooDSgPKBgoGygdKCYoKSgrKDQoNyg5KEIoRShHKFAoUyhVKF4oYShjKGwobyhxKHoofSh/KIgoiyiNKJYomSibKKQopyipKLIotSi3KMQoxyjJKMso1Cj/KQIpBSkIKQspDikRKRQpFykaKR0pICkjKSYpKSksKS8pMik1KTgpOyk9KUYpSSlLKVQpVylZKWIpZSlnKXApcyl1KX4pgSmDKYwpjymRKZopnSmfKagpqymtKbYpuSm7KcQpxynJKdIp1SnXKeAp4ynlKe4p8SnzKfwp/yoBKgoqDSoPKhgqGyodKiYqKSorKjQqNyo5KkIqRSpHKlAqUypVKmIqZSpnKmkqciqdKqAqoyqmKqkqrCqvKrIqtSq4KrsqvirBKsQqxyrKKs0q0CrTKtYq2SrbKuQq5yrpKvIq9Sr3KwArAysFKw4rESsTKxwrHyshKyorLSsvKzgrOys9K0YrSStLK1QrVytZK2IrZStnK3Arcyt1K34rgSuDK4wrjyuRK5ornSufK6grqyutK7YruSu7K8QrxyvJK9Ir1SvXK+Ar4yvlK+4r8SvzLAAsAywFLAcsECw7LD4sQSxELEcsSixNLFAsUyxWLFksXCxfLGIsZSxoLGssbixxLHQsdyx5LIIshSyHLJAskyyVLJ4soSyjLKwsryyxLLosvSy/LMgsyyzNLNYs2SzbLOQs5yzpLPIs9Sz3LQAtAy0FLQ4tES0TLRwtHy0hLSotLS0vLTgtOy09LUYtSS1LLVQtVy1ZLWItZS1nLXAtcy11LX4tgS2DLYwtjy2RLZ4toS2jLaUtri3ZLdwt3y3iLeUt6C3rLe4t8S30Lfct+i39LgAuAy4GLgkuDC4PLhIuFS4XLiAuIy4lLi4uMS4zLjwuPy5BLkouTS5PLlguWy5dLmYuaS5rLnQudy55LoIuhS6HLpAuky6VLp4uoS6jLqwury6xLrouvS6/Lsguyy7NLtYu2S7bLuQu5y7pLvIu9S73LwAvAy8FLw4vES8TLxwvHy8hLyovLS8vLzwvPy9BL0MvTC93L3ovfS+AL4Mvhi+JL4wvjy+SL5UvmC+bL54voS+kL6cvqi+tL7Avsy+1L74vwS/DL8wvzy/RL9ov3S/fL+gv6y/tL/Yv+S/7MAQwBzAJMBIwFTAXMCAwIzAlMC4wMTAzMDwwPzBBMEowTTBPMFgwWzBdMGYwaTBrMHQwdzB5MIIwhTCHMJAwkzCVMJ4woTCjMKwwrzCxMLowvTC/MMgwyzDNMNow3TDfMOEw6jEVMRgxGzEeMSExJDEnMSoxLTEwMTMxNjE5MTwxPzFCMUUxSDFLMU4xUTFTMVwxXzFhMWoxbTFvMXgxezF9MYYxiTGLMZQxlzGZMaIxpTGnMbAxszG1Mb4xwTHDMcwxzzHRMdox3THfMegx6zHtMfYx+TH7MgQyBzIJMhIyFTIXMiAyIzIlMi4yMTIzMjwyPzJBMkoyTTJPMlgyWzJdMmYyaTJrMngyezJ9Mn8yiDKzMrYyuTK8Mr8ywjLFMsgyyzLOMtEy1DLXMtoy3TLgMuMy5jLpMuwy7zLxMvoy/TL/MwgzCzMNMxYzGTMbMyQzJzMpMzIzNTM3M0AzQzNFM04zUTNTM1wzXzNhM2ozbTNvM3gzezN9M4YziTOLM5QzlzOZM6IzpTOnM7AzszO1M74zwTPDM8wzzzPRM9oz3TPfM+gz6zPtM/Yz+TP7NAQ0BzQJNBY0GTQbNB00JjRRNFQ0VzRaNF00YDRjNGY0aTRsNG80cjR1NHg0ezR+NIE0hDSHNIo0jTSPNJg0mzSdNKY0qTSrNLQ0tzS5NMI0xTTHNNA00zTVNN404TTjNOw07zTxNPo0/TT/NQg1CzUNNRY1GTUbNSQ1JzUpNTI1NTU3NUA1QzVFNU41UTVTNVw1XzVhNWo1bTVvNXg1ezV9NYY1iTWLNZQ1lzWZNaI1pTWnNbQ1tzW5Nbs1xDXvNfI19TX4Nfs1/jYBNgQ2BzYKNg02EDYTNhY2GTYcNh82IjYlNig2KzYtNjY2OTY7NkQ2RzZJNlI2VTZXNmA2YzZlNm42cTZzNnw2fzaBNoo2jTaPNpg2mzadNqY2qTarNrQ2tza5NsI2xTbHNtA20zbVNt424TbjNuw27zbxNvo2/Tb/Nwg3CzcNNxY3GTcbNyQ3JzcpNzI3NTc3N0A3QzdFN043ezd+N4E3gzeGN4k3jDePN5I3lTeYN5s3njehN6M3pjepN6s3rTewN7M3tje4N8U3yDfKN9M3/jgBOAM4BTgHOAk4CzgNOA84ETgTOBU4FzgZOBs4HTgfOCE4IzglOCc4KTgyODU4NzhEOEc4SThSOIM4hTiHOIo4jDiOOJA4kziWOJk4nDifOKE4pDinOKo4rTiwOLM4tji5OLw4vzjCOMQ4zTjQONI42zjeOOA46TjsOO44+zj+OQA5CTkwOTM5Njk5OTw5PzlBOUQ5RzlKOU05UDlTOVY5WTlcOV85YjllOWc5dDl3OXk5gjmpOaw5rjmxObQ5tzm6Ob05wDnDOcY5yTnMOc850jnVOdg52jndOd857DnvOfE5+jonOio6LTowOjM6NTo4Ojo6PTpAOkM6RTpIOks6TjpROlQ6VzpaOl06YDpjOmU6bjpxOnM6fDp/OoE6jjqROpM6lTqeOsE6xDrHOso6zTrQOtM61jrZOtw63zriOuU66DrrOu468TrzOvw6/zsBOwo7DTsPOxg7GzsdOyY7KTsrOzQ7Nzs5O0I7RTtHO1A7UztVO147YTtjO2w7bztxO3o7fTt/O4g7izuNO5Y7mTubO6Q7pzupO7I7tTu3O8A7wzvFO8470TvTO+A74zvlO+c78DwZPBw8HzwiPCU8KDwrPC48MTw0PDc8Ojw9PEA8QzxGPEk8TDxPPFI8VDxdPGA8YjxrPG48cDx5PHw8fjyHPIo8jDyVPJg8mjyjPKY8qDyxPLQ8tjy/PMI8xDzNPNA80jzbPN484DzpPOw87jz3PPo8/D0FPQg9Cj0TPRY9GD0hPSQ9Jj0vPTI9ND09PUA9Qj1LPU49UD1ZPVw9Xj1rPW49cD15Pag9qz2uPbE9tD23Pbo9vT3APcI9xT3IPcs9zj3RPdQ91z3aPd094D3jPeY96T3rPfQ99z35PgI+BT4HPhQ+Fz4ZPiI+TT5QPlM+VT5YPls+Xj5hPmQ+Zz5qPm0+cD5zPnU+eD57Pn4+gT6EPoc+iT6SPpU+lz6kPqc+qT6yPuM+5j7pPuw+7z7yPvU++D77Pv4/AT8EPwY/CT8MPw8/Ej8VPxg/Gz8dPyA/Iz8mPyg/MT80PzY/Pz9CP0Q/TT9QP1I/Xz9iP2Q/bT+SP5U/mD+bP54/oT+kP6c/qj+tP68/sj+1P7g/uz++P8A/wz/FP9I/1T/XP+BAC0AOQBFAFEAXQBpAHEAfQCJAJEAnQClALEAvQDJANUA4QDtAPkBBQENARUBOQFFAU0BcQF9AYUBuQHFAc0B8QKtArkCwQLNAtkC4QLtAvkDBQMRAx0DKQMxAzkDRQNRA10DaQN1A4EDiQORA5kDoQPFA9ED2QP9BAkEEQQ1BEEESQR9BIkEkQSZBL0FSQVVBWEFbQV5BYUFkQWdBakFtQXBBc0F2QXlBfEF/QYJBhEGNQZBBkkGbQZ5BoEGpQaxBrkG3QbpBvEHFQchBykHTQdZB2EHhQeRB5kHvQfJB9EH9QgBCAkILQg5CEEIZQhxCHkInQipCLEI1QjhCOkJDQkZCSEJRQlRCVkJfQmJCZEJxQnRCdkJ/QqpCrUKwQrNCtkK5QrxCvkLBQsRCx0LKQs1C0ELTQtZC2ELbQt5C4ELjQuVC7kLxQvNC/EL/QwFDCkMNQw9DGEMbQx1DKkMtQy9DOENfQ2JDZUNnQ2pDbUNwQ3NDdkN5Q3xDf0OCQ4VDiEOLQ45DkUOTQ5VDnkOhQ6NDrEOvQ7FDvkPBQ8NDzEP3Q/pD/UQARANEBkQJRAxED0QSRBVEGEQbRB5EIUQkRCdEKkQtRDBEM0Q1RD5EQURDRFBEUkRVRF5EZURnRHBEm0SeRKFEpESnRKpErUSwRLNEtkS5RLxEv0TCRMVEyETLRM5E0UTURNdE2UTiRORE9UT3RPlFAkUERRFFE0UWRR9FJEUmRTNFNUU4RUFFRkVIRVlFW0VdRWZFaEVxRXNFfEV+RYdFiUWaRZxFpUWnRbBFskW/RcFFxEXNRdRF1kXjReVF6EXxRfZF+EYJRgtGDUYWRhhGJUYnRipGM0Y6RjxGSUZLRk5GV0ZgRmJGc0Z1RndGgEarRq5GsUa0RrdGuka9RsBGw0bGRslGzEbPRtJG1UbYRttG3kbhRuRG50bpRvJG9EcBRwNHBkcPRxRHFkcnRylHK0c8Rz5HQEdNR09HUkdbR2BHYkdvR3FHdEd9R4RHhkePR5FHmkecR61Hr0e4R7pHx0fJR8xH1UfaR9xH6UfrR+5H90f8R/5IB0gJSBZIGEgbSCRIKUgrSDhIOkg9SEZIT0hRSF5IYEhjSGxIdUh3SIBIgkiPSJFIlEidSKRIpkizSLVIuEjBSMZIyEjRSNNI3EkJSQxJD0kSSRVJGEkbSR5JIUkkSSdJKkktSTBJM0k2STlJPEk/SUJJRUlISUpJU0lWSVlJW0lkSWtJbklxSXRJdkl/SYBJg0mMSZFJmkmbSZ1JpkmnSalJskm3SbpJvUm/SchJzUnQSdNJ1UneSeFJ5EnmSe9J9kn5SfxJ/0oBSgpKC0oNShZKG0oeSiFKI0osSjNKNko5SjxKPkpHSlBKU0pWSllKXEpeSmdKbEpvSnJKdEp9SoBKg0qFSpJKlEqXSqBKp0qpSrJKuUq8Sr9KwkrESs1K2krfSuxK9UsESwlLGEsqSy9LNAAAAAAAAAICAAAAAAAADfoAAAAAAAAAAAAAAAAAAEs2 \ No newline at end of file diff --git a/README.md b/README.md index 812826197f..10f28e5005 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-59C939.svg?style=flat)](https://github.com/Carthage/Carthage) [![Build Status](https://travis-ci.org/facebook/AsyncDisplayKit.svg)](https://travis-ci.org/facebook/AsyncDisplayKit) [![License](https://img.shields.io/cocoapods/l/AsyncDisplayKit.svg)](https://github.com/facebook/AsyncDisplayKit/blob/master/LICENSE) - + AsyncDisplayKit is an iOS framework that keeps even the most complex user interfaces smooth and responsive. It was originally built to make Facebook's @@ -18,14 +18,14 @@ interfaces smooth and responsive. It was originally built to make Facebook's [pop](https://github.com/facebook/pop)'s physics-based animations — but it's just as powerful with UIKit Dynamics and conventional app designs. -### Quick start +### Quick start ASDK is available on [CocoaPods](http://cocoapods.org). Add the following to your Podfile: ```ruby pod 'AsyncDisplayKit' ``` - + (ASDK can also be used as a regular static library: Copy the project to your codebase manually, adding `AsyncDisplayKit.xcodeproj` to your workspace. Add `libAsyncDisplayKit.a`, MapKit, AssetsLibrary, and Photos to the "Link Binary With @@ -46,7 +46,7 @@ CALayers: You can construct entire node hierarchies in parallel, or instantiate and size a single node on a background thread — for example, you could do -something like this in a UIViewController: +something like this in a UIViewController: ```objective-c dispatch_async(_backgroundQueue, ^{ diff --git a/build.sh b/build.sh index e6f1f0566f..02a2ddb4ab 100755 --- a/build.sh +++ b/build.sh @@ -36,46 +36,6 @@ if [ "$MODE" = "tests" ]; then exit 0 fi -if [ "$MODE" = "examples" ]; then - echo "Verifying that all AsyncDisplayKit examples compile." - - for example in examples/*/; do - echo "Building (examples) $example." - - if [ -f "${example}/Podfile" ]; then - echo "Using CocoaPods" - pod install --project-directory=$example - - set -o pipefail && xcodebuild \ - -workspace "${example}/Sample.xcworkspace" \ - -scheme Sample \ - -sdk "$SDK" \ - -destination "$PLATFORM" \ - -derivedDataPath ~/ \ - build | xcpretty $FORMATTER - elif [ -f "${example}/Cartfile" ]; then - echo "Using Carthage" - local_repo=`pwd` - current_branch=`git rev-parse --abbrev-ref HEAD` - cd $example - - echo "git \"file://${local_repo}\" \"${current_branch}\"" > "Cartfile" - carthage update --platform iOS - - set -o pipefail && xcodebuild \ - -project "Sample.xcodeproj" \ - -scheme Sample \ - -sdk "$SDK" \ - -destination "$PLATFORM" \ - build | xcpretty $FORMATTER - - cd ../.. - fi - done - trap - EXIT - exit 0 -fi - if [ "$MODE" = "examples-pt1" ]; then echo "Verifying that all AsyncDisplayKit examples compile."