#swift #uikit #uipickerview
Вопрос:
Всем добрый день. Я хочу, чтобы высота одного элемента соответствовала максимальному размеру. Вот пример моего кода (который не работает):
struct ContentView: View {
private let items: [String] = [
"<|OneLineLongggggggggggggggggggggggggggggggggggggggggg|>",
"<|TwoLinesLongggggggggggggnLongggggggggggggggg|>",
"<|ThreeLinesLongggggggggggggnLonggggggggggggggggnLongggggggggggggggg|>"
]
@State private var text: String = ""
@State private var index: Int = 0
var body: some View {
BoxCustomPicker(select: self.$text, items: self.items)
}
}
struct BoxCustomPicker : View {
@Binding private var select: String
private let items: [String]
@State private var width: CGFloat = 50
@State private var height: CGFloat = 50
private var selectIndex: Binding<Int> {
.init(
get: {
if let index = self.items.firstIndex(where: { $0 == self.select }) {
return index
}
return 0
},
set: {
if self.items.count > $0 {
self.select = self.items[$0]
}
}
)
}
init(select: Binding<String>, items: [String]) {
self._select = select
self.items = items
}
var body: some View {
CustomUIPicker(width: self.$width, height: self.$height, select: self.selectIndex, items: self.items)
.background(
GeometryReader { proxy in
Color.clear
.onAppear {
let width = proxy.size.width
self.width = width
print("width: (width)")
}
}
)
}
}
struct CustomUIPicker : UIViewRepresentable {
@Binding var width: CGFloat
@Binding var height: CGFloat
@Binding var select: Int
let items: [String]
func makeCoordinator() -> Self.Coordinator {
return Self.Coordinator(self)
}
func makeUIView(context: UIViewRepresentableContext<Self>) -> UIPickerView {
let picker = UIPickerView(frame: .zero)
picker.dataSource = context.coordinator
picker.delegate = context.coordinator
return picker
}
func updateUIView(_ view: UIPickerView, context: UIViewRepresentableContext<Self>) {
print("updateUIView")
}
class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
var parent: CustomUIPicker
init(_ pickerView: CustomUIPicker) {
self.parent = pickerView
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return parent.items.count
}
func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
return self.parent.width
}
func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
print("set heightRow: (self.parent.height)")
return self.parent.height
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
let rowView: RowView = {
if let row = view as? RowView {
row.frame = CGRect(x: 0, y: 0, width: self.parent.width, height: self.parent.height)
return row
}
return .init(frame: CGRect(x: 0, y: 0, width: self.parent.width, height: self.parent.height))
}()
rowView.setText(self.parent.items[row])
let maxSize: CGSize = .init(width: self.parent.width, height: CGFloat.greatestFiniteMagnitude)
let heightRow = rowView.sizeThatFitsText(maxSize).height
if self.parent.height < heightRow {
DispatchQueue.main.async {
self.parent.height = heightRow
print("update heightRow: (heightRow)")
pickerView.reloadAllComponents()
}
}
return rowView
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.parent.select = row
}
}
}
extension CustomUIPicker.Coordinator {
class RowView : UIView {
private var label: UILabel? = nil {
didSet {
oldValue?.removeFromSuperview()
if let pickerLabel = self.label {
self.addSubview(pickerLabel)
pickerLabel.translatesAutoresizingMaskIntoConstraints = false
let padding: CGFloat = 4
NSLayoutConstraint.activate([
pickerLabel.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor, constant: padding),
pickerLabel.trailingAnchor.constraint(equalTo: self.layoutMarginsGuide.trailingAnchor, constant: -padding),
pickerLabel.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor, constant: -padding),
pickerLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor, constant: padding),
])
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupView()
}
public func setText(_ text: String) {
self.label?.text = text
}
public func sizeThatFitsText(_ size: CGSize) -> CGSize {
return self.label!.sizeThatFits(size)
}
private func setupView() {
let pickerLabel = UILabel(frame: self.bounds)
pickerLabel.text = ""
pickerLabel.adjustsFontSizeToFitWidth = false
pickerLabel.textAlignment = .center
pickerLabel.lineBreakMode = .byCharWrapping
pickerLabel.numberOfLines = 0
self.label = pickerLabel
}
}
}
Консоль выводит этот журнал:
updateUIView
width: 320.0
updateUIView
set heightRow: 50.0
set heightRow: 50.0
update heightRow: 61.0
updateUIView
По какой-то причине новая высота элемента не запрашивается после изменения максимальной высоты.
Результат выглядит так:
Изначально я хотел бы получить средство выбора, которое бы подстраивалось под данные и выбирало оптимальную высоту элемента.