0

List使用一个Codable名为Package. 在某些时候,当用户点击列表中的按钮时,我需要更新项目的isFavorite值。但是我得到了. 首先,我尝试直接更改操作中的值。它没有用,所以到目前为止我得到的要点如下。PackagefavoriteCannot use mutating member on immutable value: 'package' is a 'let' constant

显然代码比这更长,但我只是想让它变得简单并删除不相关的部分。

SwiftUIView

struct AView: View {

    @ObservedObject var store: JSONController

    var body: some View {
        NavigationView {
            List(self.store.packages) { (package) in
                return self.row(package)
            }
        }
    }

    private func row(_ package: Package) -> some View {
        HStack{
            Text(package.name)
            Spacer()
            Button(action: {
                package.changeFavoriteState()
            }) {
                Image(systemName: package.isFavorite ? "star.fill" : "star")
            }.buttonStyle(PlainButtonStyle())
        }
    }
}

Package.struct

struct Package: Codable, Identifiable {

    var id: UUID
    var isFavorite: Bool

    mutating func changeFavoriteState() {
        self.isFavorite.toggle()
    }
}

PackageList.struct

struct PackageList: Codable {

    var packages: [Package]

    private enum CodingKeys: CodingKey {
        case packages
    }

    init(){
        self.packages = [Package]()
    }

    init(from decoder: Decoder) throws {
        do {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.packages = try container.decode([Package].self, forKey: .packages)
        }catch let error {
            print(error.localizedDescription+" Initializing packages with an empty array.")
            self.packages = [Package]()
        }
    }
}

JSONController.class

class JSONController: ObservableObject {

    @Published var packages: [Package] = [Package]()

    init(){
        self.readJSONFile(from: "packageList")
    }

    func readJSONFile(from url: String) {
        //stuff
    }

}

最后我得到的错误,

错误

4

2 回答 2

1

将函数的签名更改为

private func row(_ package: inout Package) -> some View

为什么这是必要的?因为当您将参数传递给函数时,该参数是一个let常量。Package所以修改它的struct任何属性意味着实际修改实例本身。你不能这样做,因为它是一个let常数。

另一件事是,因为Packageis astruct这意味着当您将它传递给您的函数时,您实际上是在传递一个副本,而不是原始值。

参数具有传入函数的inout值,由函数修改,并从函数传回以替换原始值。

于 2020-02-20T15:41:17.383 回答
1

很简单,您需要访问 List 提供的值,而不是其副本

import SwiftUI

struct Package {
    var flag: Bool
}

struct ContentView: View {
    @State var arr = [Package(flag: true), Package(flag: false), Package(flag: true)]
    var body: some View {
        List(arr.indices, id:\.self) { (index) in
            Text(self.arr[index].flag.description).onTapGesture {
                self.arr[index].flag.toggle()
            }
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

在此处输入图像描述

或使用 ObservableObject

import SwiftUI

struct Package: Identifiable {
    let id = UUID()
    var flag: Bool
}
class Model: ObservableObject {
    @Published var arr = [Package(flag: true), Package(flag: false), Package(flag: true)]
}

struct ContentView: View {
    @ObservedObject var model = Model()
    var body: some View {
        List(model.arr.indices) { (idx) in
            Text(self.model.arr[idx].flag.description)
                .onTapGesture {
                    self.model.arr[idx].flag.toggle()
            }
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

这通常是相同的......但在更复杂的情况下更可取

根据请求更新您必须重新定义行功能

import SwiftUI

struct Package: Identifiable {
    let id = UUID()
    var flag: Bool
}
class Model: ObservableObject {
    @Published var arr = [Package(flag: true), Package(flag: false), Package(flag: true)]
}

struct ContentView: View {
    @ObservedObject var model = Model()
    var body: some View {
        List(model.arr.indices) { (idx) in
            self.row(flag: self.$model.arr[idx].flag)
        }
    }

    func row(flag: Binding<Bool>)-> some View {
        Text(flag.wrappedValue.description)
            .onTapGesture {
                flag.wrappedValue.toggle()
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

或者第二个选项

import SwiftUI

struct Package: Identifiable {
    let id = UUID()
    var flag: Bool
}
class Model: ObservableObject {
    @Published var arr = [Package(flag: true), Package(flag: false), Package(flag: true)]
}

struct ContentView: View {
    @ObservedObject var model = Model()
    var body: some View {
        List(model.arr.indices) { (idx) in
            self.row(idx: idx)
        }
    }

    func row(idx: Int)-> some View {
        Text(model.arr[idx].flag.description)
            .onTapGesture {
                self.model.arr[idx].flag.toggle()
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

更新条件排序

import SwiftUI

struct Package: Identifiable {
    let id: Int
    var flag: Bool
}
class Model: ObservableObject {
    @Published var arr = [Package(id: 1, flag: true), Package(id: 3, flag: false), Package(id: 2, flag: true)]
    @Published var ascending = false

    var sorted: [Package] {
        arr.sorted { (p0, p1) -> Bool in
            let sort = (p0.id < p1.id)
            return ascending ? !sort : sort
        }
    }
}

struct ContentView: View {
    @ObservedObject var model = Model()

    var body: some View {

        VStack {
            Toggle(isOn: $model.ascending) {
                Text("ascending")
            }.padding(.horizontal)

            List(model.sorted.indices, id: \.self) { (idx) in
                self.row(idx: idx)
            }
        }
    }

    func row(idx: Int)-> some View {
        Text(verbatim: model.sorted[idx].flag.description + ": " + model.sorted[idx].id.description)
            .onTapGesture {
                if let i = self.model.arr.firstIndex (where: { (p) -> Bool in
                    self.model.sorted[idx].id == p.id
                }) {
                    self.model.arr[i].flag.toggle()
                }
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
于 2020-02-20T18:32:18.283 回答