Problem Description
The Tabs component has a horizontal scrolling control gesture. When nested within Tabs or horizontal scrolling and sliding components such as List, Scroll, Swiper, and Grid, a horizontal scrolling gesture conflict occurs, preventing horizontal scrolling of the outer Tabs. Using the Grid as an example, the problem is as follows:
The Tabs component nests within the Grid component. When the Grid component is scrolled to the bottom right, the Tabs component fails to scroll. As shown in the image below, swiping left on the two rows of icons on the page should trigger the tab switch from "Home 1" to "Home 2," but this doesn't actually happen.
Background Knowledge
- The Tabs component is an official navigation and switching component, and its page switching logic can be manually controlled using the TabsController.
- The List, Scroll, and Grid components are official sliding and scrolling components, supporting common scrolling property settings. Each component can control horizontal or vertical scrolling through its own properties. However, horizontal scrolling can cause gesture conflicts when nested with Tabs.
- The Swiper component is a slider view container, providing a sliding carousel display for its child components. Its sliding logic can also be manually controlled using the SwiperController.
Troubleshooting Process
- When using nested List, Scroll, Grid, and other components, scrollToIndex and scrollTo are supported. Using scrollBy is not recommended and will not produce animation effects.
- When using scrollTo, consider offset accumulation, such as using: this.listController.scrollTo({xOffset:this.xOffsets,yOffset:0,animation:true}) where this.xOffsets += (-event.offsetX).
- If this solution is used with nested List, Scroll, Grid, and other components, and each item does not occupy the entire screen width like Swipers/Tabs, consider whether the index is accurately determined and adjust it based on the item size.
- Solution 3: Implement scrolling logic for components nested within Tabs using a custom gesture detection PanGesture.
- Using a Swiper component nested within Tabs as an example:
- When the Swiper displays the first card, swiping rightward will cause Tabs to switch to the previous tab.
- When the Swiper displays the last card, swiping leftward will cause Tabs to switch to the next tab.
- At other times, left and right swipe gestures will trigger the Swiper's switching function: swiping left will switch to the previous card, and swiping right will switch to the next card.
Analysis Conclusion
To summarize, the following is the conclusion:
- The nestedScroll property of the Swiper component is inconsistent with the nestedScroll property of Scroll, List, and Grid. Furthermore, Swiper does not support common scroll component properties. Therefore, when nesting Swiper components within Tabs, Solutions 1 and 2 are not applicable.
- The Tabs component is a navigation and switching component and does not support nestedScroll or common scroll component properties. Therefore, when nesting Tabs within Tabs, Solutions 1 and 2 are also not applicable.
- When nesting List, Scroll, or Grid components within Tabs, Solutions 1 and 2 are preferred. When nesting Tabs or Swipers within Tabs, Solutions 3 and 4 are preferred.
Solution
- Solution 1: Since the List, Scroll, and Grid scrolling components have a common property called "nestedScroll," you can set scroll priority. For example, using a horizontally scrolling Grid, set the "nestedScroll" property for the Grid while enabling tabs swipe switching. Also, set the tabs swipe property to true.
Tabs({ barPosition: BarPosition.Start }) {
ForEach(this.exampleModel, (model: ExampleModel, index: number) => {
TabContent() {
Grid() {
}
.nestedScroll({ // Set up nested scrolling for the Grid.
scrollForward: NestedScrollMode.SELF_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
}
})
}
.scrollable(true) // Enable scrolling for Tabs.
- Solution 2: Since the List, Scroll, and Grid scrolling components have common events like onReachEnd and onReachStart, you can determine whether to execute the outer tab switching event. Use Grid events to control the scrolling effect: use onScrollFrameBegin to obtain the actual scroll distance and current scroll state, onScrollStop to set the grid's behavior after the scrolling stops, onReachStart to set the grid's behavior when the scrolling stops, and onReachEnd to set the grid's behavior when the scrolling stops. Combining the above instructions, to handle the tab switching in onScrollStop:
Grid(){
ForEach(this.exampleList, (item: Resource, index: number) => {
GridItem() {}
})
}
.onReachEnd(() => {}) // Reach the rightmost edge.
.onReachStart(() => {}) // Reach the leftmost edge.
.onScrollFrameBegin((offset: number, state: ScrollState) => {}) // Get the actual scroll distance and current scroll state.
.onScrollStop(() => {}) // Determine whether to switch tabs after the grid stops scrolling by determining whether the grid has reached the boundary and the current scroll state.
// Swiper is inside the second TabContent.
Swiper(this.swiperController) {
ForEach(this.data, (item: number, index: number) => {
Text(item.toString())
.width('100%')
.height(160)
.backgroundColor(0xAFEEEE)
.textAlign(TextAlign.Center)
.fontSize(30)
.gesture(
PanGesture()
.onActionStart((event: GestureEvent) => {
console.info('Pan start')
})
.onActionUpdate((event: GestureEvent) => {
console.info('Pan update')
})
.onActionEnd((event: GestureEvent) => {
// Swiper uses if/else logic in the second page of Tabs to prioritize the Swiper edge sliding status.
if (index === 0 && event.offsetX > 0) {
this.controller.changeIndex(0) // Swiper slides to the first page and continues to slide right, the Tabs controller jumps to the first page.
} else if (index === (this.data.length - 1) && event.offsetX < 0) {
this.controller.changeIndex(2) // Swiper slides to the last page and continues to slide left, the Tabs controller jumps to the third page.
} else if (event.offsetX < 0) {
this.swiperController.showNext() // Swiper controller.
} else if (event.offsetX > 0) {
this.swiperController.showPrevious() // Swiper controller.
}
})
)
}, (item: string) => item)
}
3.Scrolling of nested Tabs can also be achieved using PanGesture. Simply bind the gesture handler to the first and last TabContent of the inner Tabs. The complete sample code is as follows:
@Entry
@Component
struct Index {
tabsController: TabsController = new TabsController()
build() {
Column() {
Tabs({ controller: this.tabsController }) {
TabContent() {
Text('Homepage content').fontSize(30)
}
.tabBar('front page')
TabContent() {
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
Text('tab0').fontSize('30fp')
}.tabBar('tab0')
.gesture(
PanGesture({ fingers: 1, distance: 1, direction: PanDirection.Right })
.onActionStart((event: GestureEvent) => {
console.info('Pan start')
})
.onActionEnd((event: GestureEvent) => {
this.tabsController.changeIndex(0)
})
)
// Other TabContent in the middle
TabContent() {
Text('tab3').fontSize('30fp')
}.tabBar('tab3')
.gesture(
PanGesture({ fingers: 1, distance: 1, direction: PanDirection.Left })
.onActionStart((event: GestureEvent) => {
console.info('Pan start')
})
.onActionEnd((event: GestureEvent) => {
this.tabsController.changeIndex(2)
})
)
}.backgroundColor(Color.Pink)
}.tabBar('Discover')
TabContent() {
Text('Recommended content').fontSize(30)
}.tabBar('recommend')
}
}.width('100%').height('100%')
}
}
Solution 4: Use onGestureRecognizerJudgeBegin() to intercept internal component scrolling.
Gesture interception enhancements can be used to resolve scrolling conflicts in nested tabs. For an example, see "Intercepting gestures within a nested container" in a nested scenario. Use onGestureRecognizerJudgeBegin() to listen for gesture events. When the inner tab reaches the beginning or end, gestures are rejected, allowing outer tabs to respond.
Verification Result
The problem of outer tab's cannot be triggered to slide after the Tabs component is nested with the same-direction scrollable component is solved with using one of these solutions.
Related Documents or Links
https://developer.huawei.com/consumer/en/doc/harmonyos-references/ts-container-tabs
https://developer.huawei.com/consumer/en/doc/harmonyos-references/ts-container-swiper#nestedscroll11
https://developer.huawei.com/consumer/en/doc/harmonyos-references/ts-gesture-blocking-enhancement



Top comments (0)