如何使用绑定关联 Swift 枚举数组? [英] How to use Bind an Associative Swift enum array?

查看:25
本文介绍了如何使用绑定关联 Swift 枚举数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题和这个类似 -> 如何使用绑定关联 Swift 枚举?

My question is similar to this one -> How to use Bind an Associative Swift enum?

我已将提供的示例修改为数组.GroupView 接受绑定作为参数,因为我希望 GroupView 修改枚举中的数据.原始问题和这个问题的区别在于,在这个问题中,枚举是一个数组而不是单个.

I have modified the example provided to be an array. The GroupView accepts a binding as a parameter because I want the GroupView to modify the data in the enum. The difference between the original question and this one is that in this one, the enums are an array instead of a single one.

如何从枚举中提取绑定,以便 GroupView 可以正确修改枚举?这是修改后的代码

How to i extract a binding from the enums so that the GroupView can modify the enums correctly? Here is the modified code

import SwiftUI
struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()
    var body: some View {
        VStack {
            ForEach(0..<viewModel.box.instructions.count) { index -> GroupView in
                let instruction = self.viewModel.box.instructions[index]

                return GroupView(v: ????) // How do i extract the binding here???
            }
        }
    }
}
struct GroupView: View {
    @Binding var v: Group
    var body: some View {
        Button("Hello: \(self.v.groupValue)") {
            self.v.groupValue += 1
        }
    }
}
class ViewModel : ObservableObject {
    @Published var box: Box!
    init() {
        box = Box(instructions: [
            Instruction.group(Group(groupValue: 10)),
            Instruction.group(Group(groupValue: 20))
        ])
    }
}

struct Group { var groupValue: Int }
enum Instruction { case group(Group) }
struct Box { var instructions: [Instruction] }

推荐答案

好的,如果数组大小固定:

Ok, if the array is fixed in size:

ForEach(0..<viewModel.box.instructions.count) { index -> GroupView in
   return GroupView(v: self.viewModel.bindingGroup(idx: index))
}

class ViewModel : ObservableObject {
    @Published var box: Box!

    init() {
        box = Box(instructions: [
            Instruction.group(Group(groupValue: 10)),
            Instruction.group(Group(groupValue: 20))
        ])
    }

    func bindingGroup(idx: Int) -> Binding<Group> {
        return Binding<Group>(get: { () -> Group in
            if case .group(let g) = self.box.instructions[idx] {
                return g
            } else {
                return Group(groupValue: 0)
            }
        }) {
            self.box.instructions[idx] = .group($0)
        }
    }
}

如果您的阵列不是固定的,您应该从 iOS13 发行说明中考虑这一点:

If your array is not fixed, you should consider this from iOS13 release notes:

Collection 协议中的 identify(by:) 方法在支持专用的 init(:id:selection:rowContent:) 和init(:id:content:) 初始值设定项.(52976883, 52029393)

The identified(by:) method on the Collection protocol is deprecated in favor of dedicated init(:id:selection:rowContent:) and init(:id:content:) initializers. (52976883, 52029393)

追溯Int 与 Identifiable 协议的一致性被删除.更改任何依赖此一致性将 .self 传递给 id 的代码相关初始化器的参数.Int 的恒定范围继续被接受:

The retroactive conformance of Int to the Identifiable protocol is removed. Change any code that relies on this conformance to pass .self to the id parameter of the relevant initializer. Constant ranges of Int continue to be accepted:

List(0..<5) {
    Text("Rooms")
}

但是,您不应该传递在运行时发生变化的范围.如果您使用在运行时更改的变量来定义范围,列表根据初始范围显示视图和忽略对该范围的任何后续更新.

However, you shouldn’t pass a range that changes at runtime. If you use a variable that changes at runtime to define the range, the list displays views according to the initial range and ignores any subsequent updates to the range.

那么,如果你的数组大小不固定,你可能需要更多的代码:

正如我在评论中提到的.您不能使枚举可识别(如果可以,请告诉如何!).所以唯一的选择是在 ForEach 中使用 id: \.self.但是要做到这一点,我们需要使Instruction符合Hashable.

As I mentioned in the comments. You cannot make an enum identifiable (if you can, please do tell how!). So the only alternative is to use id: \.self in the ForEach. But to do that, we need to make Instruction conform to Hashable.

此外,为了获得绑定,我们需要其位置的索引.此处的解决方案 (findIndex) 可能不是性能方面的最佳选择,但我不希望您的 Instructions 数组具有数千个元素......所以应该没问题.

Also, to get the binding, we need the index of its position. The solution here (findIndex), may not be the best thing performance wise, but I don't expect your Instructions array to have thousands of elements... so that should be ok.

import SwiftUI

struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()
    var body: some View {
        VStack {
            ForEach(viewModel.box.instructions, id: \.self) { (instruction: Instruction) -> GroupView in
                let idx = self.viewModel.box.instructions.firstIndex(of: instruction)! // I am assuming it will always return a value
                return GroupView(v: self.viewModel.bindingGroup(idx: idx))
            }

            Button("Add Instruction") {
                self.viewModel.objectWillChange.send()
                self.viewModel.box.instructions.append(Instruction.group(Group(groupValue: 123)))
            }
        }
    }
}

struct GroupView: View {
    @Binding var v: Group
    var body: some View {
        Button("Hello: \(self.v.groupValue)") {
            self.v.groupValue += 1
        }
    }
}

struct Group { var groupValue: Int }
enum Instruction: Hashable {
    case group(Group)

    static func == (lhs: Instruction, rhs: Instruction) -> Bool {
        guard case .group(let gL) = lhs else { return false }
        guard case .group(let gR) = rhs else { return false }

        return gL.groupValue == gR.groupValue
    }

    func hash(into hasher: inout Hasher) {
        if case .group(let g) = self {
            hasher.combine(g.groupValue)
        }
    }
}

struct Box { var instructions: [Instruction] }

class ViewModel : ObservableObject {
    @Published var box: Box!

    init() {
        box = Box(instructions: [
            Instruction.group(Group(groupValue: 10)),
            Instruction.group(Group(groupValue: 20))
        ])
    }

    func bindingGroup(idx: Int) -> Binding<Group> {
         return Binding<Group>(get: { () -> Group in
             if case .group(let g) = self.box.instructions[idx] {
                 return g
             } else {
                 return Group(groupValue: 0)
             }
         }) {
             self.box.instructions[idx] = .group($0)
         }
     }
}

这篇关于如何使用绑定关联 Swift 枚举数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆