Skip to content

Commit 2e8ac2c

Browse files
authored
fix(both): unify setPage behavior across platforms (#335)
* fix: unify scroll behavior across platforms * add methods to readme
1 parent b59dc90 commit 2e8ac2c

File tree

3 files changed

+61
-52
lines changed

3 files changed

+61
-52
lines changed

README.md

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ This component allows the user to swipe left and right through pages of data. Un
1616

1717
## Versions
1818

19-
| 4.x | 5.x
20-
|------------- | -------------|
21-
| iOS | iOS support |
22-
| ViewPager1 | ViewPager2 |
23-
19+
| 4.x | 5.x |
20+
| ---------- | ----------- |
21+
| iOS | iOS support |
22+
| ViewPager1 | ViewPager2 |
2423

2524
## Migration
2625

27-
`"@react-native-community/viewpager"` library has been changed to `react-native-pager-view`. Here you can find more information, how to migrate pager view to the latest [version](https://github.com/callstack/react-native-pager-view/blob/master/MIGRATION.md)
26+
`"@react-native-community/viewpager"` library has been changed to `react-native-pager-view`. Here you can find more information, how to migrate pager view to the latest [version](https://github.com/callstack/react-native-pager-view/blob/master/MIGRATION.md)
27+
2828
## Getting started
2929

3030
`yarn add react-native-pager-view`
@@ -98,7 +98,7 @@ protected List<ReactPackage> getPackages() {
9898

9999
```js
100100
import React from 'react';
101-
import {StyleSheet, View, Text} from 'react-native';
101+
import { StyleSheet, View, Text } from 'react-native';
102102
import PagerView from 'react-native-pager-view';
103103

104104
const MyPager = () => {
@@ -121,8 +121,8 @@ const styles = StyleSheet.create({
121121
});
122122
```
123123

124-
**Attention:** Note that you can only use `View` components as children of `PagerView` (cf. folder */example*)
125-
. For Android if `View` has own children, set prop `collapsable` to false <https://reactnative.dev/img/view#collapsable>, otherwise react-native might remove those children views and and it's children will be rendered as separate pages
124+
**Attention:** Note that you can only use `View` components as children of `PagerView` (cf. folder _/example_)
125+
. For Android if `View` has own children, set prop `collapsable` to false <https://reactnative.dev/img/view#collapsable>, otherwise react-native might remove those children views and and it's children will be rendered as separate pages
126126

127127
## Advanced usage
128128

@@ -131,21 +131,27 @@ For advanced usage please take a look into our [example project](https://github.
131131

132132
## API
133133

134-
|Prop|Description|Platform|
135-
|-|:-----:|:---:|
136-
|`initialPage`|Index of initial page that should be selected|both
137-
|`scrollEnabled: boolean`|Should pager view scroll, when scroll enabled|both
138-
|`onPageScroll: (e: PageScrollEvent) => void`|Executed when transitioning between pages (ether because the animation for the requested page has changed or when the user is swiping/dragging between pages)|both
139-
|`onPageScrollStateChanged: (e: PageScrollStateChangedEvent) => void`|Function called when the page scrolling state has changed|both
140-
|`onPageSelected: (e: PageSelectedEvent) => void`|This callback will be called once the ViewPager finishes navigating to the selected page|both
141-
|`pageMargin: number`|Blank space to be shown between pages|both
142-
|`keyboardDismissMode: ('none' / 'on-drag')`| Determines whether the keyboard gets dismissed in response to a drag|both
143-
|`orientation: Orientation`|Set `horizontal` or `vertical` scrolling orientation (it does **not** work dynamically)|both
144-
|`transitionStyle: TransitionStyle`|Use `scroll` or `curl` to change transition style (it does **not** work dynamically)|iOS
145-
|`showPageIndicator: boolean`|Shows the dots indicator at the bottom of the view|iOS
146-
|`overScrollMode: OverScollMode`|Used to override default value of overScroll mode. Can be `auto`, `always` or `never`. Defaults to `auto`|Android
147-
|`offscreenPageLimit: number`|Set the number of pages that should be retained to either side of the currently visible page(s). Pages beyond this limit will be recreated from the adapter when needed. Defaults to RecyclerView's caching strategy. The given value must either be larger than 0.|Android
148-
|`overdrag: boolean`|Allows for overscrolling after reaching the end or very beginning or pages|iOS
134+
| Prop | Description | Platform |
135+
| -------------------------------------------------------------------- | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: |
136+
| `initialPage` | Index of initial page that should be selected | both |
137+
| `scrollEnabled: boolean` | Should pager view scroll, when scroll enabled | both |
138+
| `onPageScroll: (e: PageScrollEvent) => void` | Executed when transitioning between pages (ether because the animation for the requested page has changed or when the user is swiping/dragging between pages) | both |
139+
| `onPageScrollStateChanged: (e: PageScrollStateChangedEvent) => void` | Function called when the page scrolling state has changed | both |
140+
| `onPageSelected: (e: PageSelectedEvent) => void` | This callback will be called once the ViewPager finishes navigating to the selected page | both |
141+
| `pageMargin: number` | Blank space to be shown between pages | both |
142+
| `keyboardDismissMode: ('none' / 'on-drag')` | Determines whether the keyboard gets dismissed in response to a drag | both |
143+
| `orientation: Orientation` | Set `horizontal` or `vertical` scrolling orientation (it does **not** work dynamically) | both |
144+
| `transitionStyle: TransitionStyle` | Use `scroll` or `curl` to change transition style (it does **not** work dynamically) | iOS |
145+
| `showPageIndicator: boolean` | Shows the dots indicator at the bottom of the view | iOS |
146+
| `overScrollMode: OverScollMode` | Used to override default value of overScroll mode. Can be `auto`, `always` or `never`. Defaults to `auto` | Android |
147+
| `offscreenPageLimit: number` | Set the number of pages that should be retained to either side of the currently visible page(s). Pages beyond this limit will be recreated from the adapter when needed. Defaults to RecyclerView's caching strategy. The given value must either be larger than 0. | Android |
148+
| `overdrag: boolean` | Allows for overscrolling after reaching the end or very beginning or pages | iOS |
149+
150+
| Method | Description | Platform |
151+
| ------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: |
152+
| `setPage(index: number)` | Function to scroll to a specific page in the PagerView. Invalid index is ignored. | both |
153+
| `setPageWithoutAnimation(index: number)` | Function to scroll to a specific page in the PagerView. Invalid index is ignored. | both |
154+
| `setScrollEnabled(scrollEnabled: boolean)` | A helper function to enable/disable scroll imperatively. The recommended way is using the scrollEnabled prop, however, there might be a case where a imperative solution is more useful (e.g. for not blocking an animation) | both |
149155

150156
## Contributing
151157

@@ -165,20 +171,19 @@ requestAnimationFrame(() => refPagerView.current?.setPage(index));
165171

166172
### Android
167173

168-
horizontal | vertical
169-
:-------------------------:|:-------------------------:
170-
<img src="img/android-viewpager.gif" alt="ViewPager" width="325"> | <img src="img/android-viewpager-vertical.gif" alt="ViewPager" width="325">
174+
| horizontal | vertical |
175+
| :---------------------------------------------------------------: | :------------------------------------------------------------------------: |
176+
| <img src="img/android-viewpager.gif" alt="ViewPager" width="325"> | <img src="img/android-viewpager-vertical.gif" alt="ViewPager" width="325"> |
171177

172178
### iOS
173179

174-
horizontal - scroll | horizontal - curl
175-
:-------------------------:|:-------------------------:
176-
<img src="img/ios-viewpager-scroll.gif" alt="ViewPager" width="325"> | <img src="img/ios-viewpager-curl.gif" alt="ViewPager" width="325">
177-
178-
vertical - scroll | vertical - curl
179-
:-------------------------:|:-------------------------:
180-
<img src="img/ios-viewpager-vertical.gif" alt="ViewPager" width="325"> | <img src="img/ios-viewpager-vertical-curl.gif" alt="ViewPager" width="325">
180+
| horizontal - scroll | horizontal - curl |
181+
| :------------------------------------------------------------------: | :----------------------------------------------------------------: |
182+
| <img src="img/ios-viewpager-scroll.gif" alt="ViewPager" width="325"> | <img src="img/ios-viewpager-curl.gif" alt="ViewPager" width="325"> |
181183

184+
| vertical - scroll | vertical - curl |
185+
| :--------------------------------------------------------------------: | :-------------------------------------------------------------------------: |
186+
| <img src="img/ios-viewpager-vertical.gif" alt="ViewPager" width="325"> | <img src="img/ios-viewpager-vertical-curl.gif" alt="ViewPager" width="325"> |
182187

183188
## License
184189

android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,16 @@ class PagerViewViewManager : ViewGroupManager<ViewPager2>() {
122122
@ReactProp(name = "overScrollMode")
123123
fun setOverScrollMode(viewPager: ViewPager2, value: String) {
124124
val child = viewPager.getChildAt(0)
125-
if (value == "never") {
126-
child.overScrollMode = ViewPager2.OVER_SCROLL_NEVER
127-
} else if (value == "always") {
128-
child.overScrollMode = ViewPager2.OVER_SCROLL_ALWAYS
129-
} else {
130-
child.overScrollMode = ViewPager2.OVER_SCROLL_IF_CONTENT_SCROLLS
125+
when (value) {
126+
"never" -> {
127+
child.overScrollMode = ViewPager2.OVER_SCROLL_NEVER
128+
}
129+
"always" -> {
130+
child.overScrollMode = ViewPager2.OVER_SCROLL_ALWAYS
131+
}
132+
else -> {
133+
child.overScrollMode = ViewPager2.OVER_SCROLL_IF_CONTENT_SCROLLS
134+
}
131135
}
132136
}
133137

@@ -152,20 +156,20 @@ class PagerViewViewManager : ViewGroupManager<ViewPager2>() {
152156
super.receiveCommand(root, commandId, args)
153157
Assertions.assertNotNull(root)
154158
Assertions.assertNotNull(args)
159+
val childCount = root.adapter?.itemCount
160+
155161
when (commandId) {
156-
COMMAND_SET_PAGE -> {
157-
setCurrentItem(root, args!!.getInt(0), true)
158-
eventDispatcher!!.dispatchEvent(PageSelectedEvent(root.id, args.getInt(0)))
159-
return
160-
}
161-
COMMAND_SET_PAGE_WITHOUT_ANIMATION -> {
162-
setCurrentItem(root, args!!.getInt(0), false)
163-
eventDispatcher!!.dispatchEvent(PageSelectedEvent(root.id, args.getInt(0)))
164-
return
162+
COMMAND_SET_PAGE, COMMAND_SET_PAGE_WITHOUT_ANIMATION -> {
163+
val pageIndex = args!!.getInt(0)
164+
val canScroll = childCount != null && childCount > 0 && pageIndex >= 0 && pageIndex < childCount
165+
if (canScroll) {
166+
val scrollWithAnimation = commandId == COMMAND_SET_PAGE
167+
setCurrentItem(root, pageIndex, scrollWithAnimation)
168+
eventDispatcher.dispatchEvent(PageSelectedEvent(root.id, pageIndex))
169+
}
165170
}
166171
COMMAND_SET_SCROLL_ENABLED -> {
167172
root.isUserInputEnabled = args!!.getBoolean(0)
168-
return
169173
}
170174
else -> throw IllegalArgumentException(String.format(
171175
"Unsupported command %d received by %s.",

ios/ReactNativePageView.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,13 @@ - (void)updateDataSource {
213213
- (void)goTo:(NSInteger)index animated:(BOOL)animated {
214214
NSInteger numberOfPages = self.reactSubviews.count;
215215

216-
if (numberOfPages == 0 || index < 0) {
216+
if (numberOfPages == 0 || index < 0 || index > numberOfPages - 1) {
217217
return;
218218
}
219219

220220
UIPageViewControllerNavigationDirection direction = (index > self.currentIndex) ? UIPageViewControllerNavigationDirectionForward : UIPageViewControllerNavigationDirectionReverse;
221221

222-
NSInteger indexToDisplay = index < numberOfPages ? index : numberOfPages - 1;
222+
NSInteger indexToDisplay = index;
223223

224224
UIView *viewToDisplay = self.reactSubviews[indexToDisplay];
225225
UIViewController *controllerToDisplay = [self findAndCacheControllerForView:viewToDisplay];

0 commit comments

Comments
 (0)