SwiftUI: view之间传递数据的几种方法

2,183 阅读4分钟

在 SwiftUI 中进行数值传递有多种方式,以下是其中的几种常用方式:

使用 @State 和 @Binding 属性包装器

使用 @State 属性包装器可以在视图内部存储和管理状态,而使用 @Binding 属性包装器可以将状态传递给另一个视图并使其能够修改该状态。

例如,我们可以创建一个包含一个按钮和一个文本视图的视图,并将文本视图的文本设置为状态变量的值。当用户点击按钮时,我们将修改该状态变量的值:

struct MyView: View {
    @State private var count = 0
    
    var body: some View {
        VStack {
            Text("(count)")
            Button("Increment") {
                count += 1
            }
        }
    }
}

现在,如果我们想将 MyView 的计数值传递给另一个视图,我们可以将其封装在 @Binding 属性包装器中:

struct MyView: View {
    @State private var count = 0
    
    var body: some View {
        VStack {
            Text("(count)")
            AnotherView(count: $count)
        }
    }
}

struct AnotherView: View {
    @Binding var count: Int
    
    var body: some View {
        Text("The count is (count)")
    }
}

在上面的代码中,我们将 count 属性封装在 @Binding 属性包装器中,并将其作为参数传递给 AnotherView。现在,当我们在 MyView 中修改 count 的值时,AnotherView 的文本也会相应地更新。

使用环境对象

SwiftUI 中的环境对象是一种特殊的对象,用于在整个应用程序中共享数据和设置。我们可以创建自定义环境对象,并在其中存储需要共享的状态。然后,我们可以在需要访问该状态的视图中将该环境对象注入到视图层次结构中。

例如,我们可以创建一个 UserSettings 环境对象,并在其中存储应用程序的主题设置:

class UserSettings: ObservableObject {
    @Published var theme: String = "Light"
}

struct ContentView: View {
    @EnvironmentObject var settings: UserSettings
    
    var body: some View {
        VStack {
            Text("Current theme: (settings.theme)")
            AnotherView()
        }
    }
}

struct AnotherView: View {
    @EnvironmentObject var settings: UserSettings
    
    var body: some View {
        Button("Switch theme") {
            settings.theme = "Dark"
        }
    }
}

在上面的代码中,我们创建了一个名为 UserSettings 的环境对象,并在 ContentView 中将其注入。然后,我们在 AnotherView 中也将其注入,并在按钮的动作中修改了 theme 属性的值。由于 UserSettings 是一个环境对象,因此 ContentViewAnotherView 都可以访问和修改它的值。

使用 @ObservedObject 属性包装器

使用 @ObservedObject 属性包装器可以将一个具有 ObservableObject 协议的对象注入到视图中,并在该对象发生更改时自动更新视图。

例如,我们可以创建一个 UserData 类,该类实现了 ObservableObject 协议:

class UserData: ObservableObject {
    @Published var name = "John"
    @Published var age = 30
}

然后,我们可以创建一个视图并使用 @ObservedObject 属性包装器将 UserData 对象注入到该视图中:

struct ContentView: View {
    @ObservedObject var userData = UserData()
    
    var body: some View {
        VStack {
            Text("Name: (userData.name)")
            Text("Age: (userData.age)")
            AnotherView(userData: userData)
        }
    }
}

struct AnotherView: View {
    @ObservedObject var userData: UserData
    
    var body: some View {
        Button("Change name") {
            userData.name = "Jane"
        }
    }
}

在上面的代码中,我们将 UserData 对象注入到 ContentViewAnotherView 中,并在 ContentView 中显示其名称和年龄。然后,我们在 AnotherView 中创建了一个按钮,当用户点击该按钮时,会修改 userData 对象的名称属性。由于 userData 是一个可观察对象,因此 ContentView 中的文本也会自动更新。

使用环境键和环境值

使用环境键和环境值,我们可以创建一个可以在整个视图层次结构中共享的单个值或对象。

例如,我们可以创建一个 Settings 结构体,其中包含我们要共享的任何设置值:

struct Settings {
    var theme: String
    var fontSize: Int
}

然后,我们可以使用 EnvironmentKeyEnvironmentValues 协议为此设置创建一个环境键和环境值:

struct SettingsKey: EnvironmentKey {
    static let defaultValue = Settings(theme: "Light", fontSize: 16)
}

extension EnvironmentValues {
    var settings: Settings {
        get { self[SettingsKey.self] }
        set { self[SettingsKey.self] = newValue }
    }
}

现在,我们可以将 Settings 对象注入到视图层次结构中,并在需要访问设置值的视图中使用 @Environment 属性包装器来读取该值:

struct ContentView: View {
    @Environment(.settings) var settings
    
    var body: some View {
        VStack {
            Text("Theme: (settings.theme)")
            Text("Font size: (settings.fontSize)")
            AnotherView()
        }
    }
}

struct AnotherView: View {
    @Environment(.settings) var settings
    
    var body: some View {
        Button("Change theme") {
            settings.theme = "Dark"
        }
    }
}

在上面的代码中,我们在 ContentView 中使用 @Environment 属性包装器来读取 settings 环境值,并在 AnotherView 中修改 theme 属性的值。由于 settings 是一个环境值,因此 ContentViewAnotherView 中都可以访问 settings 的值,即使它们不是 ContentView 的子视图。

以上是在 SwiftUI 中进行数值传递的几种常见方式,你可以根据具体情况选择最适合你的方法。