文档 #
SwiftUI | Apple Developer Documentation
基础概念 #
Layout fundamentals
数据状态 #
1. @State
和 @Binding
#
https://developer.apple.com/documentation/swiftui/state
@State
是 SwiftUI 中用于声明本地状态变量的属性包装器。任何使用到 @State
变量的视图,当该变量变化时,都会重新渲染。
import SwiftUI
struct ContentView: View {
@State private var counter = 0
var body: some View {
VStack {
Text("Counter: \(counter)")
Button(action: {
counter += 1
}) {
Text("Increment Counter")
}
}
}
}
@Binding
用于在不同视图之间传递和共享 @State
数据,使得一个视图可以绑定到另一个视图的状态。
import SwiftUI
struct ParentView: View {
@State private var isPresented: Bool = false
var body: some View {
VStack {
Button(action: {
isPresented.toggle()
}) {
Text("Toggle Child View")
}
ChildView(isPresented: $isPresented)
}
}
}
struct ChildView: View {
@Binding var isPresented: Bool
var body: some View {
Text(isPresented ? "Presented!" : "Not Presented")
}
}
2. @Published
和 @ObservedObject
#
@Published
是一个属性包装器,主要在 Swift 的 Combine 框架中使用,并且与 SwiftUI 的数据驱动架构紧密集成。它用于将类的属性声明为可以被观察的,从而在属性值发生变化时发出通知给订阅者(通常是 SwiftUI 视图),以触发 UI 刷新。@ObservedObject
用于将一个符合 ObservableObject
协议的对象实例引入到视图中。当被观察对象的 @Published
属性发生变更时,系统会触发视图更新。
工作原理 #
属性声明:当一个属性使用
@Published
修饰时,这个属性会被自动封装到一个发布者中,意味着每当该属性的值发生变化时,都会向其所有订阅者发布更新。与
ObservableObject
搭配:通常,@Published
用于实现ObservableObject
协议的类中。ObservableObject
是一个可以被视图订阅的协议,表明它的状态可以随时更新。通知机制:当
@Published
修饰的属性的值改变时,它会自动通知任何观察它变化的视图刷新视图以反映新的数据。
在 SwiftUI 中,@Published
和 ObservableObject
经常一起使用,以实现响应式的用户界面。@Published
属性包装器用于标记将会被观察并可能改变的数据属性,而 ObservableObject
协议则用于在对象中的数据变化时通知视图进行刷新和更新。
下面是一些使用 @Published
和 ObservableObject
的例子:
例子 1: 简单的计数器 #
import SwiftUI
// 定义一个模型使用 ObservableObject
class CounterModel: ObservableObject {
@Published var count: Int = 0
}
struct ContentView: View {
// 使用 @ObservedObject 来观察 CounterModel 的变化
@ObservedObject var counter = CounterModel()
var body: some View {
VStack {
Text("Count: \(counter.count)")
.padding()
Button("Increase") {
counter.count += 1
}
}
}
}
在这个例子中,每当 count
变量增加时,ContentView
会自动刷新以反映新的值。
例子 2: 切换开关 #
import SwiftUI
class ToggleModel: ObservableObject {
@Published var isOn: Bool = false
}
struct ToggleView: View {
@ObservedObject var toggleModel = ToggleModel()
var body: some View {
VStack {
Toggle(isOn: $toggleModel.isOn) {
Text("Toggle is \(toggleModel.isOn ? "ON" : "OFF")")
}
.padding()
Button("Toggle manually") {
toggleModel.isOn.toggle()
}
}
}
}
在这个例子中,通过 Toggle
控件或按钮来改变 isOn
状态,ToggleView
会自动更新显示的文本。
例子 3: 复杂的表单数据 #
import SwiftUI
class UserModel: ObservableObject {
@Published var name: String = ""
@Published var age: Int = 0
}
struct UserFormView: View {
@ObservedObject var user = UserModel()
var body: some View {
Form {
Section(header: Text("User Information")) {
TextField("Name", text: $user.name)
Stepper(value: $user.age, in: 0...100) {
Text("Age: \(user.age)")
}
}
}
.navigationBarTitle("User Form")
}
}
在这个例子中,UserFormView
通过表单提供了用户信息的输入,同时响应并更新来自 UserModel
的数据变化。
通过这些例子可以看到,@Published
和 ObservableObject
结合起来使用,可以非常轻松地实现数据驱动的 UI,为用户提供即时反馈。
3. @StateObject
和 @EnvironmentObject
#
@ObservedObject
、@StateObject
和 @EnvironmentObject
是 SwiftUI 中用于处理和传递数据的属性包装器,它们之间的主要区别在于它们的作用范围和生命周期管理。
@ObservedObject
- 作用:
@ObservedObject
用于在视图内部观察和响应来自其他对象的状态变化。 - 用法:通常用于在视图间共享数据,当被观察对象的属性发生变化时,相应的视图会自动更新。
@StateObject
- 作用:
@StateObject
用于在视图内部创建和管理持久化对象或模型。 - 用法:通常用于在视图内部创建对象,并确保对象在视图销毁和重新加载时保持持久性,不会丢失。
@EnvironmentObject
- 作用:
@EnvironmentObject
用于在整个视图层次结构中共享数据模型,并将数据模型注入到子孙视图中。 - 用法:通常用于全局共享的数据模型,可在任何子孙视图中直接访问和更新该数据。
区别总结:
@ObservedObject
用于在视图内部观察和响应来自其他对象的状态变化。@StateObject
用于在视图内部创建和管理持久化对象或模型。@EnvironmentObject
用于在整个视图层次结构中共享数据模型。
示例代码:
import SwiftUI
import Combine
class UserData: ObservableObject {
@Published var name: String = "John"
}
struct ContentView: View {
@ObservedObject var observedUser = UserData()
@StateObject var stateUser = UserData()
@EnvironmentObject var environmentUser: UserData
var body: some View {
VStack {
Text("Observed: \(observedUser.name)")
Text("State: \(stateUser.name)")
Text("Environment: \(environmentUser.name)")
}
}
}
在上面的示例中,ContentView
中使用了 @ObservedObject
、@StateObject
和 @EnvironmentObject
分别观察、创建和共享 UserData
对象。每个属性包装器都有不同的作用和用法,用于处理不同的数据传递和管理需求。
4. @Bindable
与 @Observable
#
https://developer.apple.com/documentation/swiftui/bindable
@Observable
class Book: Identifiable {
var title = "Sample Book Title"
var isAvailable = true
}
struct BookEditView: View {
@Bindable var book: Book
@Environment(\.dismiss) private var dismiss
var body: some View {
Form {
TextField("Title", text: $book.title)
Toggle("Book is available", isOn: $book.isAvailable)
Button("Close") {
dismiss()
}
}
}
}
小结 #
@State
和 @Binding
是用于在单个视图或视图之间传递数据和管理状态的属性包装器,而 @Published
和 @ObservedObject
则更多用于跨视图层次结构共享数据和实现数据的响应性更新。以下是它们之间的主要区别和适用场景:
1. @State
和 @Binding
#
@State
:用于在单个视图内部管理可变状态。当状态发生变化时,视图会自动重新渲染。@Binding
:用于在不同视图间传递数据,并创建对其他视图状态的引用。可以将@State
数据绑定到其他视图的@Binding
属性上。
2. @Published
和 @ObservedObject
#
@Published
:用于在 ObservableObject 中标记需要被观察的属性,当被标记的属性发生变化时发送更新通知。@ObservedObject
:用于在视图间观察遵循ObservableObject
协议的对象的状态。当被观察对象的@Published
属性发生变化时,相应视图会自动更新。
场景和用途 #
- 如果只需要在单个视图内部管理状态或者在不同视图之间直接传递数据,可以使用
@State
和@Binding
。 - 当需要在整个应用程序或多个视图之间共享数据,让数据具有响应性更新,并且希望利用 SwiftUI 的自动更新功能时,应使用
@Published
和@ObservedObject
。
适用性举例 #
- 当您想要在两个子视图之间共享数据并同步更改时使用
@State
和@Binding
。 - 当您需要在整个应用程序中共享一个数据模型或实现数据响应性更新时使用
@Published
和@ObservedObject
。
综上所述,@State
和 @Binding
主要用于局部状态管理和视图间直接数据传递,而 @Published
和 @ObservedObject
更适用于整个应用程序范围内共享数据和实现数据响应性更新。根据具体的需求和场景,选择合适的属性包装器来实现数据管理和传递是很重要的。
可参考:
Scene #
View #
声明式语法 #
1. 不透明的返回值类型 #
2. 省略 return #
3. 尾随闭包 #
4. function builder #
vstack 定义
init(alignment: HorizontalAlignment = .center, spacing: CGFloat? = nil, content: () -> Content)
struct PracticeView: View {
var body: some View {
VStack{
Text("HELLO")
Text("hello")
}
}
}
VStack 的第三个参数,Content
闭包类型,闭包内需要返回一个 Content,我们在创建 VStack
的时候传入的闭包中只是写了2个 Text ,闭包中什么都没有返回 Content,为什么不报错呢?