【问题标题】:@EnvironmentObject property not working properly in swiftUI@EnvironmentObject 属性在 swiftUI 中无法正常工作
【发布时间】:2021-12-06 06:14:22
【问题描述】:

从 ViewModel 更新 cartArray 不会附加到当前元素,而是每次都新添加对象。我需要将 cartArray 维护为全局数组,以便可以从项目的任何视图访问它。我正在从 ViewModel 向 cartArray 添加元素。我采用了一个单独的类 DataStorage,其中包含可以通过项目访问的对象

Example_AppApp.swift
import SwiftUI

@main
struct Example_AppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(DataStorage())
        }
    }
}
DataStorage.swift

import Foundation

class DataStorage: ObservableObject {
    @Published var cartArray = [Book]()
}
ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var showSheetView = false

    var body: some View {
        NavigationView{
            
        ListViewDisplay()
           .navigationBarItems(trailing:
                    Button(action: {
                        self.showSheetView.toggle()
                    }) {
                        Image(systemName: "cart.circle.fill")
                            .font(Font.system(.title))
                    }
                )
            
            
 
        }.sheet(isPresented: $showSheetView) {
            View3()
        }
        
    }
}



struct ListViewDisplay: View{
    
    
    var book = [
       Book(bookId: 1 ,bookName: "Catch-22"),
       Book(bookId: 2 ,bookName: "Just-Shocking" ),
       Book(bookId: 3 ,bookName: "Stephen King" ),
       Book(bookId: 4,bookName: "A Gentleman in Moscow"),
    ]
    
    var body: some View {
        List(book, id: \.id) { book in
            Text(book.bookName)

            NavigationLink(destination: View1(book: book)) {
            
        }
        }
        
}
}



View1Modal.swift

import Foundation

struct Book: Codable, Identifiable {
    var id:String{bookName}  
    var bookId : Int
    var bookName: String

}
 struct BookOption: Codable{
    var name: String
    var price: Int
}

View1ViewModel.swift
import Foundation
import Combine


class View1ViewModel : ObservableObject{
    
    var dataStorage = DataStorage()
   
    func addBook (bookId:Int ,bookName : String){
           dataStorage.cartArray.append(Book(bookId:bookId, bookName: bookName)) // Adding to global array
        print(dataStorage.cartArray)
    }
  
}

View1.swift
import SwiftUI

struct View1: View {
    
    @ObservedObject var vwModel = View1ViewModel()
    @EnvironmentObject var datastrg: DataStorage
   
    var book:Book
    
    var body: some View {
        
        Text(book.bookName).font(.title)
        Spacer()
        Button(action: {
            vwModel.addBook(bookId: book.bookId, bookName: book.bookName)
            
        }, label: {
            Text("Add Book to Cart")
            
                .frame(maxWidth: .infinity, minHeight: 60)
                .background(Color.red)
                .foregroundColor(Color.white)
                .font(.custom("OpenSans-Bold", size: 24))
            
        })
        
 
        
    }
}

View3.swift

import SwiftUI

struct View3: View {
    @EnvironmentObject var datastorage : DataStorage
    var body: some View {
        NavigationView {
            List(datastorage.cartArray,id:\.id){book in

   
                    VStack{
                    Text(book.bookName)
                        .font(.custom("OpenSans-Bold", size: 20))
                          
                        }
                    
            }
            
                    .navigationBarTitle(Text("Cart"), displayMode: .inline)
                }
    }
}

第一次调用 addBook 函数时,它会打印为

[Example_App.Book(bookId: 1, bookName: "Catch-22")]

当我返回此 View1 并通过调用 addBook func 添加另一本书时,它将作为新对象添加到 cartArray

[Example_App.Book(bookId: 3, bookName: "Stephen King")]

打印cartArray 中的元素数量为1 element 而不是2 elements。当我转到 View3 并显示 Books in list 时,cartArray 显示为 empty(0 elements)

我认为 ViewModel 类中的 var dataStorage = DataStorage() 有问题。每次都是新创建的,因此不会存储先前的值。但我不明白如何保持它的状态 如何在 View3 中显示列表?任何想法/建议都会有所帮助

【问题讨论】:

  • 请包含一个可以编译的minimal reproducible example。没有它,就不清楚这里到底发生了什么。
  • @jnpdx 请立即检查。我还举例说明了它是如何打印 cartArray 的
  • 请包含可编译代码。如果你将你拥有的东西复制并粘贴到 Xcode 中,会有很多东西因为缺少代码或结构不清晰而无法编译。那我就可以给出答案了。您在寻找单例解决方案还是EnvironmentObject 解决方案?在您的代码中,您似乎正在尝试使用 EnvironmentObject,但您引用了其他 cmets 中的单例(而不是您评论的我的答案)。
  • 另外,同时还有一个指针:任何时候你做DataStorage()(带括号),你正在创建一个DataStorage新实例——你需要通过 EnvironmentObject 或单例共享 一个实例
  • 另外值得注意的是:EnvironmentObject 解决方案可能比单例解决方案更具可测试性。

标签: swift iphone xcode swiftui


【解决方案1】:

您需要有一个 一个实例 DataStorage 被传递。任何时候你写DataStorage() 都会创建一个新实例

.environmentObject 将允许您将该实例注入到视图层次结构中。然后,您可以使用@EnvironmentObject 属性包装器在View 中访问它。

View1 中,我使用onAppearView1ViewModel 上设置dataStorage 属性——这意味着它必须是View1ViewModel 上的一个可选属性,因为它不会在init 中设置。我避免在init 中设置它的原因是因为@EnvironmentObject 没有在Viewinit 中设置——它在渲染时被注入。

@main
struct Example_AppApp: App {
    var dataStorage = DataStorage()
    
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(dataStorage)
        }
    }
}

class DataStorage: ObservableObject {
    @Published var cartArray = [Book]()
}

struct ContentView: View {
    @State var showSheetView = false
    
    var body: some View {
        NavigationView{
            
            ListViewDisplay()
                .navigationBarItems(trailing:
                                        Button(action: {
                    self.showSheetView.toggle()
                }) {
                    Image(systemName: "cart.circle.fill")
                        .font(Font.system(.title))
                }
                )
        }.sheet(isPresented: $showSheetView) {
            View3()
        }
    }
}

struct ListViewDisplay: View {
    var book = [
        Book(bookId: 1 ,bookName: "Catch-22"),
        Book(bookId: 2 ,bookName: "Just-Shocking" ),
        Book(bookId: 3 ,bookName: "Stephen King" ),
        Book(bookId: 4,bookName: "A Gentleman in Moscow"),
    ]
    
    var body: some View {
        List(book, id: \.id) { book in
            Text(book.bookName)
            NavigationLink(destination: View1(book: book)) {
                
            }
        }
        
    }
}

struct Book: Codable, Identifiable {
    var id:String{bookName}
    var bookId : Int
    var bookName: String
    
}

struct BookOption: Codable{
    var name: String
    var price: Int
}

class View1ViewModel : ObservableObject{
    
    var dataStorage : DataStorage?
    
    func addBook (bookId:Int ,bookName : String) {
        guard let dataStorage = dataStorage else {
            fatalError("DataStorage not set")
        }
        dataStorage.cartArray.append(Book(bookId:bookId, bookName: bookName)) // Adding to global array
        print(dataStorage.cartArray)
    }    
}

struct View1: View {
    
    @ObservedObject var vwModel = View1ViewModel()
    @EnvironmentObject var datastrg: DataStorage
    
    var book:Book
    
    var body: some View {
        
        Text(book.bookName).font(.title)
        Spacer()
        Button(action: {
            vwModel.addBook(bookId: book.bookId, bookName: book.bookName)
            
        }, label: {
            Text("Add Book to Cart")
            
                .frame(maxWidth: .infinity, minHeight: 60)
                .background(Color.red)
                .foregroundColor(Color.white)
                .font(.custom("OpenSans-Bold", size: 24))
            
        })
            .onAppear {
                vwModel.dataStorage = datastrg
            }
    }
}

struct View3: View {
    @EnvironmentObject var datastorage : DataStorage
    var body: some View {
        NavigationView {
            List(datastorage.cartArray,id:\.id){book in
                VStack{
                    Text(book.bookName)
                        .font(.custom("OpenSans-Bold", size: 20))
                    
                }
            }
            .navigationBarTitle(Text("Cart"), displayMode: .inline)
        }
    }
}

【讨论】:

  • 成功了!!谢谢你的解释
【解决方案2】:

你没有在任何地方调用你的函数 addBook,添加一个 onappear 到你的 view3 调用函数,你的列表将填充数据。

【讨论】:

  • 我正在调用 addBook 函数,每次我调用它时,它们都会被添加到 cartArray,但我认为 ViewModel 类中的 var dataStorage = DataStorage() 有问题。每次都是新鲜创建的。因此不存储先前的值。但我不明白如何保持它的状态
  • 你在代码的什么地方调用了函数?它不在您提供的代码中。一般来说,根据我的经验,如果我想在另一个类中使用它,我会创建一个类的实例。但如果可以的话,你通常应该避免这样做。
  • 我怎样才能像我们在 Obj-C AppDelegate appDelegate=(AppAppDelegate)[[UIApplication sharedApplication]delegate] 中那样为 DataStorage 创建一个共享实例对象;
猜你喜欢
  • 2020-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多