我的问题与此类似-> How to use Bind an Associative Swift enum?
我将提供的示例修改为数组。
GroupView
接受绑定作为参数,因为我希望GroupView修改枚举中的数据。原始问题与这个问题的区别在于,在这个问题中,枚举是一个数组,而不是单个数组。
我如何从枚举中提取绑定,以便GroupView
可以正确地修改枚举?
这是修改后的代码
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] }
答案 0 :(得分:5)
好,如果数组大小固定:
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发行说明中考虑:
不赞成在Collection协议上使用identified(by :)方法 支持专用init(:id:selection:rowContent :)和 init(:id:content :)初始化程序。 (52976883,52029393)
追溯 删除了Int与Identifiable协议的一致性。改变任何 依赖于此一致性的代码将.self传递给id 相关初始化程序的参数。 Int的恒定范围继续 被接受:
List(0..<5) { Text("Rooms") }
但是,您不应传递在运行时更改的范围。如果使用在运行时更改的变量来定义 范围,列表将根据初始范围显示视图, 忽略对该范围的任何后续更新。
然后,如果阵列大小未固定,则可能需要更多代码:
正如我在评论中提到的。您无法使枚举可识别(如果可以,请告诉如何!)。因此,唯一的选择是在id: \.self
中使用ForEach
。但是要做到这一点,我们需要使Instruction
符合Hashable
。
此外,要获取绑定,我们需要其位置的索引。这里的解决方案(findIndex)可能并不是最佳的性能选择,但我不希望您的Instructions数组具有成千上万个元素...所以应该没事。
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)
}
}
}