如何在Apple Watch上用Digital Crown实现类Workout应用的纵向TabView
复刻Apple Watch Workout应用纵向滚动Tab效果
是否有公开组件可用?
官方SwiftUI没有原生提供适配Apple Watch的这种纵向分页TabView组件,部分社区开源库有类似实现,但考虑到Workout应用独有的页面切换绿色过渡动画,手动实现更便于定制细节,效果也更贴合原生体验。
手动复刻优化方案
你的旋转视图实现确实比较粗糙,以下是更优雅的实现思路和代码:
1. 核心思路
放弃旋转TabView的方式,改用ZStack叠加页面,通过状态变量控制页面的显示/隐藏及动画效果,同时绑定Digital Crown实现滚动切换,再加手势支持手指滑动操作。
2. 完整实现代码
import SwiftUI struct WorkoutTabView: View { // 页面内容列表 private let pages = ["训练数据", "心率", "进度"] // 当前页面索引 @State private var currentPage = 0 // Digital Crown旋转绑定值 @State private var crownRotation = 0.0 // 过渡动画阶段:0=正常状态,1=绿色高亮,2=淡出 @State private var transitionPhase = 0 var body: some View { VStack(spacing: 0) { // 固定顶部时间区域 Text("00:25:47") .font(.system(size: 16, weight: .medium)) .foregroundColor(.white) .padding(.vertical, 8) .frame(maxWidth: .infinity) .background(Color.black) // 可切换的页面区域 GeometryReader { proxy in ZStack { // 遍历所有页面 ForEach(0..<pages.count, id: \.self) { index in VStack { Text(pages[index]) .font(.title2) .foregroundColor(.white) } .frame(width: proxy.size.width, height: proxy.size.height) .background(Color(UIColor.darkGray)) // 页面显示透明度 .opacity(currentPage == index ? 1 : 0) // 页面缩放效果 .scaleEffect(currentPage == index ? 1 : 0.95) // 绿色过渡高亮层 .overlay( Color.green .opacity(transitionPhase == 1 && currentPage != index ? 0.4 : 0) ) // 动画过渡 .animation(.easeInOut(duration: 0.2), value: transitionPhase) .animation(.easeInOut(duration: 0.3), value: currentPage) } } .frame(width: proxy.size.width, height: proxy.size.height) .clipped() // 绑定Digital Crown,控制页面切换 .digitalCrownRotation($crownRotation, from: 0, through: Double(pages.count - 1), by: 1, sensitivity: .high, isContinuous: false, isHapticFeedbackEnabled: true) // 监听Crown旋转变化,触发页面切换和动画 .onChange(of: crownRotation) { newValue in let newPage = Int(newValue.rounded()) guard newPage != currentPage else { return } transitionPhase = 1 DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { currentPage = newPage transitionPhase = 2 DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { transitionPhase = 0 } } } // 添加手指上下滑动切换手势 .gesture( DragGesture(minimumDistance: 20) .onEnded { gesture in let translation = gesture.translation.height if translation > 50 && currentPage < pages.count - 1 { currentPage += 1 crownRotation = Double(currentPage) } else if translation < -50 && currentPage > 0 { currentPage -= 1 crownRotation = Double(currentPage) } } ) } } .background(Color.black) } } // 预览 struct WorkoutTabView_Previews: PreviewProvider { static var previews: some View { WorkoutTabView() .previewDevice("Apple Watch Series 8 - 41mm") } }
3. 关键优化点
- 抛弃旋转实现:用ZStack叠加页面,布局更符合WatchOS的原生视图逻辑,避免旋转带来的布局错乱问题
- Digital Crown绑定:通过
digitalCrownRotation修饰符直接绑定页面索引,自带触觉反馈,无需手动处理旋转逻辑 - 原生过渡动画:模拟Workout应用的绿色高亮→淡出效果,通过状态变量控制动画阶段,过渡更自然
- 双切换方式:同时支持手指上下滑动和Digital Crown滚动,和原生应用体验一致
- 固定顶部区域:顶部时间栏独立于页面区域,始终保持显示
内容的提问来源于stack exchange,提问作者braza
相关产品推荐
相关产品推荐

