我正在尝试构建一个 TextField,它从集合中获取其值,就像
Toggle<C>(
_ titleKey: LocalizedStringKey,
sources: C,
isOn: KeyPath<C.Element, Binding<Bool>>
) where C : RandomAccessCollection
我已构建以下内容:
struct MultiTextField<C, V, F>: View where C: RandomAccessCollection & MutableCollection,
F: ParseableFormatStyle,
V == F.FormatInput,
F.FormatOutput == String {
let titleKey: LocalizedStringKey
@Binding var sources: C
let keyPath: KeyPath<Binding<C.Element>, Binding<V>>
let format: F
@State private var value: V?
var body: some View {
TextField(titleKey,
value: $value,
format: format)
.onAppear(perform: {
print($sources.projectedValue[keyPath: keyPath])
})
}
}
#Preview {
struct Test {
var value: Double
}
@State var sources = [Test(value: 0.1234),
Test(value: 2.3456)]
return MultiTextField(titleKey: "Test",
sources: $sources,
keyPath: \.value,
format: .number)
}
文本字段本身可以工作,但我需要将集合/keyPath 的结果与文本字段使用的值耦合。当文本字段被提交或失去焦点时,我想将值写回集合(如果它有效(!= nil))。
我不知道如何对集合和关键路径做任何有用的事情,因此我不知道如何继续这里。此处所示的
print
语句会产生编译错误: Key path of type 'KeyPath<Binding<C.Element>, Binding<V>>' cannot be applied to a base of type 'Binding<C>'
如果我将
keyPath
更改为 Binding<C>
的根类型,那么我会在预览中收到错误消息,指出 [Test]
没有动态成员 value
。
我能想到的最好的办法是
keyPath
类型:WritableKeyPath<C.Element, V>
。这允许我们在 sources
上执行映射并提取有用的值,但它不允许我实际将数组中的所有值更新为新值。
如何利用集合和关键路径?
Toggle
如何访问其 sources
?
你就快到了,只需做一些小改变。
import SwiftUI
struct MultiTextField<C, V, F>: View where C: RandomAccessCollection & MutableCollection,
C.Element : Identifiable, //Add Identifiable for the ForEach
F: ParseableFormatStyle,
V == F.FormatInput,
F.FormatOutput == String {
let titleKey: LocalizedStringKey
@Binding var sources: C
let keyPath: WritableKeyPath<C.Element, V> //Binding == read/write so you need WritableKeyPath, KeyPath is read-only.
let format: F
var body: some View {
ForEach($sources, id:\.id){ $source in //C is a collection so you need a ForEach
TextField(titleKey,
value: Binding(get: { source[keyPath: keyPath] //CustomBinding
}, set: { newValue in
source[keyPath: keyPath] = newValue
}),
format: format)
}
}
}
#Preview {
struct Test: Identifiable{
var id: UUID = .init()
var value: Double
}
@State var sources = [Test(value: 0.1234),
Test(value: 2.3456)]
return MultiTextField(titleKey: "Test",
sources: $sources,
keyPath: \.value,
format: .number)
}