[英]SwiftUI issue with changing @Binding object in the second view
我想从第二个屏幕更改绑定中的 object,但它没有像我想的那样工作。 我可以更改 object 的属性,但不能更改实际引用。 如果有人可以解释为什么会这样,实现这一目标的最佳方法是什么? 根据我的阅读,@Binding 在 View 之外不能很好地工作。 我还包括一个解决方法,我使用第二个发布的属性来更新 ui,但在我看来这不是一个好的解决方案。我是做错了什么还是@Binding 是如何工作的? (您可以复制并粘贴代码以了解我的意思)
class TestModel {
var text:String?
init(text: String?) {
self.text = text
}
}
class FirstViewVM: ObservableObject {
@Published var model:TestModel?
@Published var showSecondView = false
}
struct FirstView: View {
@StateObject var viewModel = FirstViewVM()
var body: some View {
VStack {
Text(viewModel.model?.text ?? "n/a")
.padding()
Button("New model") {
viewModel.model = TestModel(text: "New model from screen 1")
}
.padding()
Button("Show screen 2") {
viewModel.showSecondView = true
}
.padding()
}
.sheet(isPresented: $viewModel.showSecondView) {
NavigationView {
//First check the problem.. then use the workaround view to fix it....
SecondView(viewModel: SecondViewVM(model: $viewModel.model))
// SecondViewWorkaround(viewModel: SecondViewVMWorkaround(model: $viewModel.model))
}
}
}
}
class SecondViewVM: ObservableObject {
@Binding var model:TestModel?
init(model: Binding<TestModel?>) {
_model = model
}
}
struct SecondView: View {
@StateObject var viewModel:SecondViewVM
var body: some View {
VStack {
Text(viewModel.model?.text ?? "n/a")
.padding()
Button("Edit model") {
viewModel.objectWillChange.send()
viewModel.model?.text = "Edited from screen 2"
}
.padding()
//This will not update the UI
Button("New model") {
viewModel.objectWillChange.send()
viewModel.model = TestModel(text: "New model from screen 2")
}
.padding()
}
}
}
//Workaround....First check the views above..
class SecondViewVMWorkaround: ObservableObject {
@Binding var model:TestModel? {
didSet{
modelUsedForUI = model //Passing the changes to the other model that is used for ui
}
}
@Published var modelUsedForUI:TestModel? //Added another published var that will update the ui
init(model: Binding<TestModel?>) {
_model = model
modelUsedForUI = model.wrappedValue
}
}
struct SecondViewWorkaround: View {
@StateObject var viewModel:SecondViewVMWorkaround
var body: some View {
VStack {
Text(viewModel.modelUsedForUI?.text ?? "n/a")
.padding()
Button("Edit model") {
viewModel.objectWillChange.send()
viewModel.model?.text = "Edited from screen 2"
}
.padding()
Button("New model") {
viewModel.objectWillChange.send()
viewModel.model = TestModel(text: "New model from screen 2")
}
.padding()
}
}
}
我认为当您创建第二个视图 model 以传递给第二个视图并尝试链接两个视图模型而不是链接视图时,您的代码有些过于复杂。 @Binding
是传递@State
变量时在View
中使用的包装器。 您而是在两个视图模型之间使用它,我认为它无法按您的预期工作。
我建议采用不同的方法:
@ObservedObject
。下面的示例有效,您想尝试一下:
1.只有一个视图model
class TestModel {
var text:String?
init(text: String?) {
self.text = text
}
}
class FirstViewVM: ObservableObject {
@Published var model:TestModel?
@Published var showSecondView = false
// Merge here the functionalities of your second view model, or
// just create an instance, like:
// @Published var secondVM = SecondViewModel()
}
2.和3.将同一个view model从第一个view传递给第二个view
struct FirstView: View {
@StateObject var viewModel = FirstViewVM()
var body: some View {
VStack {
Text(viewModel.model?.text ?? "n/a")
.padding()
Button("New model") {
viewModel.model = TestModel(text: "New model from screen 1")
}
.padding()
Button("Show screen 2") {
viewModel.showSecondView = true
}
.padding()
}
.sheet(isPresented: $viewModel.showSecondView) {
NavigationView {
// Just pass the same model from one view to another
SecondView(viewModel: viewModel)
}
}
}
}
struct SecondView: View {
@ObservedObject var viewModel: FirstViewVM // Read your model here
var body: some View {
VStack {
Text(viewModel.model?.text ?? "n/a")
.padding()
Button("Edit model") {
viewModel.objectWillChange.send()
viewModel.model?.text = "Edited from screen 2"
}
.padding()
// Now it updates
Button("New model") {
viewModel.objectWillChange.send()
viewModel.model = TestModel(text: "New model from screen 2")
}
.padding()
}
}
}
是的,你做错了什么。 对于 Swift 和 SwiftUI,我们利用值类型的优势来解决您面临的一致性问题。 所以你的 model 应该是一个结构,例如
struct TestModel {
var text: String
init(text: String) {
self.text = text
}
}
然后使用 object 管理 model 结构的生命周期和副作用,例如
class Model: ObservableObject {
@Published var testModel: TestModel?
// funcs to load and save etc.
func newTestModel(text: String) {
testModel = TestModel(text: text)
}
}
同样在 SwiftUI 中,我们不基于屏幕设计我们的视图,我们根据他们需要的数据将它们分解。 所以他们应该是这样的:
struct ContentView: View {
@StateObject var model = Model()
@State var showSecondView = false
var body: some View {
VStack {
if let testModel = model.testModel {
ContentView2(testModel: testModel)
}
Button("New model") {
model.newTestModel(text: "from screen 1")
}
.padding()
Button("Show screen 2") {
showSecondView = true
}
.padding()
}
.sheet(isPresented: $showSecondView) {
NavigationView {
// Just pass the same model from one view to another
SecondView(model: model)
}
}
}
}
struct ContentView2: View {
let testModel: TextModel
var body: some View {
Text(testModel.text ?? "n/a")
.padding()
}
}
struct SecondView: View {
@ObservableObject var model: Model
var body: some View {
VStack {
if let testModel = model.testModel {
Button("Edit model") {
testModel.text = "Edited from screen 2"
}
.padding()
}
// Now it updates
Button("New model") {
model.newTestModel(text: "New model from screen 2")
}
.padding()
}
}
}
注意:当我们想在工作表上显示数据时,我们使用sheet(item:)
而不是 boolean 版本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.