From 3150e9da8f7636197c63cccba14efbccd5a3d6cf Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 15 May 2024 16:15:10 +0800 Subject: [PATCH] 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). --- ios/sdk/component/viewPager/HippyViewPager.m | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ios/sdk/component/viewPager/HippyViewPager.m b/ios/sdk/component/viewPager/HippyViewPager.m index 56d393bb266..17426465a56 100644 --- a/ios/sdk/component/viewPager/HippyViewPager.m +++ b/ios/sdk/component/viewPager/HippyViewPager.m @@ -44,6 +44,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 @@ -60,6 +64,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; @@ -153,6 +158,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]; @@ -335,6 +341,7 @@ - (NSUInteger)targetPageIndexFromTargetContentOffsetX:(CGFloat)targetContentOffs } if (_lastPageIndex != thePage) { _lastPageIndex = thePage; + _lastSelectedPageItem = self.viewPagerItems[thePage]; return thePage; } else { return _lastPageIndex; @@ -362,6 +369,23 @@ - (void)hippyBridgeDidFinishTransaction { BOOL isFrameEqual = CGRectEqualToRect(self.frame, self.previousFrame); BOOL isContentSizeEqual = CGSizeEqualToSize(self.contentSize, self.previousSize); + if (!isContentSizeEqual) { + // 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)); + } + self.lastPageIndex = updatedPageIndex; + self.needsResetPageIndex = YES; + } if (!isContentSizeEqual || !isFrameEqual) { self.previousFrame = self.frame; self.previousSize = self.contentSize;