SwiftUI 声明式 UI 基础

举报
shenlan9755 发表于 2026/09/26 11:33:09 2026/09/26
【摘要】 SwiftUI 声明式 UI 基础摘要:SwiftUI 是 Apple 推出的现代声明式 UI 框架,跨 iOS / macOS / watchOS / tvOS。本文从视图、状态、布局、导航到动画,系统讲解 SwiftUI 的核心用法,帮助你从 UIKit 平稳迁移。 一、SwiftUI 的理念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。

十、性能与避坑

  1. body 应是纯计算,避免副作用。
  2. 大列表用 LazyVStack 而非 VStack。
  3. @StateObject 与 @ObservedObject 别混用,前者负责创建。
  4. 避免在 body 中做昂贵计算,提到模型或 task 中。
  5. 用 EquatableView 或 Equatable 让框架跳过未变化子树。

十一、总结

SwiftUI 用值类型视图 + 声明式描述 + 自动依赖追踪,把 UI 开发简化为"状态 → 视图"的映射。掌握基础视图、状态属性、布局容器、导航与动画即可完成绝大多数界面;互操作机制保证渐进迁移。建议新页面优先用 SwiftUI,老页面按需嵌入,逐步过渡。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。