SwiftUI:如何创建自定义选择器?

问题描述 投票:0回答:1

我正在尝试使用 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()
}
swiftui picker viewbuilder
1个回答
0
投票

您可以使用 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]
© www.soinside.com 2019 - 2024. All rights reserved.