问题
I'm trying to add a three-component Picker (UIPickerView) to a SwiftUI app (in a traditional UIKit app, the data source would return 3
from the numberOfComponents
method), but I can't find an example of this anywhere.
I've tried adding an HStack of three single-component Pickers, but the perspective is off from what it would be if they were all part of a single Picker.
回答1:
Updated answer in pure SwiftUI
- in this example the data is of type String
.
Tested on Xcode 11.1 - may not work on previous versions.
struct MultiPicker: View {
typealias Label = String
typealias Entry = String
let data: [ (Label, [Entry]) ]
@Binding var selection: [Entry]
var body: some View {
GeometryReader { geometry in
HStack {
ForEach(0..<self.data.count) { column in
Picker(self.data[column].0, selection: self.$selection[column]) {
ForEach(0..<self.data[column].1.count) { row in
Text(verbatim: self.data[column].1[row])
.tag(self.data[column].1[row])
}
}
.pickerStyle(WheelPickerStyle())
.frame(width: geometry.size.width / CGFloat(self.data.count), height: geometry.size.height)
.clipped()
}
}
}
}
}
Demo:
struct ContentView: View {
@State var data: [(String, [String])] = [
("One", Array(0...10).map { "\($0)" }),
("Two", Array(20...40).map { "\($0)" }),
("Three", Array(100...200).map { "\($0)" })
]
@State var selection: [String] = [0, 20, 100].map { "\($0)" }
var body: some View {
VStack(alignment: .center) {
Text(verbatim: "Selection: \(selection)")
MultiPicker(data: data, selection: $selection).frame(height: 300)
}
}
}
Result:
回答2:
This isn't quite as elegant but it doesn't involve porting over any UIKit stuff. I know you mentioned perspective was off in your answer but perhaps the geometry here fixes that
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)
}
}
Using the geometry and fixing the size like this shows the two pickers neatly taking up half the width of the screen in each half. Now you just need to handle selection from two different state variables instead of one but I prefer this way as it keeps everything in swift UI
回答3:
Here's an adaptation of the solutions above, using the UIKit picker:
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()
}
}
来源:https://stackoverflow.com/questions/56567539/multi-component-picker-uipickerview-in-swiftui