feat(Android, Tabs): integrate with ScrollViewMarker for special effects#4192
feat(Android, Tabs): integrate with ScrollViewMarker for special effects#4192kkafar wants to merge 3 commits into
Conversation
I'm not happy with how detection currently works. I think instead of relying on UIManager callbacks, we should just rely on `onAttachedToWindow` & `onDetachedFromWindow` callbacks. We should carefully consider that option to simplify the code.
738c715 to
f95ff89
Compare
Anchor both repeated-tab-selection effects on the selected TabsScreen instead of the container content view, so pop-to-root and scroll-to-top search from the same, tighter-scoped root. Widen findScrollViewInFirstDescendantChain to also match NestedScrollView so the fallback path can resolve it, and collapse the duplicated ScrollView/NestedScrollView branches in trySmoothScrollToTop into a single when over the shared scroll check.
There was a problem hiding this comment.
Pull request overview
This PR improves Android Tabs “repeated selection” special effects by integrating tab content scroll-to-top behavior with the ScrollViewMarker registration mechanism, making the scroll view lookup more reliable and adding NestedScrollView support.
Changes:
TabsScreennow participates in theScrollViewMarkermechanism by implementingScrollViewSeekingand storing a weak reference to the registered content scroll view.- Repeated-selection special effects now resolve the scroll view via marker registration first, then fall back to the existing descendant-chain search.
- The fallback scroll-view search and smooth-scroll behavior now support both
ScrollViewandNestedScrollView.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| android/src/main/java/com/swmansion/rnscreens/gamma/tabs/screen/TabsScreen.kt | Makes the selected tab screen capable of receiving a registered scroll view reference from ScrollViewMarker. |
| android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsContainer.kt | Updates repeated-selection effects to target the selected TabsScreen and adds scroll view resolution + NestedScrollView scrolling support. |
| android/src/main/java/com/swmansion/rnscreens/gamma/helpers/ViewFinder.kt | Expands the fallback “first descendant chain” lookup to match NestedScrollView and returns ViewGroup?. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| class TabsScreen( | ||
| val reactContext: ThemedReactContext, | ||
| ) : ViewGroup(reactContext), | ||
| FragmentProviding { | ||
| override fun onLayout( | ||
| changed: Boolean, | ||
| l: Int, | ||
| t: Int, | ||
| r: Int, | ||
| b: Int, | ||
| ) = Unit | ||
|
|
||
| FragmentProviding, | ||
| ScrollViewSeeking { |
| /** | ||
| * Attempts to scroll to top if the passed view is a `ScrollView` or `NestedScrollView` | ||
| */ | ||
| private fun trySmoothScrollToTop(maybeScrollView: ViewGroup): Boolean { |
There was a problem hiding this comment.
Instead of ViewGroup, you could use FrameLayout which is closer to the actual classes
Also, gemini came up with sth like that
interface ScrollableContainer {
fun scrollToTop()
}
// Extension functions to easily wrap them
fun ScrollView.asScrollable() = object : ScrollableContainer {
override fun scrollToTop() = smoothScrollTo(0, 0)
}
fun NestedScrollView.asScrollable() = object : ScrollableContainer {
override fun scrollToTop() = smoothScrollTo(0, 0)
}this is probably overkill, but you could make the typing more strict this way
| tabsScreenDelegate.get()?.onFragmentConfigurationChange(this, config) | ||
| } | ||
|
|
||
| // ScrollViewSeeking |
There was a problem hiding this comment.
maybe region / endregion instead?
Description
Repeated selection of the already-active tab runs "special effects" — pop the
stack to root and/or scroll the content to the top. Until now the scroll view
backing the scroll-to-top effect was located purely by walking the first
descendant of the tab content, which is fragile and only ever matched
android.widget.ScrollView.This PR integrates that lookup with the
ScrollViewMarkermechanism: a markermounted around the content's scroll view registers it with the nearest seeking
ancestor, giving the tab a reliable handle to the correct scroll view instead
of relying solely on tree traversal. It also adds
NestedScrollViewsupport.Closes https://github.com/software-mansion/react-native-screens-labs/issues/1538
Closes https://github.com/software-mansion/react-native-screens-labs/issues/1423
Note
The pop-to-root is not-yet-implemented due to modal constraints. It requires changes in the tabs model (simplifying here).
Changes
TabsScreennow implementsScrollViewSeeking; it stores the scroll viewregistered by a
ScrollViewMarkerin aWeakReferenceand exposes it viacontentScrollView().SpecialEffectsHandlerresolves the content scroll view from the registeredmarker first, falling back to the descendant-chain search. Both
repeated-selection effects (pop-to-root and scroll-to-top) now anchor on the
selected
TabsScreenrather than the container content view.ScrollViewandNestedScrollView.ViewFinder.findScrollViewInFirstDescendantChainwidened to returnViewGroup?and matchNestedScrollView, so the fallback path can resolveit too.
Visual docs
android-tabs-special-effects-before.mov
android-tabs-special-effects-after.mov
Test plan
Tested on Android via the example app scenario:
apps/src/tests/component-integration-tests/scroll-view-marker/test-stack-svm-tabs-special-effects/Steps:
onto the stack.
for both
ScrollView- andNestedScrollView-backed content.Checklist