用 SwiftUI 构建 tvOS App
Build SwiftUI apps for tvOS
2020年6月26日
一句话判断
tvOS 终于支持 SwiftUI——但电视的交互模型(遥控器聚焦、无触摸、无多任务)意味着你需要重新思考 SwiftUI 视图的交互设计,不是简单地把 iOS App 搬过去。
这场 Session 讲了什么
SwiftUI 在 tvOS 14 上首次可用,覆盖了大部分核心组件:Text、Image、Button、List、NavigationView、TabView 等。但 tvOS 有独特的交互模型,需要针对性适配。
核心差异在于焦点系统。Apple TV 用遥控器操作,通过方向键在可交互元素之间移动焦点。SwiftUI 的 focusable() 修饰符控制视图是否可获焦,prefersDefaultFocus() 控制初始焦点位置。.onPlayPauseCommand() 处理遥控器的播放/暂停按钮。
另一个重要特性是 tvOS 的全屏卡片布局。电视屏幕大、观看距离远,UI 元素需要更大、更清晰。Session 展示了如何用 Button 和 NavigationLink 构建适合电视的卡片式导航界面。
值得深挖的点
焦点管理的挑战。 iOS 上的触摸交互是”所见即所得”,但 tvOS 上的焦点导航是线性的。复杂的布局可能导致焦点跳转不符合预期。SwiftUI 提供了 focusScope() 和 prefersDefaultFocus() 来控制焦点区域和初始位置,但大面积使用时仍然需要仔细测试。
tvOS 的视觉设计原则。 电视屏幕大、观看距离 2-3 米,文字和控件需要比 iOS 大得多。Session 建议 minimum hit target 为 120pt(对比 iOS 的 44pt),文字最小 29pt。间距也要加大,避免视觉拥挤。
代码片段
import SwiftUI
// tvOS 基本的卡片布局
struct MovieCatalogView: View {
let movies: [Movie]
var body: some View {
ScrollView(.horizontal) {
LazyHStack(spacing: 40) { // tvOS 需要更大的间距
ForEach(movies) { movie in
NavigationLink(destination: MovieDetailView(movie: movie)) {
MovieCard(movie: movie)
.frame(width: 300, height: 450) // 电视上的卡片尺寸
}
.buttonStyle(PlainButtonStyle())
}
}
.padding(60) // 更大的内边距
}
}
}
struct MovieCard: View {
let movie: Movie
var body: some View {
VStack {
movie.posterImage
.resizable()
.aspectRatio(contentMode: .fill)
.frame(height: 380)
.cornerRadius(12)
Text(movie.title)
.font(.system(size: 29)) // tvOS 推荐的最小字号
.lineLimit(2)
}
}
}
// 焦点控制
struct VideoPlayerView: View {
@FocusState private var isPlayerFocused: Bool
var body: some View {
VStack {
VideoPlayerView(video: video)
.focusable() // 可以接收遥控器焦点
.focused($isPlayerFocused)
HStack(spacing: 40) {
Button("倒退 10 秒") { seekBackward() }
Button("播放/暂停") { togglePlayback() }
.onPlayPauseCommand { togglePlayback() } // 遥控器播放键
Button("前进 10 秒") { seekForward() }
}
.prefersDefaultFocus(in: controlsScope) // 初始焦点
}
}
}
// tvOS 的 TabView 侧边导航
struct MainTabView: View {
var body: some View {
TabView {
Tab("首页", systemImage: "house") {
HomeView()
}
Tab("搜索", systemImage: "magnifyingglass") {
SearchView()
}
Tab("我的", systemImage: "person") {
ProfileView()
}
}
}
}
最佳实践
- 最小触控区域 120pt。 遥控器的焦点移动需要足够大的目标区域。
- 文字最小 29pt。 电视观看距离远,小文字不可读。
- 大幅增加间距和内边距。 iOS 上合适的间距在电视上会显得拥挤。
- 优先测试焦点导航路径。 用遥控器模拟器遍历所有界面,确保焦点跳转合理。
还有什么值得关注
- tvOS 的
onSwipeCommand可以处理遥控器的滑动手势 .focusEffectDisabled()可以禁用某个视图的焦点高亮效果- tvOS SwiftUI 支持暗色模式的系统默认主题
- Top Shelf Extension 可以配合 SwiftUI 展示 tvOS 主屏推荐内容