diff --git a/AsyncDisplayKit.podspec b/AsyncDisplayKit.podspec index 2a132397..423b5ca0 100644 --- a/AsyncDisplayKit.podspec +++ b/AsyncDisplayKit.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |spec| spec.name = 'AsyncDisplayKit' - spec.version = '1.9.8' + spec.version = '1.9.7.2' spec.license = { :type => 'BSD' } spec.homepage = 'http://asyncdisplaykit.org' spec.authors = { 'Scott Goodson' => 'scottgoodson@gmail.com', 'Ryan Nystrom' => 'rnystrom@fb.com' } spec.summary = 'Smooth asynchronous user interfaces for iOS apps.' - spec.source = { :git => 'https://github.com/facebook/AsyncDisplayKit.git', :tag => '1.9.7.1' } + spec.source = { :git => 'https://github.com/facebook/AsyncDisplayKit.git', :tag => '1.9.7.2' } spec.documentation_url = 'http://asyncdisplaykit.org/appledoc/' diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index c9bc71fb..c9144df2 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -2451,6 +2451,141 @@ }; name = Release; }; + DB1020801CBCA2AD00FA6FE1 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + DB1020811CBCA2AD00FA6FE1 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ENABLE_CODE_COVERAGE = YES; + DSTROOT = /tmp/AsyncDisplayKit.dst; + GCC_INPUT_FILETYPE = automatic; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch"; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + OTHER_CFLAGS = "-Wall"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PUBLIC_HEADERS_FOLDER_PATH = "include/$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Profile; + }; + DB1020821CBCA2AD00FA6FE1 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_CODE_COVERAGE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "COCOAPODS=1", + "FB_REFERENCE_IMAGE_DIR=\"\\\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\\\"\"", + ); + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + INFOPLIST_FILE = "AsyncDisplayKitTests/AsyncDisplayKitTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AsyncDisplayKitTestHost.app/AsyncDisplayKitTestHost"; + WRAPPER_EXTENSION = xctest; + }; + name = Profile; + }; + DB1020831CBCA2AD00FA6FE1 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; + INFOPLIST_FILE = AsyncDisplayKitTestHost/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + DB1020841CBCA2AD00FA6FE1 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_CODE_COVERAGE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch"; + INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit-iOS/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = AsyncDisplayKit/module.modulemap; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = AsyncDisplayKit; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Profile; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -2459,6 +2594,7 @@ buildConfigurations = ( 057D02DF1AC0A66800C7AC3C /* Debug */, 057D02E01AC0A66800C7AC3C /* Release */, + DB1020831CBCA2AD00FA6FE1 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2468,6 +2604,7 @@ buildConfigurations = ( 058D09CD195D04C000B7D73C /* Debug */, 058D09CE195D04C000B7D73C /* Release */, + DB1020801CBCA2AD00FA6FE1 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2477,6 +2614,7 @@ buildConfigurations = ( 058D09D0195D04C000B7D73C /* Debug */, 058D09D1195D04C000B7D73C /* Release */, + DB1020811CBCA2AD00FA6FE1 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2486,6 +2624,7 @@ buildConfigurations = ( 058D09D3195D04C000B7D73C /* Debug */, 058D09D4195D04C000B7D73C /* Release */, + DB1020821CBCA2AD00FA6FE1 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2495,6 +2634,7 @@ buildConfigurations = ( B35061EE1B010EDF0018CF92 /* Debug */, B35061EF1B010EDF0018CF92 /* Release */, + DB1020841CBCA2AD00FA6FE1 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 64c28d58..0835dcfb 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -755,8 +755,13 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; } } -- (void)_scheduleCheckForBatchFetching +- (void)_scheduleCheckForBatchFetchingForNumberOfChanges:(NSUInteger)changes { + // Prevent fetching will continually trigger in a loop after reaching end of content and no new content was provided + if (changes == 0) { + return; + } + // Push this to the next runloop to be sure the scroll view has the right content size dispatch_async(dispatch_get_main_queue(), ^{ [self _checkForBatchFetching]; @@ -973,6 +978,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } + NSUInteger numberOfUpdateBlocks = _batchUpdateBlocks.count; ASPerformBlockWithoutAnimation(!animated, ^{ [_layoutFacilitator collectionViewWillPerformBatchUpdates]; [super performBatchUpdates:^{ @@ -980,7 +986,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; block(); } } completion:^(BOOL finished){ - [self _scheduleCheckForBatchFetching]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:numberOfUpdateBlocks]; if (completion) { completion(finished); } }]; }); @@ -1005,7 +1011,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:NO]; [UIView performWithoutAnimation:^{ [super insertItemsAtIndexPaths:indexPaths]; - [self _scheduleCheckForBatchFetching]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; }]; } } @@ -1026,7 +1032,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:NO]; [UIView performWithoutAnimation:^{ [super deleteItemsAtIndexPaths:indexPaths]; - [self _scheduleCheckForBatchFetching]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; }]; } } @@ -1047,7 +1053,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:NO]; [UIView performWithoutAnimation:^{ [super insertSections:indexSet]; - [self _scheduleCheckForBatchFetching]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; }]; } } @@ -1068,7 +1074,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:NO]; [UIView performWithoutAnimation:^{ [super deleteSections:indexSet]; - [self _scheduleCheckForBatchFetching]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; }]; } } diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index f7b24627..cf4c820f 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -2695,7 +2695,7 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; - (BOOL)supportsUpwardPropagation { - return YES; + return ASEnvironmentStatePropagationEnabled(); } ASEnvironmentLayoutOptionsForwarding diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 1a372084..67189322 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -730,8 +730,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; } } -- (void)_scheduleCheckForBatchFetching +- (void)_scheduleCheckForBatchFetchingForNumberOfChanges:(NSUInteger)changes { + // Prevent fetching will continually trigger in a loop after reaching end of content and no new content was provided + if (changes == 0) { + return; + } + // Push this to the next runloop to be sure the scroll view has the right content size dispatch_async(dispatch_get_main_queue(), ^{ [self _checkForBatchFetching]; @@ -904,7 +909,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ [super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions]; - [self _scheduleCheckForBatchFetching]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; }); if (_automaticallyAdjustsContentOffset) { @@ -924,7 +929,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ [super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions]; - [self _scheduleCheckForBatchFetching]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; }); if (_automaticallyAdjustsContentOffset) { @@ -945,7 +950,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ [super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions]; - [self _scheduleCheckForBatchFetching]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; }); } @@ -961,7 +966,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ [super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions]; - [self _scheduleCheckForBatchFetching]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; }); } diff --git a/AsyncDisplayKit/Details/_ASDisplayView.mm b/AsyncDisplayKit/Details/_ASDisplayView.mm index ebfd1dab..acd796ee 100644 --- a/AsyncDisplayKit/Details/_ASDisplayView.mm +++ b/AsyncDisplayKit/Details/_ASDisplayView.mm @@ -26,7 +26,7 @@ __unsafe_unretained ASDisplayNode *_node; // Though UIView has a .node property added via category, since we can add an ivar to a subclass, use that for performance. BOOL _inHitTest; BOOL _inPointInside; - NSMutableArray *_accessibleElements; + NSArray *_accessibleElements; } @synthesize asyncdisplaykit_node = _node; diff --git a/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm b/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm index 83592db3..b08296ce 100644 --- a/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm +++ b/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm @@ -11,9 +11,6 @@ #import "ASDisplayNodeExtras.h" #import "ASDisplayNode+FrameworkPrivate.h" -#import - - #pragma mark - UIAccessibilityElement @implementation UIAccessibilityElement (_ASDisplayView) @@ -34,10 +31,27 @@ #pragma mark - _ASDisplayView / UIAccessibilityContainer -@interface _ASDisplayView () { - NSMutableArray *_accessibleElements; +static NSArray *ASCollectUIAccessibilityElementsForNode(ASDisplayNode *viewNode, ASDisplayNode *subnode, id container) { + NSMutableArray *accessibleElements = [NSMutableArray array]; + ASDisplayNodePerformBlockOnEveryNodeBFS(subnode, ^(ASDisplayNode * _Nonnull currentNode) { + // For every subnode that is layer backed or it's supernode has shouldRasterizeDescendants enabled + // we have to create a UIAccessibilityElement as no view for this node exists + if (currentNode != viewNode && currentNode.isAccessibilityElement) { + UIAccessibilityElement *accessibilityElement = [UIAccessibilityElement accessibilityElementWithContainer:container node:currentNode]; + // As the node hierarchy is flattened it's necessary to convert the frame for each subnode in the tree to the + // coordinate system of the supernode + CGRect frame = [viewNode convertRect:currentNode.bounds fromNode:currentNode]; + accessibilityElement.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(frame, container); + [accessibleElements addObject:accessibilityElement]; + } + }); + + return [accessibleElements copy]; } +@interface _ASDisplayView () { + NSArray *_accessibleElements; +} @end @implementation _ASDisplayView (UIAccessibilityContainer) @@ -46,51 +60,41 @@ - (NSArray *)accessibleElements { - ASDisplayNode *selfNode = self.asyncdisplaykit_node; - if (selfNode == nil) { + ASDisplayNode *viewNode = self.asyncdisplaykit_node; + if (viewNode == nil) { return nil; } - _accessibleElements = [[NSMutableArray alloc] init]; - // Handle rasterize case - if (selfNode.shouldRasterizeDescendants) { - // If the node has shouldRasterizeDescendants enabled it's necessaty to go through the whole subnodes - // tree of the node in BFS fashion and create for all subnodes UIAccessibilityElement objects ourselves - // as the view hierarchy is flattened - ASDisplayNodePerformBlockOnEveryNodeBFS(selfNode, ^(ASDisplayNode * _Nonnull node) { - // For every subnode we have to create a UIAccessibilityElement as we cannot just add the view to the - // accessibleElements as for a subnode of a node with shouldRasterizeDescendants enabled no view exists - if (node != selfNode && node.isAccessibilityElement) { - UIAccessibilityElement *accessibilityElement = [UIAccessibilityElement accessibilityElementWithContainer:self node:node]; - // As the node hierarchy is flattened it's necessary to convert the frame for each subnode in the tree to the - // coordinate system of the node with shouldRasterizeDescendants enabled - CGRect frame = [selfNode convertRect:node.bounds fromNode:node]; - accessibilityElement.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(frame, self); - [_accessibleElements addObject:accessibilityElement]; - } - }); + if (viewNode.shouldRasterizeDescendants) { + _accessibleElements = ASCollectUIAccessibilityElementsForNode(viewNode, viewNode, self); return _accessibleElements; } // Handle not rasterize case - // Create UI accessiblity elements for each subnode that represent an elment within the accessibility container - for (ASDisplayNode *subnode in selfNode.subnodes) { + NSMutableArray *accessibleElements = [NSMutableArray array]; + + for (ASDisplayNode *subnode in viewNode.subnodes) { if (subnode.isAccessibilityElement) { + // An accessiblityElement can either be a UIView or a UIAccessibilityElement id accessiblityElement = nil; if (subnode.isLayerBacked) { - // The same comment for layer backed nodes is true as for subnodes within a shouldRasterizeDescendants node. - // See details above + // No view for layer backed nodes exist. It's necessary to create a UIAccessibilityElement that represents this node accessiblityElement = [UIAccessibilityElement accessibilityElementWithContainer:self node:subnode]; } else { accessiblityElement = subnode.view; } [accessiblityElement setAccessibilityFrame:UIAccessibilityConvertFrameToScreenCoordinates(subnode.frame, self)]; - [_accessibleElements addObject:accessiblityElement]; - } else if ([subnode accessibilityElementCount] > 0) { // Check if it's an UIAccessibilityContainer - [_accessibleElements addObject:subnode.view]; + [accessibleElements addObject:accessiblityElement]; + } else if (subnode.isLayerBacked) { + // Go down the hierarchy of the layer backed subnode and collect all of the UIAccessibilityElement + [accessibleElements addObjectsFromArray:ASCollectUIAccessibilityElementsForNode(viewNode, subnode, self)]; + } else if ([subnode accessibilityElementCount] > 0) { + // Add UIAccessibilityContainer + [accessibleElements addObject:subnode.view]; } } + _accessibleElements = [accessibleElements copy]; return _accessibleElements; } diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index a74dcd94..847b491e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -81,8 +81,14 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; id finalLayoutable = [child finalLayoutable]; if (finalLayoutable != child) { - // Layout options state of child needs to be copied to final layoutable, but don't override customized values. - ASEnvironmentStatePropagateUp(finalLayoutable, child.environmentState.layoutOptionsState); + if (ASEnvironmentStatePropagationEnabled()) { + ASEnvironmentStatePropagateUp(finalLayoutable, child.environmentState.layoutOptionsState); + } else { + // If state propagation is not enabled the layout options state needs to be copied manually + ASEnvironmentState finalLayoutableEnvironmentState = finalLayoutable.environmentState; + finalLayoutableEnvironmentState.layoutOptionsState = child.environmentState.layoutOptionsState; + finalLayoutable.environmentState = finalLayoutableEnvironmentState; + } return finalLayoutable; } } @@ -169,7 +175,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; // the specifications that are known to have more than one. - (BOOL)supportsUpwardPropagation { - return YES; + return ASEnvironmentStatePropagationEnabled(); } ASEnvironmentLayoutOptionsForwarding diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index ce2de20f..84ed0fe8 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -79,6 +79,9 @@ extern void ASLayoutableClearCurrentContext(); #define ASEnvironmentLayoutOptionsForwarding \ - (void)propagateUpLayoutOptionsState\ {\ + if (!ASEnvironmentStatePropagationEnabled()) {\ + return;\ + }\ id parent = [self parent];\ if ([parent supportsUpwardPropagation]) {\ ASEnvironmentStatePropagateUp(parent, _environmentState.layoutOptionsState);\ diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index abb2fedb..eaf0cc9f 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -721,7 +721,7 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo // Helper function with following logic: // - If the node is not loaded yet use the property from the pending state // - In case the node is loaded -// - Check if the node has a view and get the +// - Check if the node has a view and get the value from the view if loaded or from the pending state // - If view is not available, e.g. the node is layer backed return the property value #define _getAccessibilityFromViewOrProperty(nodeProperty, viewAndPendingViewStateProperty) __loaded(self) ? \ (_view ? _view.viewAndPendingViewStateProperty : nodeProperty )\ diff --git a/AsyncDisplayKit/Private/ASEnvironmentInternal.h b/AsyncDisplayKit/Private/ASEnvironmentInternal.h index e69bed23..9bd2c305 100644 --- a/AsyncDisplayKit/Private/ASEnvironmentInternal.h +++ b/AsyncDisplayKit/Private/ASEnvironmentInternal.h @@ -12,7 +12,7 @@ #pragma once -enum class ASEnvironmentStatePropagation { DOWN, UP }; +BOOL ASEnvironmentStatePropagationEnabled(); #pragma mark - Set and get extensible values for layout options @@ -33,6 +33,11 @@ void ASEnvironmentPerformBlockOnObjectAndChildren(id object, void void ASEnvironmentPerformBlockOnObjectAndParents(id object, void(^block)(id object)); +#pragma mark - + +enum class ASEnvironmentStatePropagation { DOWN, UP }; + + #pragma mark - Merging static const struct ASEnvironmentStateExtensions ASEnvironmentDefaultStateExtensions = {}; diff --git a/AsyncDisplayKit/Private/ASEnvironmentInternal.mm b/AsyncDisplayKit/Private/ASEnvironmentInternal.mm index 1c834946..83ea991b 100644 --- a/AsyncDisplayKit/Private/ASEnvironmentInternal.mm +++ b/AsyncDisplayKit/Private/ASEnvironmentInternal.mm @@ -17,6 +17,11 @@ #define AS_SUPPORT_PROPAGATION NO +BOOL ASEnvironmentStatePropagationEnabled() +{ + return AS_SUPPORT_PROPAGATION; +} + #pragma mark - Traversing an ASEnvironment Tree @@ -109,7 +114,7 @@ ASEnvironmentState ASEnvironmentMergeObjectAndState(ASEnvironmentState environme // Merge object and layout options state LOG(@"Merge object and state: %@ - ASEnvironmentLayoutOptionsState", object); - if (!AS_SUPPORT_PROPAGATION) { + if (!ASEnvironmentStatePropagationEnabled()) { return environmentState; }