diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fca7b81e..312e3b248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ - [ASPINRemoteImageManager] Add a new API for setting a preconfigured PINRemoteImageManager. [Ernest Ma](https://github.com/ernestmama) [#1124](https://github.com/TextureGroup/Texture/pull/1124) - Small optimization to the layout spec & yoga layout systems by eliminating array copies. [Adlai Holler](https://github.com/Adlai-Holler) - Optimize layout process by removing `ASRectMap`. [Adlai Holler](https://github.com/Adlai-Holler) +- Remove necessity to use view to access rangeController in ASTableNode, ASCollectionNode. [Michael Schneider](https://github.com/maicki) ## 2.7 - Fix pager node for interface coalescing. [Max Wang](https://github.com/wsdwsd0829) [#877](https://github.com/TextureGroup/Texture/pull/877) diff --git a/Source/ASCollectionNode.mm b/Source/ASCollectionNode.mm index 82388c85a..bf854b046 100644 --- a/Source/ASCollectionNode.mm +++ b/Source/ASCollectionNode.mm @@ -30,9 +30,12 @@ #pragma mark - _ASCollectionPendingState -@interface _ASCollectionPendingState : NSObject -@property (weak, nonatomic) id delegate; -@property (weak, nonatomic) id dataSource; +@interface _ASCollectionPendingState : NSObject { +@public + std::vector> _tuningParameters; +} +@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id dataSource; @property (nonatomic) UICollectionViewLayout *collectionViewLayout; @property (nonatomic) ASLayoutRangeMode rangeMode; @property (nonatomic) BOOL allowsSelection; // default is YES @@ -40,7 +43,7 @@ @interface _ASCollectionPendingState : NSObject @property (nonatomic) BOOL inverted; //default is NO @property (nonatomic) BOOL usesSynchronousDataLoading; @property (nonatomic) CGFloat leadingScreensForBatching; -@property (weak, nonatomic) id layoutInspector; +@property (nonatomic, weak) id layoutInspector; @property (nonatomic) BOOL alwaysBounceVertical; @property (nonatomic) BOOL alwaysBounceHorizontal; @property (nonatomic) UIEdgeInsets contentInset; @@ -52,11 +55,14 @@ @interface _ASCollectionPendingState : NSObject @implementation _ASCollectionPendingState +#pragma mark Lifecycle + - (instancetype)init { self = [super init]; if (self) { _rangeMode = ASLayoutRangeModeUnspecified; + _tuningParameters = std::vector> (ASLayoutRangeModeCount, std::vector (ASLayoutRangeTypeCount, ASRangeTuningParametersZero)); _allowsSelection = YES; _allowsMultipleSelection = NO; _inverted = NO; @@ -66,23 +72,8 @@ - (instancetype)init } return self; } -@end - -// TODO: Add support for tuning parameters in the pending state -#if 0 // This is not used yet, but will provide a way to avoid creating the view to set range values. -@implementation _ASCollectionPendingState { - std::vector> _tuningParameters; -} -- (instancetype)init -{ - self = [super init]; - if (self) { - _tuningParameters = std::vector> (ASLayoutRangeModeCount, std::vector (ASLayoutRangeTypeCount)); - _rangeMode = ASLayoutRangeModeUnspecified; - } - return self; -} +#pragma mark Tuning Parameters - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType { @@ -107,7 +98,6 @@ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMo } @end -#endif #pragma mark - ASCollectionNode @@ -118,6 +108,7 @@ @interface ASCollectionNode () id _batchFetchingDelegate; } @property (nonatomic) _ASCollectionPendingState *pendingState; +@property (nonatomic, weak) ASRangeController *rangeController; @end @implementation ASCollectionNode @@ -188,6 +179,8 @@ - (void)didLoad ASCollectionView *view = self.view; view.collectionNode = self; + + _rangeController = view.rangeController; if (_pendingState) { _ASCollectionPendingState *pendingState = _pendingState; @@ -219,9 +212,24 @@ - (void)didLoad if (!CGPointEqualToPoint(contentOffset, CGPointZero)) { [view setContentOffset:contentOffset animated:pendingState.animatesContentOffset]; } - + + let tuningParametersVector = pendingState->_tuningParameters; + let tuningParametersVectorSize = tuningParametersVector.size(); + for (NSInteger rangeMode = 0; rangeMode < tuningParametersVectorSize; rangeMode++) { + let tuningparametersRangeModeVector = tuningParametersVector[rangeMode]; + let tuningParametersVectorRangeModeSize = tuningparametersRangeModeVector.size(); + for (NSInteger rangeType = 0; rangeType < tuningParametersVectorRangeModeSize; rangeType++) { + ASRangeTuningParameters tuningParameters = tuningparametersRangeModeVector[rangeType]; + if (!ASRangeTuningParametersEqualToRangeTuningParameters(tuningParameters, ASRangeTuningParametersZero)) { + [_rangeController setTuningParameters:tuningParameters + forRangeMode:(ASLayoutRangeMode)rangeMode + rangeType:(ASLayoutRangeType)rangeType]; + } + } + } + if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) { - [view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; + [_rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; } // Don't need to set collectionViewLayout to the view as the layout was already used to init the view in view block. @@ -253,7 +261,7 @@ - (void)didEnterPreloadState // We can get rid of this call later when ASDataController, ASRangeController and ASCollectionLayout can operate without the view. // TODO (ASCL) If this node supports async layout, kick off the initial data load without allocating the view if (ASHierarchyStateIncludesRangeManaged(self.hierarchyState) && CGRectEqualToRect(self.bounds, CGRectZero) == NO) { - [[self view] layoutIfNeeded]; + [self.view layoutIfNeeded]; } } @@ -285,12 +293,6 @@ - (ASDataController *)dataController return self.view.dataController; } -// TODO: Implement this without the view. -- (ASRangeController *)rangeController -{ - return self.view.rangeController; -} - - (_ASCollectionPendingState *)pendingState { if (!_pendingState && ![self isNodeLoaded]) { @@ -647,22 +649,30 @@ - (void)setUsesSynchronousDataLoading:(BOOL)usesSynchronousDataLoading - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType { - return [self.rangeController tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; + return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType { - [self.rangeController setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; + [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; } - (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - return [self.rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + if ([self pendingState]) { + return [_pendingState tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + } else { + return [self.rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + } } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - return [self.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + if ([self pendingState]) { + [_pendingState setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + } else { + return [self.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + } } #pragma mark - Selection diff --git a/Source/ASTableNode.h b/Source/ASTableNode.h index 2476c309c..2cd2394c8 100644 --- a/Source/ASTableNode.h +++ b/Source/ASTableNode.h @@ -31,8 +31,8 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly) ASTableView *view; // These properties can be set without triggering the view to be created, so it's fine to set them in -init. -@property (nullable, weak, nonatomic) id delegate; -@property (nullable, weak, nonatomic) id dataSource; +@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id dataSource; /** * The number of screens left to scroll before the delegate -tableNode:beginBatchFetchingWithContext: is called. diff --git a/Source/ASTableNode.mm b/Source/ASTableNode.mm index 12d3f972f..cbcb54f77 100644 --- a/Source/ASTableNode.mm +++ b/Source/ASTableNode.mm @@ -25,9 +25,12 @@ #pragma mark - _ASTablePendingState -@interface _ASTablePendingState : NSObject -@property (weak, nonatomic) id delegate; -@property (weak, nonatomic) id dataSource; +@interface _ASTablePendingState : NSObject { +@public + std::vector> _tuningParameters; +} +@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id dataSource; @property (nonatomic) ASLayoutRangeMode rangeMode; @property (nonatomic) BOOL allowsSelection; @property (nonatomic) BOOL allowsSelectionDuringEditing; @@ -39,14 +42,19 @@ @interface _ASTablePendingState : NSObject @property (nonatomic) CGPoint contentOffset; @property (nonatomic) BOOL animatesContentOffset; @property (nonatomic) BOOL automaticallyAdjustsContentOffset; + @end @implementation _ASTablePendingState + +#pragma mark - Lifecycle + - (instancetype)init { self = [super init]; if (self) { _rangeMode = ASLayoutRangeModeUnspecified; + _tuningParameters = std::vector> (ASLayoutRangeModeCount, std::vector (ASLayoutRangeTypeCount, ASRangeTuningParametersZero)); _allowsSelection = YES; _allowsSelectionDuringEditing = NO; _allowsMultipleSelection = NO; @@ -61,6 +69,30 @@ - (instancetype)init return self; } +#pragma mark Tuning Parameters + +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType +{ + return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType +{ + return [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Requesting a range that is OOB for the configured tuning parameters"); + return _tuningParameters[rangeMode][rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Setting a range that is OOB for the configured tuning parameters"); + _tuningParameters[rangeMode][rangeType] = tuningParameters; +} + @end #pragma mark - ASTableView @@ -72,6 +104,7 @@ @interface ASTableNode () } @property (nonatomic) _ASTablePendingState *pendingState; +@property (nonatomic, weak) ASRangeController *rangeController; @end @implementation ASTableNode @@ -116,9 +149,11 @@ - (void)didLoad ASTableView *view = self.view; view.tableNode = self; + + _rangeController = view.rangeController; if (_pendingState) { - _ASTablePendingState *pendingState = _pendingState; + _ASTablePendingState *pendingState = _pendingState; view.asyncDelegate = pendingState.delegate; view.asyncDataSource = pendingState.dataSource; view.inverted = pendingState.inverted; @@ -129,8 +164,23 @@ - (void)didLoad view.contentInset = pendingState.contentInset; self.pendingState = nil; + let tuningParametersVector = pendingState->_tuningParameters; + let tuningParametersVectorSize = tuningParametersVector.size(); + for (NSInteger rangeMode = 0; rangeMode < tuningParametersVectorSize; rangeMode++) { + let tuningparametersRangeModeVector = tuningParametersVector[rangeMode]; + let tuningParametersVectorRangeModeSize = tuningparametersRangeModeVector.size(); + for (NSInteger rangeType = 0; rangeType < tuningParametersVectorRangeModeSize; rangeType++) { + ASRangeTuningParameters tuningParameters = tuningparametersRangeModeVector[rangeType]; + if (!ASRangeTuningParametersEqualToRangeTuningParameters(tuningParameters, ASRangeTuningParametersZero)) { + [_rangeController setTuningParameters:tuningParameters + forRangeMode:(ASLayoutRangeMode)rangeMode + rangeType:(ASLayoutRangeType)rangeType]; + } + } + } + if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) { - [view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; + [_rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; } [view setContentOffset:pendingState.contentOffset animated:pendingState.animatesContentOffset]; @@ -159,7 +209,7 @@ - (void)didEnterPreloadState [super didEnterPreloadState]; // Intentionally allocate the view here and trigger a layout pass on it, which in turn will trigger the intial data load. // We can get rid of this call later when ASDataController, ASRangeController and ASCollectionLayout can operate without the view. - [[self view] layoutIfNeeded]; + [self.view layoutIfNeeded]; } #if ASRangeControllerLoggingEnabled @@ -190,12 +240,6 @@ - (ASDataController *)dataController return self.view.dataController; } -// TODO: Implement this without the view. -- (ASRangeController *)rangeController -{ - return self.view.rangeController; -} - - (_ASTablePendingState *)pendingState { if (!_pendingState && ![self isNodeLoaded]) { @@ -476,22 +520,30 @@ - (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType { - return [self.rangeController tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; + return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType { - [self.rangeController setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; + [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; } - (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - return [self.rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + if ([self pendingState]) { + return [_pendingState tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + } else { + return [self.rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + } } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - return [self.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + if ([self pendingState]) { + [_pendingState setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + } else { + return [self.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + } } #pragma mark - Selection diff --git a/Tests/ASCollectionViewTests.mm b/Tests/ASCollectionViewTests.mm index 372e258f2..92fb349e8 100644 --- a/Tests/ASCollectionViewTests.mm +++ b/Tests/ASCollectionViewTests.mm @@ -369,6 +369,17 @@ - (void)testTuningParameters XCTAssertTrue(ASRangeTuningParametersEqualToRangeTuningParameters(preloadParams, [collectionView tuningParametersForRangeType:ASLayoutRangeTypePreload])); } +// Informations to test: https://github.com/TextureGroup/Texture/issues/1094 +- (void)testThatCollectionNodeCanHandleNilRangeController +{ + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionNode *collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout]; + [collectionNode recursivelySetInterfaceState:ASInterfaceStateDisplay]; + [collectionNode setHierarchyState:ASHierarchyStateRangeManaged]; + [collectionNode recursivelySetInterfaceState:ASInterfaceStateNone]; + ASCATransactionQueueWait(nil); +} + /** * This may seem silly, but we had issues where the runtime sometimes wouldn't correctly report * conformances declared on categories.