如何从 swiftui 中的按钮修改 ViewModel 数据

问题描述 投票:0回答:1

看来我完全失去了 MVVM 数据流的概念: 我有一个简单的示例视图,显示 Doc 项目数组,由 ViewModel 类提供,该类必须是单例,可以从多个位置访问。 每个项目都有名称和一个按钮,应该更改它的“isFav”值,并通过所述按钮的颜色变化反映变化。

我尝试通过 vm 函数、struct mutating func 等直接方式来更改它,但我提出的每一个都会给我带来不同的错误。 我做错了什么?

import SwiftUI

struct Doc: Identifiable
{
    let id: UUID
    var name: String
    var isFav: Bool = false
    
    init(_ name: String)
    {
        self.id = UUID()
        self.name = name
    }
    
    init()
    {
        self.id = UUID()
        self.name = id.uuidString.dropLast(13).lowercased()
    }
    
    mutating func updateIsFav()
    {
        self.isFav.toggle()
    }
}

class ViewModel: ObservableObject
{
    static var shared = ViewModel()
    
    private init() {}
    @Published var array: [Doc] = []

    func updateIsFav(for doc: Doc)
    {
        if let index = array.firstIndex(where: { $0.id == doc.id} )
        {
            self.array[index].isFav.toggle()
        }
    }
}

struct ContentView: View
{    
    @ObservedObject var model = ViewModel.shared
    var body: some View
    {
        ZStack
        {
            Color.black.ignoresSafeArea()
            ScrollView
            {
                VStack
                {
                    ForEach( model.array, id: \.id )
                    {
                        doc in
                        HStack
                        {
                            Text( doc.name )
                                .foregroundColor(.white)
                            
                            Button(action:
                                    {
                                        doc.updateIsFav()           // Cannot use mutating member on immutable value: 'doc' is a 'let' constant
                                        doc.isFav.toggle            // Cannot reference 'mutating' method as function value
                                        model.updateisFav(for: doc) // Cannot call value of non-function type 'Binding<Subject>'
                                    },
                                   label:
                                    {
                                            Image(systemName: "star")
                                        .foregroundColor(doc.isFav ? .yellow : .white)
                                    }
                            )
                           

                        }
                    }
                }
            }
        }
        .onAppear()
        {
            model.array.append(Doc())
            model.array.append(Doc())
            model.array.append(Doc())
            model.array.append(Doc())
            model.array.append(Doc())
            model.array.append(Doc())
            model.array.append(Doc())
        }
        
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View 
    {
        ContentView()
            .previewDevice("iPhone 13")
    }
}
ios swiftui mvvm
1个回答
0
投票

为了从

Doc
的动作中变异
Button
,您需要一个
Binding<Doc>

您可以使用语法

Binding<[Doc]>
从现有
model
属性中获取
$model.array
。如果您将此
Binding
传递给
ForEach
,那么在
ForEach
内容中,您会得到
Binding<Doc>
而不是普通的
Doc

因此:

struct ContentView: View {
    @ObservedObject var model = ViewModel.shared
    var body: some View {
        List {
            ForEach($model.array, id: \.id) { $doc in
                 // ^ NEW!                    ^ NEW!           ⬅️
                HStack {
                    Text(doc.name)

                    Spacer()

                    Button {
                        doc.updateIsFav()
                        // also works now: doc.isFav.toggle()
                    } label: {
                        Image(systemName: doc.isFav ? "star.fill" : "star")
                            .foregroundColor(.yellow)
                    }
                }
            }
        }.onAppear {
            model.array.append(Doc())
            model.array.append(Doc())
            model.array.append(Doc())
            model.array.append(Doc())
            model.array.append(Doc())
            model.array.append(Doc())
            model.array.append(Doc())
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.