WWDC Quick Look 💓 By SwiftGGTeam
SwiftUI essentials

SwiftUI essentials

观看原视频

Highlight

这是一场面向 SwiftUI 入门到中级开发者的基础梳理 Session。讲者用一个「宠物 trick 排行榜」app 作为贯穿始终的示例,从最基础的 View 概念讲到 List、Navigation、自定义组件、状态管理,最后到和其他框架的集成。


核心内容

这是一场面向 SwiftUI 入门到中级开发者的基础梳理 Session。讲者用一个「宠物 trick 排行榜」app 作为贯穿始终的示例,从最基础的 View 概念讲到 List、Navigation、自定义组件、状态管理,最后到和其他框架的集成。

核心内容分三个层面。第一层是 View 的本质:SwiftUI 的 View 是声明式的描述,不是长期存在的对象实例。每次状态变化时,SwiftUI 会重新求值 View body,生成新的描述,然后高效地更新渲染。第二层是组合:通过容器视图(HStack、VStack、List)和 View Modifier 把简单视图组合成复杂界面。第三层是状态驱动:用 @State@Bindable@Environment 等属性包装器声明数据依赖,SwiftUI 自动处理 UI 更新(00:07)。

Session 还花了不少篇幅讲 SwiftUI 如何与其他框架共存——UIKit 互操作、SwiftData 持久化、Widget 扩展——强调 SwiftUI 天生支持增量采用,不需要整个 app 重写(21:08)。


详细内容

声明式 View 的本质

SwiftUI 的 View 是值类型(struct),不是长期存在的对象实例。每次状态变化时,SwiftUI 会重新求值 View body,生成新的描述,然后高效地更新渲染。这意味着你不需要手动管理视图生命周期,只需描述「UI 应该是什么样子」(00:09)。

// 基础 View 声明
HStack {
    Label("Pet Tricks", systemImage: "pawprint")
    Spacer()
    Text("Rank")
}

关键点:

  • HStack 是容器视图,水平排列子视图
  • Label 组合图标和文本,是 SwiftUI 内置的组合控件
  • Spacer 自动占据剩余空间,实现布局对齐

List 和 ForEach 的组合

List 是最常用的容器视图之一。它接受一个集合参数,内部自动创建 ForEach 视图为每个元素生成行。这意味着你不需要手动管理复用和滚动性能(02:56)。

// List 自动为集合中每个元素创建行
List(pets) { pet in
    HStack {
        Text(pet.name)
        Text(pet.trick)
    }
}

关键点:

  • List 的集合参数自动创建 ForEach 视图
  • 每个元素对应一个 HStack 行视图
  • SwiftUI 自动处理滚动性能和视图复用

View Modifier 的链式调用

View Modifier 是 SwiftUI 的另一个核心模式。它是对基础视图的「修饰」,返回一个新的修饰后的视图。多个 Modifier 可以链式调用,形成清晰的效果层次(06:17)。

// View Modifier 链式调用
Image("whiskers")
    .clipShape(Circle())
    .shadow(radius: 5)
    .overlay(Circle().stroke(Color.green, lineWidth: 2))

关键点:

  • clipShape(Circle()) 将图片裁剪为圆形
  • shadow(radius:) 添加阴影效果
  • overlay 在上方叠加绿色边框
  • Modifier 的顺序决定效果的应用顺序

自定义 View 的封装

当界面变复杂时,可以把视图层级封装成自定义 View。自定义 View 只需遵循 View 协议,提供 body 属性即可。因为 View 是值类型,拆分视图不会损害性能(07:02)。

// 自定义 View 封装
struct PetRow: View {
    let pet: Pet

    var body: some View {
        HStack {
            ProfileImage(for: pet)
            VStack(alignment: .leading) {
                Text(pet.name)
                Text(pet.trick)
            }
        }
    }

    private var profileImage: some View {
        Image(pet.imageName)
            .clipShape(Circle())
            .shadow(radius: 5)
    }
}

关键点:

  • 自定义 View 可以添加属性作为输入参数
  • private 计算属性帮助组织代码
  • View 可以在任何需要的地方复用,如 List(pets) { PetRow(pet: $0) }

状态驱动的 UI 更新

SwiftUI 的第三个核心特性是状态驱动。当状态变化时,SwiftUI 自动重新计算依赖该状态的 View body,更新 UI。这消除了手动更新 UI 的样板代码和更新遗漏 bug(08:50)。

// 状态驱动的条件渲染
struct PetRow: View {
    let pet: ObservablePet

    var body: some View {
        HStack {
            Text(pet.name)
            if pet.hasAward {
                Image(systemName: "trophy.fill")
            }
        }
    }
}

关键点:

  • pet.hasAward 变化时,SwiftUI 自动重新计算 body
  • 条件渲染自动更新,无需手动刷新
  • SwiftUI 追踪 View body 中使用的属性,建立依赖关系

@State 和 Binding

@State 创建 View 的内部状态源。Binding 是对其他 View 状态的双向引用。通过 Binding,子 View 可以修改父 View 的状态,实现状态共享(09:30)。

// @State 创建内部状态
struct RatingView: View {
    @State private var rating = 0

    var body: some View {
        HStack {
            Button("-") { rating -= 1 }
            Text("\(rating)")
            Button("+") { rating += 1 }
        }
    }
}

// Binding 共享状态
struct RatingContainer: View {
    @State private var rating = 0

    var body: some View {
        VStack {
            Gauge(value: rating, in: 0...10)
            RatingView(rating: $rating)  // 传递 Binding
        }
    }
}

struct RatingView: View {
    @Binding var rating: Int  // 接收 Binding

    var body: some View {
        HStack {
            Button("-") { rating -= 1 }
            Text("\(rating)")
            Button("+") { rating += 1 }
        }
    }
}

关键点:

  • @State 让 SwiftUI 管理属性存储
  • $ 前缀创建 Binding,实现双向绑定
  • 子 View 修改 Binding 会更新父 View 状态
  • 状态共享让多个 View 同步更新

动画集成

SwiftUI 的动画构建在状态驱动的更新机制上。用 withAnimation 包裹状态变化,SwiftUI 会自动应用默认动画。也可以自定义动画曲线和过渡效果(11:40)。

// 状态驱动的动画
Button("+") {
    withAnimation {
        rating += 1
    }
}

// 自定义过渡效果
Text("\(rating)")
    .contentTransition(.numericText())

关键点:

  • withAnimation 包装状态变化,触发动画
  • .numericText() 过渡适合数字变化
  • 动画是声明式的,无需手动控制时序

自适应控件

SwiftUI 的控件是「目的描述」而非精确视觉构造。Button 只声明「带标签的动作」,具体样式会根据上下文自动适配——在列表中是滑动操作,在菜单中是菜单项(14:09)。

// 自适应搜索
List(pets) { pet in
    PetRow(pet: pet)
}
.searchable(text: $searchText)

关键点:

  • .searchable modifier 自动添加搜索功能
  • iOS 显示覆盖列表,macOS 显示下拉菜单
  • 控件根据上下文自动适配样式

平台适配与互操作

SwiftUI 支持所有 Apple 平台。同一套代码可以自动适配不同平台的设计规范——macOS 支持多窗口,watchOS 支持数码表冠滚动,visionOS 支持空间内容(15:54)。

// watchOS 数码表冠滚动
.scoreboard
.digitalCrownRotation($rating, from: 0, through: 10)

// UIKit 互操作
struct UIKitView: UIViewRepresentable {
    func makeUIView(context: Context) -> UIViewType {
        // 创建 UIKit 视图
    }
    func updateUIView(_ uiView: UIViewType, context: Context) {
        // 更新 UIKit 视图
    }
}

关键点:

  • .digitalCrownRotation 启用数码表冠控制
  • UIViewRepresentable 在 SwiftUI 中嵌入 UIKit 视图
  • UIHostingController 在 UIKit 中嵌入 SwiftUI 视图

核心启发

  1. 从 UIKit 迁移:不要试图一次性重写。SwiftUI 的 UIViewRepresentableUIHostingController 让你可以在 UIKit 中嵌入单个 SwiftUI 视图,或反过来。找到一个独立的、边界清晰的 feature(比如设置页面、新手引导),先用 SwiftUI 实现它,验证团队的学习曲线和开发效率。

  2. 新项目直接用 SwiftUI。Apple 内部已经在大量新 app 中使用 SwiftUI,框架的成熟度已经不是问题。搭配 SwiftData 做数据持久化,用 @Query 自动同步 UI 和数据,整个技术栈都是声明式的。但要注意一些高级自定义场景可能还是需要回退到 UIKit,比如高度自定义的滚动行为、复杂的触摸事件处理。

  3. 善用 View 的值类型特性拆分代码。把大 View 拆成小 View,每个 View 接受明确的输入参数。不要在一个 View 的 body 里写 200 行代码——拆成 5 个子 View 不仅更易维护,对性能也没有负面影响。SwiftUI 会高效地更新只依赖变化状态的那些 View。


关联 Session

评论

GitHub Issues · utterances