SwiftUI 声明式 UI 基础
SwiftUI 声明式 UI 基础
摘要:SwiftUI 是 Apple 推出的现代声明式 UI 框架,跨 iOS / macOS / watchOS / tvOS。本文从视图、状态、布局、导航到动画,系统讲解 SwiftUI 的核心用法,帮助你从 UIKit 平稳迁移。
一、SwiftUI 的理念
SwiftUI 用声明式语法描述界面:你告诉框架"在某个状态下 UI 长什么样",框架负责在状态变化时高效更新。相比 UIKit 的命令式 addSubview / setText,SwiftUI 代码更短、状态更清晰、跨平台复用更强。
一个最小示例:
struct ContentView: View {
var body: some View {
Text("你好,北京")
.font(.title)
.foregroundStyle(.red)
}
}
View 协议要求一个 body 计算属性,返回具体视图。视图是不可值类型(struct),轻量且高效。
二、常用基础视图
文本
Text("标题")
.font(.headline)
.fontWeight(.bold)
.foregroundColor(.primary)
Text(attributedString)
.multilineTextAlignment(.center)
.lineLimit(2)
图标
Image(systemName: "star.fill")
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
Image(systemName:) 使用 SF Symbols,无需自备素材。
按钮
Button(action: { print("点击") }) {
Label("收藏", systemImage: "heart")
}
.buttonStyle(.borderedProminent)
输入
TextField("请输入用户名", text: $username)
.textFieldStyle(.roundedBorder)
.autocapitalization(.none)
Toggle("自动登录", isOn: $autoLogin)
Slider(value: $volume, in: 0...1)
三、状态管理
@State
局部状态,值变化触发视图刷新:
struct Counter: View {
@State private var count = 0
var body: some View {
VStack {
Text("\(count)")
Button("加一") { count += 1 }
}
}
}
@Binding
子视图接收父状态的引用,可双向修改:
struct ToggleView: View {
@Binding var isOn: Bool
var body: some View {
Toggle("开关", isOn: $isOn)
}
}
调用处用 $isOn 传入。
@ObservedObject 与 @StateObject
引用类型(ObservableObject)作为外部模型:
class UserStore: ObservableObject {
@Published var name = ""
}
struct ProfileView: View {
@StateObject var store = UserStore()
var body: some View {
TextField("姓名", text: $store.name)
}
}
@StateObject 由当前视图拥有生命周期;@ObservedObject 仅观察,由外部传入。
@EnvironmentObject
全局共享,跨视图注入:
@main
struct App: SwiftUI.App {
var body: some Scene {
WindowGroup {
RootView()
.environmentObject(UserStore())
}
}
}
struct ChildView: View {
@EnvironmentObject var store: UserStore
var body: some View { Text(store.name) }
}
四、布局
VStack / HStack / ZStack
VStack(alignment: .leading, spacing: 12) {
Text("标题").font(.title)
Text("副标题").font(.caption)
}
HStack 水平,ZStack 层叠。
List 与 ForEach
List(items) { item in
HStack {
Image(systemName: item.icon)
Text(item.title)
}
}
List 自带滚动与单元格复用。ForEach 用于动态构造:
ForEach(0..<5) { i in
Text("第 \(i) 行")
}
LazyVStack / ScrollView
ScrollView {
LazyVStack(spacing: 8) {
ForEach(items) { ItemRow($0) }
}
}
LazyVStack 只创建可见项,适合长列表。
自定义布局
iOS 16+ 提供 Layout 协议,可自定义排列算法;更常用的是组合 Spacer、frame、padding:
HStack {
Text("左")
Spacer()
Text("右")
}
.padding(.horizontal, 16)
五、导航
NavigationStack
NavigationStack {
List(items) { item in
NavigationLink(value: item) {
Text(item.title)
}
}
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
}
}
iOS 16+ 推荐用 navigationDestination(for:) 配合 NavigationLink(value:),类型安全且支持深链。
TabView
TabView {
HomeView().tabItem { Label("首页", systemImage: "house") }
SettingsView().tabItem { Label("设置", systemImage: "gear") }
}
Sheet 与全屏覆盖
.sheet(isPresented: $showSheet) {
ModalView()
}
.fullScreenCover(isPresented: $showCover) {
FullView()
}
六、动画
withAnimation(.spring(response: 0.4, dampingFraction: 0.8)) {
isExpanded.toggle()
}
隐式动画:
Text("hello")
.scaleEffect(isBig ? 1.5 : 1)
.animation(.easeInOut(duration: 0.3), value: isBig)
transition 控制插入移除动画:
if showBanner {
Banner()
.transition(.move(edge: .top).combined(with: .opacity))
}
七、绘制与自定义视图
struct CircleView: View {
var body: some View {
Circle()
.fill(LinearGradient(colors: [.red, .yellow], startPoint: .top, endPoint: .bottom))
.frame(width: 100, height: 100)
}
}
复杂图形用 Canvas 或 Path:
Path { path in
path.move(to: .zero)
path.addLine(to: CGPoint(x: 50, y: 50))
}
.stroke(Color.blue, lineWidth: 2)
八、预览
#Preview {
ContentView()
.environmentObject(UserStore())
}
#Preview("深色") {
ContentView()
.preferredColorScheme(.dark)
}
Xcode 中实时渲染,无需运行 App。
九、与 UIKit 互操作
struct MapWrapper: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView { MKMapView() }
func updateUIView(_ uiView: MKMapView, context: Context) { }
}
UIViewControllerRepresentable 用于包装控制器。反之在 UIKit 中用 UIHostingController 嵌入 SwiftUI。
十、性能与避坑
body应是纯计算,避免副作用。- 大列表用
LazyVStack而非VStack。 @StateObject与@ObservedObject别混用,前者负责创建。- 避免在
body中做昂贵计算,提到模型或task中。 - 用
EquatableView或Equatable让框架跳过未变化子树。
十一、总结
SwiftUI 用值类型视图 + 声明式描述 + 自动依赖追踪,把 UI 开发简化为"状态 → 视图"的映射。掌握基础视图、状态属性、布局容器、导航与动画即可完成绝大多数界面;互操作机制保证渐进迁移。建议新页面优先用 SwiftUI,老页面按需嵌入,逐步过渡。
- 点赞
- 收藏
- 关注作者
评论(0)