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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 15:50:26