Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ViewPager] Add setPageWithoutAnimation #3621

Closed
wants to merge 3 commits into from
Closed

[ViewPager] Add setPageWithoutAnimation #3621

wants to merge 3 commits into from

Conversation

brentvatne
Copy link
Collaborator

In some cases it's desirable to set the page in the ViewPager without animating it -- we have this in ScrollView with scrollWithoutAnimationTo. This PR adds setPageWithoutAnimation on ViewPager.

cc @ide @kmagiera

@facebook-github-bot
Copy link
Contributor

By analyzing the blame information on this pull request, we identified @mkonicek to be a potential reviewer.

@facebook-github-bot facebook-github-bot added GH Review: review-needed CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. labels Oct 23, 2015
@mkonicek
Copy link
Contributor

Thanks Brent! Will take a look.

@@ -41,6 +41,11 @@ public void setSelectedPage(ReactViewPager view, int page) {
view.setCurrentItemFromJs(page);
}

@ReactProp(name = "animatePageTransition")
public void setAnimatePageTransition(ReactViewPager view, boolean animatedPageTransition) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: animated -> animate

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to get rid of this as per comment below :)

@brentvatne
Copy link
Collaborator Author

@mkonicek - updated!

@facebook-github-bot
Copy link
Contributor

@brentvatne updated the pull request.

@mkonicek
Copy link
Contributor

Oh sorry I meant:

// All page updates are instant
<ViewPager animateSetPage={false} selectedPage={this.state.page} ... />

// All page updates are animated
<ViewPager selectedPage={this.state.page} ... />

Do you think that API would make sense?

It's similar to how you set the bounces property on a ScrollView - it's a prop to control its visual behavior.

With the current solution there are two ways to set a page which seems a bit confusing to me. Looking at the API I would think: logically a ViewPager only has one selected page, why does it also have a selectedPageWithoutAnimation? If I update one does the other also change?

@mkonicek
Copy link
Contributor

Is it sufficient for you to create a ViewPager that never animates, or do you need to flip between animated and instant page sets dynamically?

The dynamic case is harder: I think we should have some way to set both animateSetPage and selectedPage in the same batch (same render()) and have the native side set all the props first and only then call setCurrentItemFromJs, so it can determine whether to animate or not based on values of other props.

@mkonicek
Copy link
Contributor

Just talked to Krzysztof who made a really good point - another option is not to have a selectedPage prop at all but have methods setPage, setPageWithoutAnimation. But then, is it the right API from React's perspective?

We think this requires more discussion, will chat about it with more people at fb to get more opinions.

@brentvatne
Copy link
Collaborator Author

Just talked to Krzysztof who made a really good point - another option is not to have a selectedPage prop at all but have methods setPage, setPageWithoutAnimation. But then, is it the right API from React's perspective?

This is the exact approach that we are currently using with this PR 😄 -- the public interface for props only exposes initialPage, onPageScroll, onPageSelected, and keyboardDismissMode. In addition to this, we expose setPage or setPageWithoutAnimation functions that can be called on the ref.

Internally, when setPage or setPageWithoutAnimation are called, we use props to update those. I think this part is an implementation detail and I just went along with how setPage was implemented already, but if we wanted some more consistency with how it's done on iOS at the moment then we could look at ScrollView and the equivalents scrollTo and scrollWithoutAnimationTo. These rely on bridge methods on RCTUIManager of the same name. So the implementation of setPage(selectedPage) on ViewPagerAndroid.android.js would look something like this:

  setPage: function(selectedPage) {
    ViewPagerManager.setPage(selectedPage);
  },

rather than its current implementation:

  setPage: function(selectedPage) {
    this.setState({
      selectedPage,
    });
  },

@brentvatne
Copy link
Collaborator Author

@mkonicek - ok thinking about this some more and I think the currently implemented approach of using props to update the page is flawed. Think of the following situation:

  • this.viewPager.setPage(5)
  • Manually scroll to page 1
  • this.viewPager.setPage(5)
  • Page remains on 1. This is because React sees currentPage as 5 on the ViewPager, so and when we setState via setPage we set it to 5 again, which of course results in no update being sent over to native.

So, I think for values that live on the native side like this and scroll position and such, we either need to have a very good approach for keeping that in sync with the JS side or just use the approach from ScrollView to set them imperatively.

@machard
Copy link
Contributor

machard commented Nov 6, 2015

I need this feature too to make a looping carousel.
I looked quickly how to code the imperative approach but I'm not sure how to access the view object from a ReactMethod. I think I should use the reactTag but I'm not sure what to use to get the view from it.

machard referenced this pull request in machard/react-native Nov 6, 2015
machard referenced this pull request in machard/react-native Nov 6, 2015
@mkonicek
Copy link
Contributor

mkonicek commented Nov 7, 2015

@brentvatne I realized we also want to make the ViewPagerAndroid API closer to the iOS ScrollView for consistency: We might even want to render a ViewPagerAndroid under the hood if you specify <ScrollView pagingEnabled />.

Therefore I think we should get rid of the prop and have a function to do the paging imperatively, like ScrollView does.

We should make this work exactly the same way as on iOS. It doesn't look like ScrollView has an easy way to scroll to a given page though?

scrollTo: function(destY?: number, destX?: number) {
  // $FlowFixMe - Don't know how to pass Mixin correctly. Postpone for now
  this.getScrollResponder().scrollResponderScrollTo(destX || 0, destY || 0);
},

We should support that on both platforms.

We can make this consistent in steps:

  1. First, change the ViewPagerAndroid API to have two imperative functions for scrolling, get rid of the prop. Update the example. That's a good start, let's do that in this PR.
  2. Add support for scrolling to page to iOS ScrollView
  3. Unify both APIs

@mkonicek
Copy link
Contributor

mkonicek commented Nov 7, 2015

@machard Appreciate your help! You can read the code for ScrollView.scrollTo for example, this will be very similar. But I think @brentvatne has got this once he's back from vacation :)

@mkonicek
Copy link
Contributor

mkonicek commented Nov 7, 2015

@brentvatne Your example with setting the page to 5, scrolling and then setting it again is great! Should have thought of it. Luckily it's not so hard to change it :)

@facebook-github-bot
Copy link
Contributor

@brentvatne updated the pull request.

1 similar comment
@facebook-github-bot
Copy link
Contributor

@brentvatne updated the pull request.

@brentvatne
Copy link
Collaborator Author

Hiya @mkonicek!

@brentvatne I realized we also want to make the ViewPagerAndroid API closer to the iOS ScrollView for consistency: We might even want to render a ViewPagerAndroid under the hood if you specify .

Yup! That would be nice to have eventually.

Therefore I think we should get rid of the prop and have a function to do the paging imperatively, like ScrollView does.

We should make this work exactly the same way as on iOS. It doesn't look like ScrollView has an easy way to scroll to a given page though?

Correct! Right now you have to just know the width of each page and multiply that by the page that you want to be on. Would be nice to have this on both though, agreed.

We can make this consistent in steps:

  1. First, change the ViewPagerAndroid API to have two imperative functions for scrolling, get rid of the prop. Update the example. That's a good start, let's do that in this PR.
  2. Add support for scrolling to page to iOS ScrollView
  3. Unify both APIs

Sounds good! I took care of #1 here. setPage and setPageWithoutAnimation are implemented imperatively now.

@ide
Copy link
Contributor

ide commented Nov 7, 2015

A brief thought on paginated scrollviews and ViewPager -- we may want (hypothetically speaking) ViewPager.ios.js to use a paginated ScrollView instead of having ScrollView sometimes render a ViewPager. I think of ScrollView as a lower-level component than ViewPager, and it might be easier to implement ViewPager on iOS using a ScrollView, than it is to implement a paginated ScrollView on Android using a ViewPager (think of all the scroll responder APIs).

@mkonicek
Copy link
Contributor

mkonicek commented Nov 8, 2015

That's a good point @ide.

@brentvatne Looks awesome! I think you might be able to inline the ReactViewPagerCommandHelper into the View pager, the reason we have that helper for ScrollView is that there is a vertical and horizontal one and wanted to share code between them.

Does the initialPage prop as expected with this change? Could you also try running and update the example please?
https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/ViewPagerAndroidExample.android.js

@facebook-github-bot
Copy link
Contributor

@brentvatne updated the pull request.

@brentvatne
Copy link
Collaborator Author

I think you might be able to inline the ReactViewPagerCommandHelper into the View pager, the reason we have that helper for ScrollView is that there is a vertical and horizontal one and wanted to share code between them.

Done!

Does the initialPage prop as expected with this change? Could you also try running and update the example please?

It does now! Updated example to add a button to toggle between using setPage and setPageWithoutAnimation

return this.refs[VIEWPAGER_REF].getInnerViewNode();
},

_childrenWithOverridenStyle: function() {
_childrenWithOverridenStyle: function(): Array {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not great with flow -- what should I put here as the return type @mkonicek?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. Try Array<ReactElement> and run flow from the root of the repo and see if that works.

Doesn't look like the React codebase uses Flow and not sure how the Flow handles the boundaries between code that uses Flow and code that doesn't.

If Array<ReactElement> doesn't work I'd go for Array or Array<Object>. It's a private function you're using just in this file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is no longer required to override styles this way here. We did this initially because we didn't have view flattening on android back then. Now that view flattening is there we should just wrap children in views positioned absolutely and with collapsable=false similarly to how we do it here: https://github.com/facebook/react-native/blob/master/Libraries/Components/ScrollView/RecyclerViewBackedScrollView.android.js#L95

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kmagiera It's a good point and looked into doing that when open sourcing the view pager but decided not to (don't remember the exact reason anymore). I think changing this is outside the scope of Brent's PR though. I'll pull this in as it looks good otherwise.

@mkonicek
Copy link
Contributor

Looks awesome! 👍 Thanks for improving the example and using Flow too!

Sorry for the delay. Feel free to try out what return type works best for _childrenWithOverridenStyle (or just use Array) and ping me when you want this merged :)

@mkonicek
Copy link
Contributor

@facebook-github-bot shipit

@facebook-github-bot
Copy link
Contributor

Thanks for importing. If you are an FB employee go to https://our.intern.facebook.com/intern/opensource/github/pull_request/568096366678113/int_phab to review.

brentvatne added a commit to expo/react-native that referenced this pull request Nov 13, 2015
Summary: In some cases it's desirable to set the page in the ViewPager without animating it -- we have this in ScrollView with `scrollWithoutAnimationTo`. This PR adds `setPageWithoutAnimation` on ViewPager.

cc ide kmagiera
Closes facebook#3621

Reviewed By: svcscm

Differential Revision: D2652056

Pulled By: mkonicek

fb-gh-sync-id: 6f1f38558c41ffdd863c0ebb2f046c75b5c0392c
sunnylqm pushed a commit to sunnylqm/react-native that referenced this pull request Dec 2, 2015
Summary: In some cases it's desirable to set the page in the ViewPager without animating it -- we have this in ScrollView with `scrollWithoutAnimationTo`. This PR adds `setPageWithoutAnimation` on ViewPager.

cc ide kmagiera
Closes facebook#3621

Reviewed By: svcscm

Differential Revision: D2652056

Pulled By: mkonicek

fb-gh-sync-id: 6f1f38558c41ffdd863c0ebb2f046c75b5c0392c
Crash-- pushed a commit to Crash--/react-native that referenced this pull request Dec 24, 2015
Summary: In some cases it's desirable to set the page in the ViewPager without animating it -- we have this in ScrollView with `scrollWithoutAnimationTo`. This PR adds `setPageWithoutAnimation` on ViewPager.

cc ide kmagiera
Closes facebook#3621

Reviewed By: svcscm

Differential Revision: D2652056

Pulled By: mkonicek

fb-gh-sync-id: 6f1f38558c41ffdd863c0ebb2f046c75b5c0392c
ferrannp pushed a commit to ferrannp/react-native-viewpager that referenced this pull request Feb 8, 2019
Summary: In some cases it's desirable to set the page in the ViewPager without animating it -- we have this in ScrollView with `scrollWithoutAnimationTo`. This PR adds `setPageWithoutAnimation` on ViewPager.

cc ide kmagiera
Closes facebook/react-native#3621

Reviewed By: svcscm

Differential Revision: D2652056

Pulled By: mkonicek

fb-gh-sync-id: 6f1f38558c41ffdd863c0ebb2f046c75b5c0392c
james-watkin added a commit to james-watkin/react-native-pager-view that referenced this pull request Dec 28, 2021
Summary: In some cases it's desirable to set the page in the ViewPager without animating it -- we have this in ScrollView with `scrollWithoutAnimationTo`. This PR adds `setPageWithoutAnimation` on ViewPager.

cc ide kmagiera
Closes facebook/react-native#3621

Reviewed By: svcscm

Differential Revision: D2652056

Pulled By: mkonicek

fb-gh-sync-id: 6f1f38558c41ffdd863c0ebb2f046c75b5c0392c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants