如何修复tvOS 15.2中SwiftUI TabView滚动时的焦点回退问题
tvOS 15.2 中ScrollView嵌套TabView的焦点回退问题
问题详情
- 在tvOS 15.2系统中,将TabView嵌套进ScrollView后,从左向右切换TabView页面时,会出现焦点意外回退到ScrollView下方按钮的异常
- 该问题仅存在于tvOS 15.2,tvOS 14和16版本无此现象
- 场景演示:焦点处于TabView内的按钮上,按下右方向键切换Tab页时,焦点未移动到新Tab页的对应按钮,反而跳转到ScrollView下方的随机按钮
复现代码
import SwiftUI struct ContentView: View { @State var index: Int = 0 var body: some View { ScrollView(showsIndicators: true) { VStack { TabView(selection: $index) { ForEach(0..<5, id: \.self) { item in //PageContent(model: data[item]) Button { } label: { Text("METIN ATALAY \(item)") } } } .background(Color.yellow) .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) .padding(.bottom, 0) .frame(width: UI.screenSize.width, height: UI.screenSize.height * 0.64) Button { } label: { Text("\(UUID().uuidString)") } Button { } label: { Text("\(UUID().uuidString)") } } } } struct UI { static let screenBounds: CGRect = UIScreen.main.bounds static let screenSize: CGSize = CGSize(width: screenBounds.width, height: screenBounds.height) }
修复方案
方案1:手动控制Tab切换后的焦点
利用SwiftUI原生@FocusStateAPI手动管理焦点,当Tab索引变化时,强制将焦点绑定到当前Tab页的按钮:
import SwiftUI struct ContentView: View { @State var index: Int = 0 @FocusState private var focusedButton: Int? var body: some View { ScrollView(showsIndicators: true) { VStack { TabView(selection: $index) { ForEach(0..<5, id: \.self) { item in Button { } label: { Text("METIN ATALAY \(item)") } .focused($focusedButton, equals: item) } } .background(Color.yellow) .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) .padding(.bottom, 0) .frame(width: UI.screenSize.width, height: UI.screenSize.height * 0.64) .onChange(of: index) { newIndex in focusedButton = newIndex } Button { } label: { Text("\(UUID().uuidString)") } Button { } label: { Text("\(UUID().uuidString)") } } } } } struct UI { static let screenBounds: CGRect = UIScreen.main.bounds static let screenSize: CGSize = CGSize(width: screenBounds.width, height: screenBounds.height) }
方案2:调整底层ScrollView的焦点行为
通过SwiftUI-Introspect库获取底层UIScrollView,修改其触摸响应属性以避免干扰TabView焦点(需先添加该库依赖):
import SwiftUI import Introspect struct ContentView: View { @State var index: Int = 0 var body: some View { ScrollView(showsIndicators: true) { VStack { TabView(selection: $index) { ForEach(0..<5, id: \.self) { item in Button { } label: { Text("METIN ATALAY \(item)") } } } .background(Color.yellow) .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) .padding(.bottom, 0) .frame(width: UI.screenSize.width, height: UI.screenSize.height * 0.64) Button { } label: { Text("\(UUID().uuidString)") } Button { } label: { Text("\(UUID().uuidString)") } } } .introspectScrollView { scrollView in scrollView.canCancelContentTouches = false scrollView.delaysContentTouches = false } } } struct UI { static let screenBounds: CGRect = UIScreen.main.bounds static let screenSize: CGSize = CGSize(width: screenBounds.width, height: screenBounds.height) }
内容的提问来源于stack exchange,提问作者Metin Atalay
相关产品推荐
相关产品推荐

