WWDC Quick Look 💓 By SwiftGGTeam
Meet watchOS 10

Meet watchOS 10

观看原视频

Highlight

watchOS 10 重新设计了 Apple Watch 的整个用户界面:引入 Dial、Infographic、List 三种基础布局,用垂直分页替代水平分页,用源列表简化层级导航,并利用半透明材质和语义化背景让每一屏信息更易扫读。


核心内容

小屏幕上的 App 为什么难用

从 2014 年 Apple Watch 发布到 2023 年,硬件已经迭代了好几代——屏幕更大更亮、支持常亮显示、加入蜂窝网络和更多传感器。但 App 的设计模式没有跟上。

三个痛点一直存在。

第一,导航不统一。有的 App 用水平分页(左右滑),有的用层级跳转(push/pop),有的两种混在一起。用户在系统 App 中学到的操作方式,到了第三方 App 里就失效了。

第二,布局随意。每个 App 的按钮位置、标签大小、内容区边界都是设计师自己定的。不同 App 放在一起看,视觉上不协调。

第三,信息密度难以控制。小屏幕塞太多文字看不清,太少又浪费空间。没有一套系统规则来约束”该放多少内容在一屏上”。

watchOS 10 的设计系统重构

watchOS 10 做了一次彻底的设计系统重构。核心思路是:用显示器的物理形状来定义布局规则,而不是让每个 App 各自发挥。

Apple 定义了三种基础布局(01:40):

  • Dial(表盘布局):适合展示单一关键数值,比如计时器、秒表。信息集中在屏幕中央,周围留白。
  • Infographic(信息图布局):适合展示带上下文的复杂数据,比如 Activity 圆环、天气详情。大区域放主要内容,顶部或底部放补充信息。
  • List(列表布局):适合展示多条同类型信息,比如邮件列表、联系人、设置项。

这三种布局会自动适配 watchOS 10 支持的所有 Apple Watch 尺寸。设计师不需要自己计算像素,控件大小、位置、间距都由系统决定。

导航也统一了。Digital Crown 承担核心导航职责——旋转它在垂直分页之间切换、在列表中滚动、或者检查数据。侧边按钮单击打开控制中心,双击打开 Wallet。Home Screen 改为竖直滚动,旋转 Digital Crown 向下浏览。

材质和背景承担信息传达功能

watchOS 10 引入了新的材质(Materials)系统。不同层级使用不同透明度的半透明效果来建立视觉层次(05:03)。

状态栏现在允许内容滚动到其下方,状态栏通过半透明材质保持可读。这释放了原本浪费的屏幕空间。元素使用语义化系统颜色时,会自动根据背景材质调整对比度。

背景内容不只是装饰。World Clock 的背景显示当前时区的日照情况,Weather 的背景反映天气状况,Stocks 的背景用颜色表示涨跌趋势。Activity 中每个信息图页面的背景色匹配对应圆环的颜色——Move 红色、Exercise 绿色、Stand 蓝色(04:28)。


详细内容

垂直分页:Digital Crown 驱动的页面切换

垂直分页是 watchOS 10 最核心的导航创新(02:49)。

以前的 watchOS App 常用水平分页(左右滑动切换页面)。问题有两个:手指在小屏幕上滑动容易误触,滑动手势本身会遮挡内容。

垂直分页改用 Digital Crown 旋转来切换页面。在 Activity App 中,旋转 Digital Crown 从 Activity Rings 概览页切换到 Move 详情页,再到 Exercise 详情页,再到 Stand 详情页。页面指示器对齐 Digital Crown 的位置,在任何背景上都保持可见。

import SwiftUI

struct ActivityView: View {
    @State private var selection: ActivityTab = .overview

    enum ActivityTab: Hashable {
        case overview
        case moveDetail
        case exerciseDetail
        case standDetail
    }

    var body: some View {
        TabView(selection: $selection) {
            RingOverviewView()
                .tag(ActivityTab.overview)

            MoveDetailView()
                .tag(ActivityTab.moveDetail)

            ExerciseDetailView()
                .tag(ActivityTab.exerciseDetail)

            StandDetailView()
                .tag(ActivityTab.standDetail)
        }
        .tabViewStyle(.verticalPage)
    }
}

关键点:

  • TabView 加上 .tabViewStyle(.verticalPage) 启用垂直分页模式
  • 用户旋转 Digital Crown 在页面之间切换,不需要手指滑动
  • 每个页面的内容应该控制在一个屏幕高度内——这迫使每个页面有明确目的
  • 页面指示器自动对齐 Digital Crown 位置,系统处理适配

实现垂直分页时需要注意三个原则。

原则一:每页限制单屏高度。这个约束迫使设计师思考”这一页到底要说什么”,结果就是更容易扫读。如果内容真的需要超出,把滚动视图放在固定高度页面之后。Messages App 就是这样:第一页固定高度放置顶对话,第二页是普通对话的滚动列表。

原则二:跨页面共享的元素用动画过渡,不要复制。如果 Activity 圆环同时出现在概览页和详情页,通过缩放、位移、信息密度变化来呈现,而不是每个页面放一个新实例。这种”对象恒存”给用户提供了视觉锚点,让他们知道自己在看同一个东西的不同层面。

原则三:优先用垂直分页替代水平分页。Digital Crown 是竖直旋转的,垂直分页的物理感受更自然。

源列表导航:两层级替代多层级

源列表(Source List)是另一种新的导航模式,适合”多个实体共享同一类信息”的场景(03:36)。

以 World Clock 为例。用户关心多个城市的当地时间。源列表的设计:打开 App 直接显示当前选中城市的详情,左上角有一个源列表按钮。点击按钮弹出城市列表,选择另一个城市后回到详情页。

对比传统层级导航:传统方案是”列表 -> 点击 -> 详情 -> 返回 -> 列表”。源列表方案是”详情(默认显示)-> 点击按钮 -> 切换城市 -> 新详情”。用户大部分时间在看详情,偶尔切换城市时操作也很轻量。

import SwiftUI

struct WorldClockView: View {
    @State private var cities: [City] = [
        City(name: "San Francisco", timeZone: "PST"),
        City(name: "New York", timeZone: "EST"),
        City(name: "London", timeZone: "GMT"),
    ]
    @State private var selectedCity: City?

    var body: some View {
        NavigationSplitView {
            List(cities, selection: $selectedCity) { city in
                CityRow(city: city)
            }
        } detail: {
            if let city = selectedCity {
                CityDetailView(city: city)
            } else {
                ContentUnavailableView("Select a City", systemImage: "globe")
            }
        }
    }
}

关键点:

  • NavigationSplitView 是 watchOS 10 新增的容器,自动提供源列表导航模式
  • 左侧(sidebar)是实体列表,右侧(detail)是选中实体的详情
  • 系统自动处理导航动画和返回逻辑
  • 不需要手动管理导航栈

传统层级导航在 watchOS 10 中仍然保留,但 Apple 建议只在内容无法用源列表或垂直分页表达时使用。Settings 和 Mail 是适合层级导航的例子——它们的结构天然是多层的。watchOS 10 为层级导航引入了新的过渡动画,让用户清楚知道自己在进入哪个层级。

背景内容:颜色传达信息

背景内容在 watchOS 10 中承担信息传达功能,不是纯装饰(04:28)。

import SwiftUI

struct ActivityDetailView: View {
    var type: ActivityType

    var body: some View {
        ZStack {
            // 背景色匹配 Activity 类型,帮助用户感知位置
            type.backgroundColor
                .ignoresSafeArea()

            VStack(spacing: 12) {
                Text(type.title)
                    .font(.title3)
                    .foregroundStyle(.primary)

                Text("\(type.progress, format: .percent)")
                    .font(.system(size: 48, weight: .bold))
                    .foregroundStyle(.primary)
            }
        }
    }
}

enum ActivityType {
    case move, exercise, stand

    var backgroundColor: Color {
        switch self {
        case .move: return .red.opacity(0.15)
        case .exercise: return .green.opacity(0.15)
        case .stand: return .blue.opacity(0.15)
        }
    }

    var title: String {
        switch self {
        case .move: return "Move"
        case .exercise: return "Exercise"
        case .stand: return "Stand"
        }
    }

    var progress: Double {
        switch self {
        case .move: return 0.75
        case .exercise: return 0.5
        case .stand: return 0.83
        }
    }
}

关键点:

  • 背景色不只为好看——Move/Exercise/Stand 各自使用不同颜色,用户扫一眼就知道自己在哪个页面
  • opacity(0.15) 让背景色足够淡,前景文字始终保持可读
  • 前景文字使用 .primary 语义颜色,系统自动根据背景调整对比度
  • 背景内容从颜色、渐变到动画都可以,但必须提供额外信息

材质:释放屏幕顶部空间

watchOS 10 的材质系统用半透明效果区分层级(05:03)。

状态栏下方的内容现在可以延伸到屏幕最顶部,状态栏通过半透明材质叠加在上面保持可读。

import SwiftUI

struct WeatherDetailView: View {
    var body: some View {
        NavigationStack {
            ScrollView {
                VStack(alignment: .leading, spacing: 16) {
                    // 内容从屏幕顶部开始,不再需要避开状态栏
                    CurrentConditionsView(temperature: 72, condition: "Sunny")
                    HourlyForecastView()
                    TenDayForecastView()
                }
            }
            .toolbarBackground(.ultraThinMaterial, for: .navigationBar)
            .navigationTitle("San Francisco")
        }
    }
}

关键点:

  • .toolbarBackground(.ultraThinMaterial, for: .navigationBar) 让导航栏使用半透明材质
  • 滚动时内容从半透明区域下方穿过,最大化可用空间
  • 状态栏文字始终可读,系统自动调整颜色
  • 不需要手动为状态栏预留 padding

Digital Crown 作为数据检查工具

在不需要导航列表或切换页面的场景中,Digital Crown 可以用作数据检查工具(03:36)。

World Clock 是一个好例子。用户在详情页看到当前选中的城市时间,旋转 Digital Crown 可以前后调整时间——用来对比”如果我晚上 8 点给伦敦同事打电话,他那边是几点”。

import SwiftUI

struct WorldClockDetailView: View {
    @State private var timeOffset: TimeInterval = 0

    var body: some View {
        VStack {
            Text(adjustedTime, style: .time)
                .font(.system(size: 56, weight: .medium))
                .focusable()
                .digitalCrownRotation(
                    $timeOffset,
                    from: -12 * 3600,
                    through: 12 * 3600,
                    by: 300,
                    sensitivity: .low,
                    isContinuous: true
                )

            Text("Current: \(Date.now, style: .time)")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
    }

    var adjustedTime: Date {
        Date.now.addingTimeInterval(timeOffset)
    }
}

关键点:

  • .digitalCrownRotation 将 Digital Crown 旋转绑定到 TimeInterval
  • from:through: 定义时间偏移范围(-12 到 +12 小时)
  • by: 300 设置步长为 5 分钟
  • sensitivity: .low 让旋转不那么敏感,适合精细调整
  • isContinuous: true 允许值在到达边界后循环

核心启发

  1. 用垂直分页重新设计你的 App 信息架构:如果你的 App 目前使用水平分页或多层导航,改为垂直分页。每个页面放一个核心信息,用户旋转 Digital Crown 切换。为什么值得做:垂直分页是 watchOS 10 的原生导航模式,系统 App 全部采用。用户不需要学习新操作。怎么开始:用 TabView + .tabViewStyle(.verticalPage) 替代水平滑动,确保每个页面内容不超过一个屏幕高度。

  2. 为每个功能区域引入语义化背景:根据内容上下文切换背景色。天气 App 根据天气状况改变背景,运动 App 根据运动类型改变背景。为什么值得做:系统 App 已全部使用语义化背景,用户形成预期。颜色提供即时视觉提示,减少识别当前状态的时间。怎么开始:在 SwiftUI 中用 ZStack 放置背景层,根据数据状态动态切换背景色或渐变,透明度控制在 0.1-0.2。

  3. 用源列表替代深层导航:如果你的 App 管理多个同类实体(多个账户、设备、项目),使用源列表导航。打开 App 直接显示默认实体的详情,左上角放切换按钮。为什么值得做:减少一层导航。用户大部分时间在详情页,偶尔切换实体。怎么开始:使用 NavigationSplitView,sidebar 放实体列表,detail 放详情内容。

  4. 用材质最大化内容空间:把之前为状态栏预留的空白利用起来,让内容延伸到屏幕顶部。为什么值得做:Apple Watch 屏幕每一像素都宝贵,材质方案释放了原本浪费的空间。怎么开始:使用 .toolbarBackground(.ultraThinMaterial, for: .navigationBar) 配合 ScrollView。

  5. 用对象恒存动画连接页面:如果同一数据元素出现在多个垂直分页中,用缩放和位移动画过渡,不要放两个实例。为什么值得做:提供视觉锚点,让用户知道在看同一个东西的不同层面。怎么开始:在 SwiftUI 中使用 matchedGeometryEffect 实现跨页面共享元素过渡。


关联 Session

评论

GitHub Issues · utterances