我正在尝试使用 Apple 正在采取的类似方法来创建自定义
Picker
。简而言之,我试图将选定的 Text
设为蓝色,将未选定的 Text
设为黑色。
所以我已经走到这一步了(参见下面的代码)。
我感觉我已经很接近了,但我错过了最后一块拼图。
struct ContentView: View {
@State private var selectionIndex: Int = 2
var body: some View {
CustomPicker(selection: $selectionIndex) {
Text("1").tag(1)
Text("2").tag(2)
}
}
}
struct CustomPicker<T: Hashable>: View {
@Binding var selection: T
init<Content: View>(selection: Binding<T>, @ViewBuilder content: () -> TupleView<(Content)>) {
self._selection = selection
let content = content() //<-- How to store content to be used in body?
}
init<each Content: View>(selection: Binding<T>, @ViewBuilder content: () -> TupleView<(repeat each Content)>) {
self._selection = selection
let content = content() //<-- How to store content to be used in body?
repeat print(newView(each content.value)) //<-- How to create a new (altered) TupleView?
}
var body: some View {
return Text("Done") //<-- Should return the altered content
}
func newView(_ view: some View) -> some View {
if let tag = tag(view), tag == self.selection {
return view.foregroundColor(.blue)
} else {
return view.foregroundColor(.black)
}
}
func tag(_ view: some View) -> T? {
Mirror(reflecting: view).descendant("modifier", "value", "tagged") as? T
}
}
#Preview {
ContentView()
}
您可以使用 View Extractor 从
ViewBuilder
中获取视图。请注意,这使用了不稳定的 API。
这是一个例子:
struct ContentView: View {
@State private var selectionIndex: Int = 2
var body: some View {
CustomPicker(selection: $selectionIndex) {
Text("1").id(1)
Text("2").id(2)
}
}
}
struct CustomPicker<Content: View, Selection: Hashable>: View {
let content: Content
@Binding var selection: Selection
init(selection: Binding<Selection>, @ViewBuilder content: () -> Content) {
self.content = content()
self._selection = selection
}
var body: some View {
HStack {
ExtractMulti(content) { views in
ForEach(views) { view in
let tag = view.id(as: Selection.self)
Button {
if let tag {
selection = tag
}
} label: { view }
.foregroundStyle(selection == tag ? .blue : .black)
}
}
}
}
}
注意,这里我使用
id
而不是 tag
来标识视图,因为这样更方便。 tag
的视图特征键是内部类型,因此很难访问它。您可以尝试找到镜像路径,但编写自己的视图特征键可能会更容易:
struct CustomTagTrait<V: Hashable>: _ViewTraitKey {
static var defaultValue: V? { nil }
}
extension View {
func customTag<V: Hashable>(_ tag: V) -> some View {
_trait(CustomTagTrait<V>.self, tag)
}
}
...
CustomPicker(selection: $selectionIndex) {
Text("1").customTag(1)
Text("2").customTag(2)
}
然后你可以这样获取标签:
let tag = view[CustomTagTrait<Selection>.self]