代码之家  ›  专栏  ›  技术社区  ›  Mark

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

  •  0
  • Mark  · 技术社区  · 2 年前

    我正在尝试创建一个自定义 Picker 使用苹果公司正在采取的类似方法。简单地说,我正在努力 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()
    }
    
    1 回复  |  直到 2 年前
        1
  •  0
  •   Sweeper    2 年前

    您可以使用 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 识别视图,因为这样更方便。的视图特征键 标签 是内部类型,因此很难访问它。您可以尝试找到镜像路径,但只需编写自己的视图特征键可能会更容易:

    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]
    
    推荐文章