SwiftUI中的多组件选择器(UIPickerView)

SwiftUI中的多组件选择器(UIPickerView),swift,uipickerview,picker,swiftui,Swift,Uipickerview,Picker,Swiftui,我正在尝试向SwiftUI应用程序添加一个三组件选择器(UIPickerView)(在传统的UIKit应用程序中,数据源将从numberOfComponents方法返回3),但我在任何地方都找不到这样的示例 我尝试添加一个由三个单组件选择器组成的HStack,但是如果它们都是一个选择器的一部分,那么前景就不一样了 在纯SwiftUI中更新答案-在本例中,数据类型为String 在Xcode 11.1上测试-在以前的版本上可能无法工作 struct MultiPicker:视图{ typealia

我正在尝试向SwiftUI应用程序添加一个三组件选择器(UIPickerView)(在传统的UIKit应用程序中,数据源将从
numberOfComponents
方法返回
3
),但我在任何地方都找不到这样的示例


我尝试添加一个由三个单组件选择器组成的HStack,但是如果它们都是一个选择器的一部分,那么前景就不一样了

在纯
SwiftUI
中更新答案-在本例中,数据类型为
String

在Xcode 11.1上测试-在以前的版本上可能无法工作

struct MultiPicker:视图{
typealias标签=字符串
typealias条目=字符串
let数据:[(标签,[条目]])]
@绑定变量选择:[条目]
var body:一些观点{
GeometryReader{中的几何体
HStack{

ForEach(0..这不是很优雅,但它不涉及移植任何UIKit东西。我知道你在回答中提到透视图是不正确的,但这里的几何图形可能修复了这一点

GeometryReader { geometry in

    HStack
    {
         Picker(selection: self.$selection, label: Text(""))
         {
              ForEach(0 ..< self.data1.count)
              {
                  Text(self.data1[$0])
                     .color(Color.white)
                     .tag($0)
              }
          }
          .pickerStyle(.wheel)
          .fixedSize(horizontal: true, vertical: true)
          .frame(width: geometry.size.width / 2, height: geometry.size.height, alignment: .center)


          Picker(selection: self.$selection2, label: Text(""))
          {
               ForEach(0 ..< self.data2.count)
               {
                   Text(self.data2[$0])
                       .color(Color.white)
                       .tag($0)
               }
          }
          .pickerStyle(.wheel)
          .fixedSize(horizontal: true, vertical: true)
          .frame(width: geometry.size.width / 2, height: geometry.size.height, alignment: .center)

    }
}
GeometryReader{geometry中的几何体
HStack
{
选择器(选择:self.$selection,标签:Text(“”))
{
ForEach(0..

使用几何图形和固定大小,如图所示,两个选择器整齐地占据了屏幕的一半宽度。现在,您只需要处理来自两个不同状态变量的选择,而不是一个,但我更喜欢这种方式,因为它将所有内容都保存在swift UI中克尔:

import SwiftUI

struct PickerView: UIViewRepresentable {
    var data: [[String]]
    @Binding var selections: [Int]

    //makeCoordinator()
    func makeCoordinator() -> PickerView.Coordinator {
        Coordinator(self)
    }

    //makeUIView(context:)
    func makeUIView(context: UIViewRepresentableContext<PickerView>) -> UIPickerView {
        let picker = UIPickerView(frame: .zero)

        picker.dataSource = context.coordinator
        picker.delegate = context.coordinator

        return picker
    }

    //updateUIView(_:context:)
    func updateUIView(_ view: UIPickerView, context: UIViewRepresentableContext<PickerView>) {
        for i in 0...(self.selections.count - 1) {
            view.selectRow(self.selections[i], inComponent: i, animated: false)
        }
    }

    class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
        var parent: PickerView

        //init(_:)
        init(_ pickerView: PickerView) {
            self.parent = pickerView
        }

        //numberOfComponents(in:)
        func numberOfComponents(in pickerView: UIPickerView) -> Int {
            return self.parent.data.count
        }

        //pickerView(_:numberOfRowsInComponent:)
        func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
            return self.parent.data[component].count
        }

        //pickerView(_:titleForRow:forComponent:)
        func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
            return self.parent.data[component][row]
        }

        //pickerView(_:didSelectRow:inComponent:)
        func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
            self.parent.selections[component] = row
        }
    }
}

import SwiftUI

struct ContentView: View {
    private let data: [[String]] = [
        Array(0...10).map { "\($0)" },
        Array(20...40).map { "\($0)" },
        Array(100...200).map { "\($0)" }
    ]

    @State private var selections: [Int] = [5, 10, 50]

    var body: some View {
        VStack {
            PickerView(data: self.data, selections: self.$selections)

            Text("\(self.data[0][self.selections[0]]) \(self.data[1][self.selections[1]]) \(self.data[2][self.selections[2]])")
        } //VStack
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
导入快捷界面
结构PickerView:UIViewRepresentable{
变量数据:[[String]]
@绑定变量选择:[Int]
//makeCoordinator()
func makeCoordinator()->PickerView.Coordinator{
协调员(自我)
}
//makeUIView(上下文:)
func makeUIView(上下文:UIViewRepresentableContext)->UIPickerView{
让picker=UIPickerView(帧:.0)
picker.dataSource=context.coordinator
picker.delegate=context.coordinator
回程选择器
}
//updateUIView(uIf:上下文:)
func updateUI视图(view:UIPickerView,context:UIViewRepresentableContext){
对于0中的i…(self.selections.count-1){
view.selectRow(self.selections[i],不完整:i,动画:false)
}
}
类协调器:NSObject、UIPickerViewDataSource、UIPickerViewDelegate{
变量父项:PickerView
//init(389;:)
init(pickerView:pickerView){
self.parent=pickerView
}
//部件数量(单位:)
func numberOfComponents(在pickerView:UIPickerView中)->Int{
返回self.parent.data.count
}
//pickerView(uuOfRowsInComponent:)
func pickerView(pickerView:UIPickerView,numberOfRowsInComponent:Int)->Int{
返回self.parent.data[component].count
}
//pickerView(uu:titleForRow:forComponent:)
func pickerView(pickerView:UIPickerView,titleForRow行:Int,forComponent组件:Int)->String{
返回self.parent.data[组件][行]
}
//pickerView(u2;:didSelectRow:不完整:)
func pickerView(pickerView:UIPickerView,didSelectRow行:Int,不完整组件:Int){
self.parent.selections[组件]=行
}
}
}
导入快捷键
结构ContentView:View{
私有let数据:[[String]]=[
数组(0…10).map{“\($0)”},
数组(20…40).map{“\($0)”},
数组(100…200).map{“\($0)”}
]
@国家私有变量选择:[Int]=[5,10,50]
var body:一些观点{
VStack{
PickerView(数据:self.data,选择:self.$selections)
文本(\(self.data[0][self.selections[0]])\(self.data[1][self.selections[1]])\(self.data[2][self.selections[2]]))
}//VStack
}
}
结构内容视图\u预览:PreviewProvider{
静态var预览:一些视图{
ContentView()
}
}
即使使用
.clipped()
,底层选取器也不会收缩,并且倾向于与其他选取器重叠。我成功剪裁底层选取器视图的唯一方法是将
.mask(Rectangle())
添加到父容器中。不要问为什么,我不知道

带有2个选择器的工作示例(小时和分钟):

GeometryReader{geometry中的几何体
HStack(间距:0){
选择器(“,选择:self.$hoursIndex){

ForEach(0..我非常喜欢woko的答案,但最终结果在视觉上有点不理想。元素感觉有点间隔,所以我将geometry.size.width乘数从2更改为5,并在选择器的两侧添加了间隔符。(我还包括了woko答案中缺失的hoursIndex和mintuesIndex变量。)

以下是在iPhone 12 Pro Max模拟器上使用Xcode 12在iOS 14上进行的测试

struct TimerView: View {
    @State private var hours = Calendar.current.component(.hour, from: Date())
    @State private var minutes = Calendar.current.component(.minute, from: Date())

    var body: some View {
        TimeEditPicker(selectedHour: $hours, selectedMinute: $minutes)
    }
}

struct TimeEditPicker: View {
    @Binding var selectedHour: Int
    @Binding var selectedMinute: Int

    var body: some View {
        GeometryReader { geometry in
            HStack(spacing: 0) {
                Spacer()
                Picker("", selection: self.$selectedHour) {
                    ForEach(0..<24) {
                        Text(String($0)).tag($0)
                    }
                }
                .labelsHidden()
                .fixedSize(horizontal: true, vertical: true)
                .frame(width: geometry.size.width / 5, height: 160)
                .clipped()

                Picker("", selection: self.$selectedMinute) {
                    ForEach(0..<60) {
                        Text(String($0)).tag($0)
                    }
                }
                .labelsHidden()
                .fixedSize(horizontal: true, vertical: true)
                .frame(width: geometry.size.width / 5, height: 160)
                .clipped()

                Spacer()
            }
        }
        .frame(height: 160)
        .mask(Rectangle())
    }
}
struct TimerView:视图{
@State private var hours=Calendar.current.component(.hour,from:Date())
@状态私有变量分钟数=Calendar.current.component(