把数据流方向想清楚
SwiftUI 界面是状态的函数:状态一变,界面自动重画。进阶的第一课是弄清楚数据从哪来、往哪去。子视图不该自己造数据,而由父视图传下来;子视图要改数据,要么回调要么双向绑定。数据流方向单一,界面才不会互相乱改。
SwiftUI 的心智模型:声明界面长什么样,剩下的让框架在状态变化时替你更新。
这篇顺着数据流、动画、自定义布局三件事,把 SwiftUI 从入门带到进阶。
@State 到 @Observable
简单的值状态用 @State 包在视图里。稍复杂的模型对象,用 @Observable 标注整个类,视图里直接观测它的属性,属性一变用到它的视图就刷新。相比老的 ObservableObject 加 @Published,新写法少了样板。
@Observable
class Profile {
var name: String = ""
var age: Int = 0
}
视图里用 @State 持有一个 Profile 实例,或用 @Environment 从上层注入。
单向数据流与绑定
父传子用 let 属性;子要回写用 @Binding,它是一个双向连接:子视图改它,父视图的值跟着变。表单输入就是典型——TextField 绑定到父级状态,输入即时反映上去。
- @State视图内部私有状态。
- @Binding与外部双向连接。
- @Observable可观测模型对象。
- @Environment从环境注入共享值。
隐式与显式动画
SwiftUI 做动画出奇地简单:改一个状态值,包在 withAnimation 里,相关视图自动补间。这是显式动画;隐式动画则给某个视图加 .animation,它关心的值一变就自动动。
Button("展开") {
withAnimation(.easeOut(duration: 0.3)) { expanded.toggle() }
}
.frame(height: expanded ? 200 : 80)
高度从 80 到 200 平滑过渡,不用手写逐帧。
过渡与转场
视图出现消失时,用 transition 决定怎么进场退场:opacity、offset、scale 可以自由组合。配合条件渲染,一个视图的插入和移除就有了优雅的动画,而不是突然蹦出来。
NavigationStack 的推送也能加 hero 转场,让列表项平滑放大成详情。
自定义布局 Layout 协议
HStack、VStack 满足不了时,实现 Layout 协议自己量稿子。你拿到所有子视图的尺寸建议,自己决定怎么摆。流式布局、重叠卡片这种特殊排列都能这样做,并且让布局计算只发生一次。
写自定义布局前先问一句:能否用现有容器加修饰符解决?大多数界面不需要 Layout 协议,它是最后手段。
实战:一个可动效卡片
把数据流与动画串起来:点击卡片翻转展开。
struct FlipCard: View {
@State private var up = false
var body: some View {
RoundedRectangle(cornerRadius: 16)
.fill(.blue.opacity(0.2))
.frame(height: up ? 220 : 100)
.onTapGesture {
withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
up.toggle()
}
}
}
}
点一下,卡片用弹簧曲线展开;再点收起。状态、动画、布局三者配合,代码只有寥寥几行。这就是 SwiftUI 进阶的体感:想清楚数据流,剩下的动效交给框架。