SwiftUI 回顾与学习

前言

在 Apple 推出 SwiftUI 之后,iOS 开发迈入了声明式 UI 的新时代。本章节将系统性地回顾 SwiftUI 的基础语法与核心概念,并标注出每个特性的 Swift 版本与最低 iOS 支持版本,帮助开发者在项目中合理规划兼容性。

SwiftUI是什么?

SwiftUI 是 Apple 在 WWDC 2019(Swift 5.1)中引入的一套声明式 UI 框架,目标是用更简洁、更可组合、更具响应式的数据驱动方式构建 UI。SwiftUI 从 iOS 13 起可用,并在后续版本中不断增强。

基本结构:View 协议与组合视图

声明一个视图
struct GreetingView: View {
    var body: some View {
        Text("Hello, SwiftUI!")
    }
}
  • View是 SwiftUI 中所有视图的根协议。
  • body属性是只读的,用于描述当前视图的UI.
  • SwiftUI 会根据数据变化自动刷新视图
组合视图

SwiftUI 提倡组合(Composition)而非继承:

struct WelcomeView: View {
    var body: some View {
        VStack {
            Text("Welcome")
                .font(.title)
            Text("SwiftUI makes UI easy")
                .foregroundColor(.gray)
        }
    }
}

  • VStack, HStack, ZStack 是基本布局容器。
  • 所有的修饰符(.font(), .foregroundColor() 等)都是返回新的视图结构。

状态驱动:@State、@Binding、@ObservedObject 等

SwiftUI 的核心理念之一是:状态驱动 UI。各种属性包装器(Property Wrappers)用于绑定数据源与视图。

@State
struct CounterView: View {
    @State private var count = 0

    var body: some View {
        Button("Clicked \(count) times") {
            count += 1
        }
    }
}

  • State 是本地状态,仅适用于当前View。
  • 每次 count变化时,body会重新计算。
    ✅ iOS 13+
    ✅ Swift 5.1+
@Binding
struct ToggleView: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle("Enable", isOn: $isOn)
    }
}

@binding 用于在父子视图之间传递可变状态。
✅ iOS 13+
✅ Swift 5.1+

@ObservedObject 和 @StateObject
class TimerModel: ObservableObject {
    @Published var time = Date()
}

struct TimerView: View {
    @ObservedObject var model: TimerModel
    var body: some View {
        Text("\(model.time)")
    }
}

  • @ObservedObject 用于观察外部传入的可观察对象。
  • @StateObject 是 iOS 14+ 提供的新属性,用于首次创建并持有生命周期的对象。

✅ @ObservedObject:iOS 13+ / Swift 5.1+
✅ @StateObject:iOS 14+ / Swift 5.3+

视图修饰符(Modifiers)

SwiftUI 中的视图修饰符是一种链式调用模式,提升了代码的可读性与组合性:

Text("Hello, World!")
    .font(.headline)
    .padding()
    .background(Color.yellow)
    .cornerRadius(8)

✅ 修饰符通用于 iOS 13+

修饰符返回新视图,不会修改原始视图本身。

条件与列表

if isLoggedIn {
    Text("Welcome Back")
} else {
    Text("Please Login")
}

SwiftUI 允许在 body 中写条件语句。

列表展示
struct ItemListView: View {
    let items = ["Apple", "Banana", "Orange"]

    var body: some View {
        List(items, id: \.self) { item in
            Text(item)
        }
    }
}

  • List 会自动提供滚动与复用机制。
  • id: 参数指定数据的唯一标识。
    ✅ List:iOS 13+
    ✅ 条件控制语法:SwiftUI 支持原生 Swift 条件表达式

生命周期与 App 协议(iOS 14+)

在 iOS 14 中,SwiftUI 引入了 @main 与 App 协议,简化 App 入口定义:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

  • 替代传统 UIApplicationDelegate.
  • 可以组合多个Scene,支持多窗口(iPadOS).
    ✅ iOS 14+
    ✅ Swift 5.3+
特性 属性包装器/组件 最低 iOS 版本 Swift 版本
@State 本地状态 iOS 13+ Swift 5.1+
@Binding 绑定父状态 iOS 13+ Swift 5.1+
@ObservedObject 外部状态观察 iOS 13+ Swift 5.1+
@StateObject 自有状态持有(推荐) iOS 14+ Swift 5.3+
App 协议 新版 App 入口 iOS 14+ Swift 5.3+
List 列表组件 iOS 13+ Swift 5.1+

在实际开发中,SwiftUI 与 UIKit 在架构思想、视图构建、状态管理、动画处理等方面有着根本的不同。本章将从以下几个关键维度出发,详细对比 SwiftUI 和 UIKit 的异同,并注释各项技术的可用最低版本和平台支持情况。

二、视图构建方式对比 SwiftUI vs UIKit

对比项 SwiftUI UIKit
声明方式 声明式(Declarative) 命令式(Imperative)
构建结构 使用 struct 和组合 View 使用 UIViewControllerUIView
最低支持版本 iOS 13+、macOS 10.15+ iOS 2.0+

示例

var body: some View {
    VStack {
        Text("Hello")
        Button("Tap Me") { /* 逻辑 */ }
    }
}
 
 
let label = UILabel()
label.text = "Hello"
let button = UIButton()
button.setTitle("Tap Me", for: .normal)
view.addSubview(label)
view.addSubview(button)

|


生命周期管理

对比项 SwiftUI UIKit
生命周期入口 @main + App 协议 (SwiftUIApp) UIApplicationDelegate, SceneDelegate
页面生命周期 onAppear, onDisappear viewDidLoad, viewWillAppear, viewDidAppear
最低支持版本 iOS 14+ (@main),iOS 13+ (SceneDelegate) iOS 2.0+

状态管理机制

对比项 SwiftUI UIKit
数据绑定 @State, @Binding, @ObservedObject, @EnvironmentObject 手动状态同步
UI 更新 自动响应数据变更 手动调用 reloadDatasetNeedsLayout
多页面数据共享 ObservableObject + @EnvironmentObject 通常通过代理、单例或闭包传递
最低支持版本 iOS 13+ iOS 2.0+

动画机制对比

对比项 SwiftUI UIKit
动画语法 .animation, .transition, .withAnimation UIView.animate(...)
控制细节 限于系统提供的组合 可通过 Core Animation 自定义
最低支持版本 iOS 13+ iOS 2.0+

SwiftUI 示例:

@State private var scale = 1.0

Button("Animate") {
    withAnimation {
        scale += 0.1
    }
}
.scaleEffect(scale)

布局系统对比

对比项 SwiftUI UIKit
布局方式 使用 VStack, HStack, ZStack, GeometryReader 使用 Auto Layout, frame, stackView
自适应布局 更加自然响应尺寸变化 通常依赖 NSLayoutConstraint 或 SnapKit 等三方库
最低支持版本 iOS 13+ iOS 6+(Auto Layout)

数据源绑定(列表)

对比项 SwiftUI UIKit
列表视图 List 组件 UITableView, UICollectionView
刷新机制 @StateObservableObject 自动刷新 手动调用 reloadData()
最低支持版本 iOS 13+ iOS 2.0+

响应式编程支持

对比项 SwiftUI UIKit
响应式内建 原生支持 Combine 框架 通常需配合 RxSwift、Combine 手动集成
最低支持版本 iOS 13+(Combine) iOS 13+(Combine),iOS 8+(RxSwift)

响应式声明 vs 命令式编程

比较项 SwiftUI(声明式) UIKit(命令式)
编程方式 声明式编程,描述 UI 的状态和依赖关系 命令式编程,逐步设置 UI 的属性与事件响应
UI 更新方式 自动追踪状态变化并刷新 UI 手动触发 reloadData 或调用 setNeedsLayout 等方法
数据驱动 使用 @State@Binding@ObservedObject 等数据绑定工具 使用 MVC/MVVM 等手动绑定数据到视图
生命周期 组件级生命周期,使用 .onAppear.onDisappear 等处理 ViewController 生命周期,如 viewDidLoadviewWillAppear
动画处理 使用 withAnimation 轻松声明动画 使用 UIView.animate(...) 手动指定动画

示例


// SwiftUI 实现按钮点击计数器
struct CounterView: View {
    @State private var count = 0
    var body: some View {
        VStack {
            Text("点击次数: \(count)")
            Button("点击我") {
                count += 1
            }
        }
    }
}

// UIKit 实现按钮点击计数器
class CounterViewController: UIViewController {
    var count = 0
    let label = UILabel()
    let button = UIButton(type: .system)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = "点击次数: 0"
        button.setTitle("点击我", for: .normal)
        button.addTarget(self, action: #selector(increaseCount), for: .touchUpInside)
        
        // 手动布局略
    }

    @objc func increaseCount() {
        count += 1
        label.text = "点击次数: \(count)"
    }
}

三、 SwiftUI与UIKit 混合开发

尽管 SwiftUI 发展迅速,许多现有项目依然是基于 UIKit 构建的。在这种情况下,掌握如何将 SwiftUI 与 UIKit 混合使用成为过渡期的重要技能。Apple 也为这种混合提供了良好的支持。

在 UIKit 中嵌入 SwiftUI

在 UIKit 项目中,可以通过 UIHostingController 将 SwiftUI 视图嵌入到任意 UIKit 层级中:

import SwiftUI

struct MySwiftUIView: View {
    var body: some View {
        Text("这是 SwiftUI 视图")
            .padding()
    }
}

// 在 UIViewController 中使用
let swiftUIView = MySwiftUIView()
let hostingController = UIHostingController(rootView: swiftUIView)
present(hostingController, animated: true)

✅ 最低支持:iOS 13
✅ 使用场景:从 UIKit 过渡到 SwiftUI 的局部页面替换、展示临时页面、弹窗等
也可以直接将其嵌入某个 UIView:

let hosting = UIHostingController(rootView: MySwiftUIView())
addChild(hosting)
view.addSubview(hosting.view)
hosting.view.frame = view.bounds
hosting.didMove(toParent: self)

在 SwiftUI 中嵌入 UIKit

在 SwiftUI 中也可以嵌入 UIKit 控件或控制器,使用的是 UIViewRepresentable 和 UIViewControllerRepresentable 协议。

嵌入 UIView 示例:
import UIKit
import SwiftUI

struct UIKitButton: UIViewRepresentable {
    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle("UIKit Button", for: .normal)
        return button
    }

    func updateUIView(_ uiView: UIButton, context: Context) {
        // 更新逻辑
    }
}

嵌入 UIViewController 示例:
struct UIKitViewControllerWrapper: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> MyViewController {
        return MyViewController()
    }

    func updateUIViewController(_ uiViewController: MyViewController, context: Context) {}
}

✅ 最低支持:iOS 13
✅ 使用场景:已有组件复用、自定义控件封装、三方库兼容、复杂动画控制等

状态共享与事件桥接

在混合开发中,SwiftUI 和 UIKit 如何共享状态或进行事件传递,是一个关键问题。

方法一:通过 Combine 桥接

使用 ObservableObject 与 @Published 可以让 UIKit 监听 SwiftUI 的状态变更,反之亦然。

class SharedModel: ObservableObject {
    @Published var isOn = false
}

  • 在 SwiftUI 中使用:@ObservedObject var model: SharedModel
  • 在 UIKit 中订阅:model.$isOn.sink { ... }
方法二:使用代理(Delegate)或闭包(Closure)

当 SwiftUI 嵌入 UIKit 或 UIKit 嵌入 SwiftUI 时,可以使用闭包作为事件回调的桥梁。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。