Skip to content

Commit

Permalink
feat(ios): update ViewPager's pageIndex automatically after data chan…
Browse files Browse the repository at this point in the history
…ges (#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'
  • Loading branch information
wwwcg authored May 16, 2024
1 parent 4249f3a commit 4769911
Showing 1 changed file with 32 additions and 5 deletions.
37 changes: 32 additions & 5 deletions renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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];
}

Expand All @@ -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];
Expand Down Expand Up @@ -363,6 +388,7 @@ - (NSUInteger)targetPageIndexFromTargetContentOffsetX:(CGFloat)targetContentOffs
}
if (_lastPageIndex != thePage) {
_lastPageIndex = thePage;
_lastSelectedPageItem = self.viewPagerItems[thePage];
return thePage;
} else {
return _lastPageIndex;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 4769911

Please sign in to comment.