From 4769911d34c54ca1b7d45b9fd46fdab6a1e38353 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 16 May 2024 21:41:45 +0800 Subject: [PATCH] feat(ios): update ViewPager's pageIndex automatically after data changes (#3857) * feat(ios): pageIndex of ViewPager auto update after data changes Also fixed the issue of missing onPageSelected callbacks upon first entry; The logic for automatic updates keep same with Android, as follows 1. If the previous item only changes its location, update the current location and keep the current item displayed. 2. If the previous item does not exist, do not adjust the position, but keep the current position in the valid range (that is, 0 ~ count-1). * fix(ios): auto-update of page index may lag by one frame in view pager this is a minor fix for feat 'pageIndex of ViewPager auto update after data changes' --- .../component/viewPager/HippyViewPager.mm | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm b/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm index 4ad6bc61a20..a19dc6f4d6f 100644 --- a/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm +++ b/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm @@ -46,6 +46,10 @@ @interface HippyViewPager () @property (nonatomic, assign) CGFloat previousStopOffset; @property (nonatomic, assign) NSUInteger lastPageSelectedCallbackIndex; +/// A weak property used to record the currently displayed item, +/// which is used for updating the page index when the data changes. +@property (nonatomic, weak) UIView *lastSelectedPageItem; + @end @implementation HippyViewPager @@ -62,6 +66,7 @@ - (instancetype)initWithFrame:(CGRect)frame { self.previousFrame = CGRectZero; self.scrollViewListener = [NSHashTable weakObjectsHashTable]; self.lastPageIndex = NSUIntegerMax; + self.lastPageSelectedCallbackIndex = NSUIntegerMax; self.targetContentOffsetX = CGFLOAT_MAX; if (@available(iOS 11.0, *)) { self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; @@ -149,6 +154,25 @@ - (void)hippySetFrame:(CGRect)frame { - (void)didUpdateHippySubviews { [super didUpdateHippySubviews]; self.needsLayoutItems = YES; + + // Update the latest page index based on the currently displayed item (aka lastSelectedPageItem). + // Keep the same logic as android: + // 1. If the previous item only changes its location, + // update the current location and keep the current item displayed. + // 2. If the previous item does not exist, do not adjust the position, + // but keep the current position in the valid range (that is, 0 ~ count-1). + UIView *previousSelectedItem = self.lastSelectedPageItem; + NSUInteger updatedPageIndex; + if (previousSelectedItem) { + updatedPageIndex = [self.viewPagerItems indexOfObject:previousSelectedItem]; + } else { + updatedPageIndex = MAX(0, MIN(self.lastPageIndex, self.viewPagerItems.count - 1)); + } + if (self.lastPageIndex != updatedPageIndex) { + self.lastPageIndex = updatedPageIndex; + self.needsResetPageIndex = YES; + } + [self setNeedsLayout]; } @@ -161,6 +185,7 @@ - (void)setPage:(NSInteger)pageNumber animated:(BOOL)animated { _lastPageIndex = pageNumber; UIView *theItem = self.viewPagerItems[pageNumber]; + self.lastSelectedPageItem = theItem; self.targetContentOffsetX = CGRectGetMinX(theItem.frame); [self setContentOffset:theItem.frame.origin animated:animated]; [self invokePageSelected:pageNumber]; @@ -363,6 +388,7 @@ - (NSUInteger)targetPageIndexFromTargetContentOffsetX:(CGFloat)targetContentOffs } if (_lastPageIndex != thePage) { _lastPageIndex = thePage; + _lastSelectedPageItem = self.viewPagerItems[thePage]; return thePage; } else { return _lastPageIndex; @@ -389,7 +415,6 @@ - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated { - (void)hippyBridgeDidFinishTransaction { BOOL isFrameEqual = CGRectEqualToRect(self.frame, self.previousFrame); BOOL isContentSizeEqual = CGSizeEqualToSize(self.contentSize, self.previousSize); - if (!isContentSizeEqual || !isFrameEqual) { self.previousFrame = self.frame; self.previousSize = self.contentSize; @@ -425,10 +450,12 @@ - (void)layoutSubviews { return; } - self.contentSize = CGSizeMake( - lastViewPagerItem.frame.origin.x + lastViewPagerItem.frame.size.width, - lastViewPagerItem.frame.origin.y + lastViewPagerItem.frame.size.height - ); + CGSize updatedSize = CGSizeMake(lastViewPagerItem.frame.origin.x + lastViewPagerItem.frame.size.width, + lastViewPagerItem.frame.origin.y + lastViewPagerItem.frame.size.height); + if (!CGSizeEqualToSize(self.contentSize, updatedSize)) { + self.contentSize = updatedSize; + } + if (!_didFirstTimeLayout) { [self setPage:self.initialPage animated:NO]; _didFirstTimeLayout = YES;