SwiftUI tvOS向下导航时如何让底部HStack首个按钮A获取焦点
tvOS SwiftUI 自定义向下焦点跳转实现方案
核心原理
系统默认焦点导航按几何距离最近原则选中目标元素,要实现固定跳转到底部第一个按钮A,通过tvOS 15+原生提供的@FocusState和onMoveFocusAPI手动接管焦点跳转逻辑即可,无需引入第三方依赖或桥接复杂UIKit逻辑,和你现有代码的版本适配要求完全匹配。
实现步骤
- 定义遵守
Hashable协议的焦点枚举,为所有可交互按钮分配唯一标识 - 添加
@FocusState属性,实时追踪当前持有的焦点元素 - 给所有按钮绑定对应的焦点ID,建立状态和UI元素的关联
- 给顶部按钮所在的焦点区域添加
onMoveFocus拦截逻辑,检测到用户触发向下导航时,直接将焦点赋值给底部按钮A,返回true告知系统本次跳转已由自定义逻辑处理,不再执行默认的最近元素匹配规则。
完整修改后代码
import SwiftUI // 标记所有可聚焦元素的唯一身份 enum FocusTarget: Hashable { case topBtn1, topBtn2, topBtn3 case bottomBtnA, bottomBtnB, bottomBtnC } struct DummyView: View { @Environment(\.presentationMode) var presentationMode // 焦点状态绑定 @FocusState private var currentFocus: FocusTarget? var body: some View { ZStack { // 原代码未显式声明容器,两个全尺寸View实际为叠放关系,用ZStack明确布局逻辑 contentView parent } } private var parent: some View { VStack { if #available(tvOS 15.0, *) { HStack { Spacer() Button ("1") {} .focused($currentFocus, equals: .topBtn1) Button ("2") {} .focused($currentFocus, equals: .topBtn2) Button ("3") {} .focused($currentFocus, equals: .topBtn3) Spacer() } .focusSection() .border(Color.white, width: 2) // 拦截顶部区域的焦点移动事件 .onMoveFocus { direction in guard direction == .down else { return false } // 向下操作时强制将焦点移至底部按钮A currentFocus = .bottomBtnA return true } } else { // 低版本适配逻辑可在此补充 } Spacer() if #available(tvOS 15.0, *) { HStack { Button ("A") {} .focused($currentFocus, equals: .bottomBtnA) Spacer() Button ("B") {} .focused($currentFocus, equals: .bottomBtnB) Spacer() Button ("C") {} .focused($currentFocus, equals: .bottomBtnC) } .border(Color.white, width: 2) .focusSection() } else { // 低版本适配逻辑可在此补充 } } } private var contentView: some View { VStack { Spacer() Text("THIS IS DUMMY SCREEN") Spacer() } } }
补充说明
- 上述逻辑仅拦截顶部区域的向下导航操作,其余交互(顶部按钮左右切换、底部按钮左右/向上导航)均走系统默认逻辑,不会破坏原有操作体验
- 如果需要适配tvOS 15以下版本,可通过
UIViewRepresentable桥接UIKit的UIFocusEnvironment协议,重写preferredFocusEnvironments实现自定义跳转 - 若需要指定页面初始焦点,可在页面
.onAppear回调中给currentFocus赋值对应枚举值即可

内容的提问来源于stack exchange,提问作者Abhishek Thapliyal
相关产品推荐
相关产品推荐

