From 929d118c962dcf595386daf5c91ce30e1f8e577d Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 16 Jul 2017 16:59:28 -0700 Subject: [PATCH 1/3] fix SIMULATE_WEB_RESPONSE not imported #449 --- examples/ASCollectionView/Sample/ViewController.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index d00ea60df..4e6a04e97 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -16,7 +16,7 @@ // #import "ViewController.h" - +#import "AppDelegate.h" #import #import "SupplementaryNode.h" #import "ItemNode.h" @@ -70,8 +70,8 @@ - (void)viewDidLoad { NSLog(@"ViewController is not nil"); strongSelf->_data = [[NSArray alloc] init]; - [strongSelf->_collectionView performBatchUpdates:^{ - [strongSelf->_collectionView insertSections:[[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, 100)]]; + [strongSelf->_collectionNode performBatchUpdates:^{ + [strongSelf->_collectionNode insertSections:[[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, 100)]]; } completion:nil]; NSLog(@"ViewController finished updating collectionView"); } @@ -81,7 +81,7 @@ - (void)viewDidLoad }; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), mockWebService); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.navigationController popViewControllerAnimated:YES]; }); #endif From 329f35ff56aa6605ba9157174568cfd18a7a5373 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Mon, 5 Feb 2018 14:10:21 -0800 Subject: [PATCH 2/3] Fix to make rangeMode update in right time --- Source/ASCollectionView.mm | 1 + Source/ASTableView.mm | 1 + Source/Details/ASRangeController.h | 5 +++++ Source/Details/ASRangeController.mm | 14 ++++++++++---- Tests/ASCollectionViewTests.mm | 1 + 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Source/ASCollectionView.mm b/Source/ASCollectionView.mm index 0c706faa4..7c20dbddf 100644 --- a/Source/ASCollectionView.mm +++ b/Source/ASCollectionView.mm @@ -1511,6 +1511,7 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView // If a scroll happenes the current range mode needs to go to full ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController]; if (ASInterfaceStateIncludesVisible(interfaceState)) { + _rangeController.contentOffsetHasChanged = YES; [_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull]; [self _checkForBatchFetching]; } diff --git a/Source/ASTableView.mm b/Source/ASTableView.mm index 4606726f2..7bcd0fd5c 100644 --- a/Source/ASTableView.mm +++ b/Source/ASTableView.mm @@ -1218,6 +1218,7 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView // If a scroll happenes the current range mode needs to go to full ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController]; if (ASInterfaceStateIncludesVisible(interfaceState)) { + _rangeController.contentOffsetHasChanged = YES; [_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull]; [self _checkForBatchFetching]; } diff --git a/Source/Details/ASRangeController.h b/Source/Details/ASRangeController.h index 46a5cbf69..152c4e8b5 100644 --- a/Source/Details/ASRangeController.h +++ b/Source/Details/ASRangeController.h @@ -100,6 +100,11 @@ AS_SUBCLASSING_RESTRICTED */ @property (nonatomic, weak) id delegate; +/** + * Property that indicates whether the scroll view for this range controller has ever changed its contentOffset. + */ +@property (nonatomic, assign) BOOL contentOffsetHasChanged; + @end diff --git a/Source/Details/ASRangeController.mm b/Source/Details/ASRangeController.mm index 818fb3a1d..4d10838b7 100644 --- a/Source/Details/ASRangeController.mm +++ b/Source/Details/ASRangeController.mm @@ -45,6 +45,7 @@ @interface ASRangeController () NSSet *_allPreviousIndexPaths; NSHashTable *_visibleNodes; ASLayoutRangeMode _currentRangeMode; + BOOL _contentOffsetHasChanged; BOOL _preserveCurrentRangeMode; BOOL _didRegisterForNodeDisplayNotifications; CFTimeInterval _pendingDisplayNodesTimestamp; @@ -77,6 +78,7 @@ - (instancetype)init _rangeIsValid = YES; _currentRangeMode = ASLayoutRangeModeUnspecified; + _contentOffsetHasChanged = NO; _preserveCurrentRangeMode = NO; _previousScrollDirection = ASScrollDirectionDown | ASScrollDirectionRight; @@ -237,9 +239,13 @@ - (void)_updateVisibleNodeIndexPaths ASInterfaceState selfInterfaceState = [self interfaceState]; ASLayoutRangeMode rangeMode = _currentRangeMode; - // If the range mode is explicitly set via updateCurrentRangeWithMode: it will last in that mode until the - // range controller becomes visible again or explicitly changes the range mode again - if ((!_preserveCurrentRangeMode && ASInterfaceStateIncludesVisible(selfInterfaceState)) || [[self class] isFirstRangeUpdateForRangeMode:rangeMode]) { + BOOL updateRangeMode = !_preserveCurrentRangeMode && _contentOffsetHasChanged; + + // If we've never scrolled before, we never update the range mode, so it doesn't jump into Full too early. + // This can happen if we have multiple, noisy updates occurring from application code before the user has engaged. + // If the range mode is explicitly set via updateCurrentRangeWithMode:, we'll preserve that for at least one update cycle. + // Once the user has scrolled and the range is visible, we'll always resume managing the range mode automatically. + if ((updateRangeMode && ASInterfaceStateIncludesVisible(selfInterfaceState)) || [[self class] isFirstRangeUpdateForRangeMode:rangeMode]) { rangeMode = [ASRangeController rangeModeForInterfaceState:selfInterfaceState currentRangeMode:_currentRangeMode]; } @@ -412,7 +418,7 @@ - (void)_updateVisibleNodeIndexPaths // NSLog(@"custom: %@", visibleNodePathsSet); // } [modifiedIndexPaths sortUsingSelector:@selector(compare:)]; - NSLog(@"Range update complete; modifiedIndexPaths: %@", [self descriptionWithIndexPaths:modifiedIndexPaths]); + NSLog(@"Range update complete; modifiedIndexPaths: %@, rangeMode: %d", [self descriptionWithIndexPaths:modifiedIndexPaths], rangeMode); #endif ASSignpostEnd(ASSignpostRangeControllerUpdate); diff --git a/Tests/ASCollectionViewTests.mm b/Tests/ASCollectionViewTests.mm index b223e246f..11ab711d6 100644 --- a/Tests/ASCollectionViewTests.mm +++ b/Tests/ASCollectionViewTests.mm @@ -1071,6 +1071,7 @@ - (void)testInitialRangeBounds for (NSInteger i = 0; i < c; i++) { NSIndexPath *ip = [NSIndexPath indexPathForItem:i inSection:s]; ASCellNode *node = [cn nodeForItemAtIndexPath:ip]; + [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]]; if (node.inPreloadState) { CGRect frame = [cn.view layoutAttributesForItemAtIndexPath:ip].frame; r = CGRectUnion(r, frame); From f1c2aae2bd8dc0067966f91f3fd1502028b8ae23 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Thu, 22 Mar 2018 23:01:08 -0700 Subject: [PATCH 3/3] This is for cases when CollectionNode is created in cell of another collectionNode, the interfaceState(say didEnterVisible) is not called as expected. It's because interfaceCoalescing alter the time line and cause RangeController update before actual interface applied. --- Source/ASDisplayNodeExtras.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/ASDisplayNodeExtras.mm b/Source/ASDisplayNodeExtras.mm index 2839e1a6c..385343ae6 100644 --- a/Source/ASDisplayNodeExtras.mm +++ b/Source/ASDisplayNodeExtras.mm @@ -70,7 +70,7 @@ extern ASInterfaceState ASInterfaceStateForDisplayNode(ASDisplayNode *displayNod // Directly clear the visible bit if we are not in a window. This means that the interface state is, // if not already, about to be set to invisible as it is not possible for an element to be visible // while outside of a window. - ASInterfaceState interfaceState = displayNode.interfaceState; + ASInterfaceState interfaceState = displayNode.pendingInterfaceState; return (window == nil ? (interfaceState &= (~ASInterfaceStateVisible)) : interfaceState); } else { // For not range managed nodes we might be on our own to try to guess if we're visible.