【问题标题】:SwiftUI: Data sharing between separate viewsSwiftUI:不同视图之间的数据共享
【发布时间】:2021-03-12 07:28:15
【问题描述】:

在视图之间共享变量的最佳做法是什么? 我的应用程序只有一个视图。但是随着它变得越来越复杂,我认为我应该将它分成几个视图。还要分开方法。 我从这样的事情开始:

struct ContentView: View {
    @State var str: String = "String"
    var body: some View {
        VStack(alignment: .leading) {
            Text(str)
            TextField("Input", text: $str)
            Button("button", action: { doSomething() })
        }.padding()
    }
    func doSomething() {
        str = str + " " + str
    }
}

想去那里:

class GlobalVars: ObservableObject {
    @Published var str: String = "Initial String"
}
struct ContentView: View {
    @ObservedObject var globalvars = GlobalVars()
    var body: some View {
        VStack(alignment: .leading) {
            DisplayView()
            EditView()
            ControlView()
        }.padding()
    }
}
struct DisplayView: View {
    @Binding var str:  String
    var body: some View {
        Text(self.globalvars.str)
    }
}
struct EditView: View {
    @Binding var str:  String
    var body: some View {
        TextField("Input", text: self.$str)
    }
}
struct ControlView: View {
    @Binding var str:  String
    var body: some View {
        Button("button", action: { doSomething() })
    }
}
func doSomething() {
    @Binding var str:  String
    self.str = self.str + " " + self.str
}

我尝试使用@Published、@ObservedObject 和@Binding。但不明白。提前感谢您的任何指点。

【问题讨论】:

    标签: swiftui data-sharing code-separation


    【解决方案1】:

    有很多方法可以解决这个问题。

    我的选择可能是将绑定仅传递给您需要访问的变量。可能看起来像这样:

    class GlobalVars: ObservableObject {
        @Published var str: String = "Initial String"
    }
    struct ContentView: View {
        @ObservedObject var globalvars = GlobalVars()
        var body: some View {
            VStack(alignment: .leading) {
                DisplayView(str: globalvars.str) //don't need a binding here since it doesn't get modified
                EditView(str: $globalvars.str)
                ControlView(str: $globalvars.str)
            }.padding()
        }
    }
    struct DisplayView: View {
        var str:  String //don't need a binding here since it doesn't get modified
        var body: some View {
            Text(str)
        }
    }
    struct EditView: View {
        @Binding var str:  String
        var body: some View {
            TextField("Input", text: $str)
        }
    }
    struct ControlView: View {
        @Binding var str:  String
        var body: some View {
            Button("button", action: { doSomething() })
        }
        
        func doSomething() {
            str = str + " " + str
        }
    }
    

    请注意,现在在ContentView 中,有一个参数传递给每个子视图,其中包含与GlobalVars str 属性的绑定(使用$ 符号)。

    另外,doSomething 被移动到 ControlView 的正文中


    您也可以使用 EnvironmentObject 来处理这个问题。我个人不太喜欢这种方法,因为我宁愿明确地看到我的参数的去向。它还使子视图可以访问整个 ObservableObject,这并不是必需的。但是,它向您展示了校长:

    class GlobalVars: ObservableObject {
        @Published var str: String = "Initial String"
    }
    struct ContentView: View {
        @ObservedObject var globalvars = GlobalVars()
        var body: some View {
            VStack(alignment: .leading) {
                DisplayView()
                EditView()
                ControlView()
            }.padding()
            .environmentObject(globalvars)
        }
    }
    struct DisplayView: View {
        @EnvironmentObject var globalvars : GlobalVars
        var body: some View {
            Text(globalvars.str)
        }
    }
    struct EditView: View {
        @EnvironmentObject var globalvars : GlobalVars
        var body: some View {
            TextField("Input", text: $globalvars.str)
        }
    }
    struct ControlView: View {
        @EnvironmentObject var globalvars : GlobalVars
        var body: some View {
            Button("button", action: { doSomething() })
        }
        
        func doSomething() {
            globalvars.str = globalvars.str + " " + globalvars.str
        }
    }
    
    

    请注意,现在,globalvars 通过与.environmentObject 一起放置在视图层次结构中传递给子级。每个子视图都可以通过声明@EnvironmentObject var globalvars : GlobalVars的属性来访问它


    您还可以做一种混合模型,在其中显式将 ObservableObject 作为参数传递给子视图:

    struct ContentView: View {
        @ObservedObject var globalvars = GlobalVars()
        var body: some View {
            VStack(alignment: .leading) {
                DisplayView(globalvars: globalvars)
            }.padding()
            .environmentObject(globalvars)
        }
    }
    
    struct DisplayView: View {
        @ObservedObject var globalvars : GlobalVars
        var body: some View {
            Text(globalvars.str)
        }
    }
    

    【讨论】:

    • 谢谢!哇,就是这样。也需要消化答案的第二部分。当我想在多个视图中使用该方法时,我该怎么做? (如果我有后续问题,我应该打开一个新问题吗?
    • “方法”是指doSomething()吗?你可以把它放在GlobalVars 中。跟进问题很好,除非它们只是关于可以快速回答的初始问题的详细信息。感谢您的提问!
    • 对不起,是的,我的意思是功能。我测试了所有 3 种方法。第一个真的很美!但也许对于复杂的场景,混合方法可能会很好。现在我需要进入我的真实应用程序。我只是对您在这里提供的顶级支持感到惊讶!有了你的两个答案,你为我节省了几天甚至几周的工作时间。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多