如何使用绑定关联 Swift 枚举数组?
How to use Bind an Associative Swift enum array?
我的问题和这个类似 ->
我已将提供的示例修改为一个数组。
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] }
好的,如果数组固定大小:
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([=11=])
}
}
}
如果您的数组不固定,您应该从 iOS13 发行说明中考虑这一点:
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)
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
.
此外,要获得绑定,我们需要其位置的索引。这里的解决方案 (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([=13=])
}
}
}
我的问题和这个类似 ->
我已将提供的示例修改为一个数组。
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] }
好的,如果数组固定大小:
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([=11=])
}
}
}
如果您的数组不固定,您应该从 iOS13 发行说明中考虑这一点:
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)
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
.
此外,要获得绑定,我们需要其位置的索引。这里的解决方案 (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([=13=])
}
}
}