SwiftUI是Apple的一个声明式框架,用于在生态系统中的所有平台上构建用户界面。无需命令式地描述步骤,开发者只需声明界面的外观,SwiftUI就会负责其渲染和更新。根据Apple Developer Documentation (2025),SwiftUI支持iOS 15+、iPadOS 15+、macOS 12+、watchOS 8+和tvOS 15+,并使用View Protocol作为所有界面组件的基础构建块。
要点
body属性 — 每个SwiftUI UI组件的基础,通过视图组合返回屏幕描述。SwiftUI是一个声明式框架,由Apple于2019年推出,用于在新项目中取代UIKit。开发者无需手动创建UIView实例并将其添加到层次结构中,而是通过实现View协议的结构来描述界面。SwiftUI自动计算当前状态和新状态之间的差异,并使用自己的渲染引擎仅重绘更改的部分。
该框架使用Swift编写,采用值语义(结构体而非类),这使得UI组件轻量且线程安全。与UIKit不同,在UIKit中UIViewController由于Objective-C运行时可能占用200+字节,而SwiftUI View只是一个几字节大小的结构体。这对于内存有限的watchOS尤其重要。
相同的View描述可以在iPhone、iPad、Mac、Apple Watch、Apple TV和Apple Vision Pro上运行。SwiftUI会根据平台调整界面:在iOS上使用触控手势,在macOS上使用键盘快捷键,在watchOS上使用Digital Crown滚动。这缩短了在多个Apple平台上发布应用的公司的开发时间,但需要针对每个平台的特定元素进行额外配置。
在SwiftUI中,每个屏幕都是一个实现View协议的结构,只有一个要求:some View类型的计算属性body。关键字some(不透明类型)隐藏了视图的具体类型,使SwiftUI能够优化渲染。在body内部,开发者使用ViewBuilder组合现成的组件——Text、Image、Button、List——将多个视图合并为一个。
struct GreetingView: View {
let name: String
var var body: some View {
VStack {
Text("你好,\(name)!")
.font(.title)
.foregroundColor(.blue)
Image(systemName: "hand.wave")
.imageScale(.large)
}
.padding()
}
}
在示例中,VStack(垂直堆栈)包含Text和Image。name值通过结构体的初始化器传递——这就是SwiftUI中无需外部DI容器的依赖注入(DI)的工作方式。每个修饰符返回应用了更改的新视图,而不改变原始视图。这是通过值类型的不可变性实现的。
ViewBuilder是一个结果构建器,使用@resultBuilder注解,可将最多10个视图合并为一个。在body内部,可以使用if/else、switch和ForEach而无需额外的包装器。ForEach与Identifiable元素一起使用——每个视图获得唯一的id,以便在插入/删除时进行正确的动画。
在SwiftUI中,状态决定屏幕上显示的内容。当状态发生变化时,SwiftUI会重新创建依赖视图的body,并将结果与之前的结果进行比较,应用差异算法。使用属性包装器(property wrappers)来存储状态——每个包装器解决自己的任务:本地状态、与子视图的连接或外部数据模型。
struct CounterView: View {
@State private var count = 0
var var body: some View {
VStack {
Text("计数器:\(count)")
Button("增加") {
count += 1
}
}
}
}
class UserViewModel: ObservableObject {
@Published var name = ""
@Published var age = 0
}
@State在View结构体内存储局部简单值(Int、String、Bool)。SwiftUI将内存从结构体转移到单独的存储空间——因此即使View是值类型,带有@State的属性也可以被修改(改变)。@ObservableObject用于具有@Published属性的类,其更改会自动通知SwiftUI需要重绘。
@Binding创建与父视图中数据源的双向连接。父视图传递$variable(投影值),子视图通过绑定读取和写入值。这允许将文本输入或开关提取到单独的组件中,同时将状态保留在父视图中。没有@Binding,每次更改都需要回调闭包来向上传递新值。
在iOS 16之前,SwiftUI中的导航基于NavigationView——一个在iPad上具有复杂行为(拆分视图、双列)的过时API。从iOS 16开始,Apple推荐NavigationStack——一个具有类型安全路由的简化替代方案。开发者定义可能路由的枚举,NavigationStack自动管理屏幕堆栈,支持深层链接和返回根视图。
enum Route: Hashable {
case detail(id: Int)
case settings
}
struct ContentView: View {
var var body: some View {
NavigationStack {
List {
NavigationLink("详情页面",
value: Route.detail(id: 42))
NavigationLink("设置",
value: Route.settings)
}
.navigationDestination(for: Route.self) { route in
switch route {
case .detail(let id): DetailView(id: id)
case .settings: SettingsView()
}
}
}
}
}
Route类型的路由:Hashable允许使用任何数据类型传递参数。navigationDestination(for:destination:)将路由类型与目标视图连接。与UIKit导航相比的优势:添加新路由时无需重绘——只需在枚举中添加case并在switch中添加处理程序即可。深层链接通过NavigationStack上的processDeepLink处理。
对于编程式转换(登录后、计时器或服务器响应后),使用@State和NavigationLink初始化器:NavigationLink(isActive: $isActive)。设置isActive = true后,无需用户触摸即可进行转换。替代方案:在NavigationStack中绑定$path数组:$path.append(Route.detail(id: 1))。
Modifier是一种返回视图修改副本的方法。与UIKit不同,在UIKit中属性的配置是通过修改现有视图完成的,而SwiftUI创建应用了更改的新值。修饰符链(chaining)通过顺序转换构建最终界面:字体→间距→颜色→阴影→手势。
Apple提供了200多个内置修饰符。最常见的有:.font()、.foregroundColor()、.padding()、.background()、.cornerRadius()、.shadow()、.opacity()、.offset()。修饰符的顺序很重要:.padding()在.background()之前会为带有间距的区域着色,之后仅着色内部区域。自定义修饰符通过ViewModifier协议创建。
修饰符可以通过三元运算符有条件地应用:.foregroundColor(isError ? .red : .primary)。动画使用.animation(.easeInOut, value: state)——动画修饰符绑定到状态的特定属性。当此属性更改时,SwiftUI会在旧值和新值之间进行动画过渡。动画支持opacity、offset、scale、rotation、大小和颜色——每个属性都有相应的AnimatableParameter。
对于自定义动画,可使用.transition(出现/消失)和.matchedGeometryEffect(元素在两个容器之间的平滑过渡)。后者用于列表中的英雄动画:列表单元格中的图标平滑地转变为详情屏幕上的大图像。
在SwiftUI和UIKit之间选择是iOS开发者的首要难题之一。两个框架都得到Apple的支持,但以根本不同的方式解决构建界面的任务:SwiftUI采用声明式,UIKit采用命令式。差异体现在状态管理、导航、性能和兼容性方面。
| 方面 | SwiftUI | UIKit |
|---|---|---|
| 方法 | 声明式:显示什么 | 命令式:如何构建 |
| 状态 | Property Wrappers,自动重绘 | 手动:reloadData、setNeedsLayout |
| UI代码 | 简洁,修饰符链 | 冗长,NSCoder/Storyboard/约束 |
| 性能 | iOS 17+上高,diff算法 | iOS 12–16上峰值,直接控制 |
| 最低版本 | iOS 15+(完全支持) | iOS 2+(所有版本) |
对于最低版本为iOS 17的新项目,Apple推荐SwiftUI作为主要框架。对于需要精细控制渲染的界面(自定义UICollectionViewLayout、复杂的CAAnimation场景)或支持iOS 12–14,UIKit仍然是必需的。许多项目采用混合方法:SwiftUI通过UIHostingController嵌入到UIKit应用中,而UIViewRepresentable允许在SwiftUI层次结构中使用UIKit组件。
常见问题
可以,通过UIHostingController(SwiftUI在UIKit中)和UIViewRepresentable(UIKit在SwiftUI中)。这是一种混合方法,在迁移时很流行。
iOS 17 — 完整功能:NavigationStack、Observation framework、Swift Charts。iOS 15 — 生产环境的最低门槛。
最常见的原因是在后台线程上更改@Published属性。ObservableObject必须将更改发送到main actor:@MainActor class ViewModel。
通过Combine使用.debounce:Button.publisher(for: .tap) .debounce(for: .seconds(0.3), scheduler: RunLoop.main)。
支持,通过Gesture修饰符:DragGesture、LongPressGesture、MagnificationGesture、RotationGesture。通过.simultaneousGesture()和.sequenced()组合它们。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。