You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 03:30:46