MacOS - 使用 JSON 格式化带复选框的 SwiftUI 列表(续)

MacOS - Using JSON to format SwiftUI list with checkboxes (continued)

昨天,我问了一个相关问题。 我现在有这个脚本:

@available(OSX 11.0, *)
struct Mods_UI: View {
    @State private var falseerror = false
    @State var jsonDataList = [jsonData]()

    var body: some View {
        VStack {
            List(jsonDataList, id: \.id) { jsonDataList in
                VStack(alignment: .leading) {
                    HStack {
                        VStack(alignment: .leading) {
                            Text(jsonDataList.display)
                                .font(.title3)
                                .fontWeight(.bold)
                            Text(String(jsonDataList.description))
                                .font(.subheadline)
                        }
                        Spacer()
                        Image(systemName: jsonDataList.enabled ?? false ? "checkmark.square": "square")
                    }
                    Spacer()
                }
            }
            .onAppear(perform: loadData)
        }
    }

    func loadData() {

        guard let modsURL = URL(string: "https://raw.githubusercontent.com/nacrt/SkyblockClient-REPO/main/files/mods.json") else {
            print("Invalid URL")
            return
        }

        let task = URLSession.shared.dataTask(with: modsURL) { (data, _, error) in
            if let error = error { print(error); return }
            do {
                let result = try JSONDecoder().decode([jsonData].self, from: data!)
                jsonDataList = result
                print("Response:",jsonDataList)
            } catch {
                print(error)
            }
        }
        task.resume()
    }

}

@available(OSX 11.0, *)
struct Mods_UI_Previews: PreviewProvider {
    static var previews: some View {
        Mods_UI()
    }
}

struct jsonData: Codable, Identifiable {
    let id: String
    let display: String
    let description: String
    let url: String?
    let config: Bool?
    let enabled: Bool?
    let hidden: Bool?
    let icon: String?
    let categories: [String]?
}

我想将复选框:Image(systemName: jsonDataList.enabled ?? false ? "checkmark.square": "square") 格式化为切换:Toggle("", isOn: $jsonDataList.enabled)。我已经尝试了几种格式化它的方法,但它似乎总是 return 一个错误。我的计划是能够检查该框是否被选中,如果是,则在某处安装一个文件。

我想做的另一件事是仅显示 JSON 中具有 jsonDataList.hiddenfalse 或没有 jsonDataList.hidden 的项目.

要完成这项工作,需要做几件事。首先是您的模型可能应该 enabledvar 而不是 let —— 这样,当 Toggle 被操作时它的值可以改变:

struct jsonData: Codable, Identifiable {
    let id: String
    let display: String
    let description: String
    let url: String?
    let config: Bool?
    var enabled: Bool? //<-- Here
    let hidden: Bool?
    let icon: String?
    let categories: [String]?
}

接下来,您需要创建一个绑定以与 Toggle 一起使用。因为您的模型在一个数组中,所以 Binding 需要知道要更新哪个项目(它在数组中的索引)。您会在我的代码中看到 enabledBindingForIndex 函数。

最后,为了获得您将传递给 enabledBindingForIndex 函数的每个项目的索引,必须稍微更改 ForEach 代码以便它通过在 JSON 项 索引中。我喜欢为此使用 .enumerated()。请注意,项目的 id 现在是 .1.id,因为您将获得一个包含索引 (.0) 和项目 (.1) 的元组。我还过滤掉了同一行中的隐藏项。


struct Mods_UI: View {
    @State private var falseerror = false
    @State var jsonDataList = [jsonData]()

    func enabledBindingForIndex(index: Int) -> Binding<Bool> {
        Binding<Bool> { () -> Bool in
            return jsonDataList[index].enabled ?? false
        } set: { (newValue) in
            jsonDataList[index].enabled = newValue
        }
    }
    
    var body: some View {
        VStack {
            List(Array(jsonDataList.filter { [=11=].hidden != true }.enumerated()),  //<-- Here
                 id: \.1.id) { (index,jsonDataList) in //<-- Here
                VStack(alignment: .leading) {
                    HStack {
                        VStack(alignment: .leading) {
                            Text(jsonDataList.display)
                                .font(.title3)
                                .fontWeight(.bold)
                            Text(String(jsonDataList.description))
                                .font(.subheadline)
                        }
                        Spacer()
                        Toggle(isOn: enabledBindingForIndex(index: index)) { } //Here
                    }
                    Spacer()
                }
            }
            .onAppear(perform: loadData)
        }
    }

    func loadData() {

        guard let modsURL = URL(string: "https://raw.githubusercontent.com/nacrt/SkyblockClient-REPO/main/files/mods.json") else {
            print("Invalid URL")
            return
        }

        let task = URLSession.shared.dataTask(with: modsURL) { (data, _, error) in
            if let error = error { print(error); return }
            do {
                let result = try JSONDecoder().decode([jsonData].self, from: data!)
                jsonDataList = result
                print("Response:",jsonDataList)
            } catch {
                print(error)
            }
        }
        task.resume()
    }

}

如果您想在 Toggle 为 enabled/disabled 时产生一些副作用,您可以在 enabledBindingForIndex set 闭包中这样做。