Лучшее решение, которое я нашел - пока что - это перенести исходный UIPickerView
в SwiftUI через UIViewRepresentable
и координатор.
Портирование компонентов UIKit в SwiftUI обсуждается в этом удивительном видео WWDC 2019:
Интеграция SwiftUI
Вот результат (см. Демонстрационный код внизу):
Осуществление
Есть две привязки, data
и selection
, которые передаются из суперпредставления.
data - это массив массивов Data
, например,
[
Array(1...100),
Array(1...100),
Array(1...100)
]
Каждый массив представляет компонент выбора.
Каждое значение в массиве представляет строку в компоненте выбора.
выделение - это массив Data
, например [10, 20, 30]
Представляет выбор сборщика (для всех компонентов).
Если одна из привязок изменяется, она вызывает перерисовку всех компонентов.
Также выбор восстанавливается.
struct CustomPicker<Data>: UIViewRepresentable where Data: Equatable {
@Binding var data: [[Data]]
@Binding var selection: [Data]
class Coordinator: NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
@Binding var data: [[Data]]
@Binding var selection: [Data]
init(data: Binding<[[Data]]>, selection: Binding<[Data]>) {
$data = data
$selection = selection
}
func pickerView(_ pickerView: UIPickerView,
numberOfRowsInComponent component: Int) -> Int {
data[component].count
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
data.count
}
func pickerView(_ pickerView: UIPickerView,
widthForComponent component: Int) -> CGFloat {
return (pickerView.superview?.bounds.width ?? 0) * 0.33
}
func pickerView(_ pickerView: UIPickerView,
rowHeightForComponent component: Int) -> CGFloat {
return 30
}
func pickerView(_ pickerView: UIPickerView,
viewForRow row: Int,
forComponent component: Int,
reusing view: UIView?) -> UIView {
guard let reusableView = view as? UILabel else {
let label = UILabel(frame: .zero)
label.backgroundColor = UIColor.red.withAlphaComponent(0.15)
label.text = "\(data[component][row])"
return label
}
reusableView.text = "\(data[component][row])"
return reusableView
}
func pickerView(_ pickerView: UIPickerView,
didSelectRow row: Int,
inComponent component: Int) {
let value = data[component][row]
selection[component] = value
}
}
func makeCoordinator() -> CustomPicker.Coordinator {
return Coordinator(data: $data,
selection: $selection)
}
func makeUIView(context: UIViewRepresentableContext<CustomPicker>) -> UIPickerView {
let picker = UIPickerView()
picker.delegate = context.coordinator
return picker
}
func updateUIView(_ uiView: UIPickerView,
context: UIViewRepresentableContext<CustomPicker>) {
uiView.reloadAllComponents()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.selection.enumerated().forEach { tuple in
let (offset, value) = tuple
let row = self.data[offset].firstIndex { $0 == value } ?? 0
uiView.selectRow(row, inComponent: offset, animated: false)
}
}
}
}
Демо
struct ContentView: View {
@State var data: [[Int]] = [
Array(0...10),
Array(20...40),
Array(100...200)
]
@State var selection: [Int] = [0, 20, 100]
var body: some View {
NavigationView {
VStack {
CustomPicker(data: self.$data,
selection: self.$selection)
Text("Selection: \(String(describing: selection))")
}
}
}
}