ResizeablePicker.swift 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. //
  2. // ResizeablePicker.swift
  3. // LoopKitUI
  4. //
  5. // Created by Rick Pasetto on 12/7/21.
  6. // Copyright © 2021 LoopKit Authors. All rights reserved.
  7. //
  8. import SwiftUI
  9. import UIKit
  10. public struct ResizeablePicker<SelectionValue>: UIViewRepresentable where SelectionValue: CustomStringConvertible & Hashable {
  11. private let selection: Binding<SelectionValue>
  12. private var selectedRow: Int = 0
  13. // TODO: Would be nice if we could just use `ForEach` and Content, but for now, this'll do
  14. private let data: [SelectionValue]
  15. private let formatter: (SelectionValue) -> String
  16. private let colorer: (SelectionValue) -> Color
  17. public init(selection: Binding<SelectionValue>,
  18. data: [SelectionValue],
  19. formatter: @escaping (SelectionValue) -> String = { $0.description },
  20. colorer: @escaping (SelectionValue) -> Color = { _ in .primary }
  21. ) {
  22. self.selection = selection
  23. self.selectedRow = data.firstIndex(of: selection.wrappedValue) ?? 0
  24. self.data = data
  25. self.formatter = formatter
  26. self.colorer = colorer
  27. }
  28. public func makeCoordinator() -> ResizeablePicker.Coordinator {
  29. Coordinator(self)
  30. }
  31. public func makeUIView(context: UIViewRepresentableContext<ResizeablePicker>) -> UIPickerView {
  32. let picker = UIPickerViewResizeable(frame: .zero)
  33. picker.dataSource = context.coordinator
  34. picker.delegate = context.coordinator
  35. return picker
  36. }
  37. public func updateUIView(_ view: UIPickerView, context: UIViewRepresentableContext<ResizeablePicker>) {
  38. context.coordinator.updateData(newData: data)
  39. view.reloadAllComponents()
  40. if view.selectedRow(inComponent: 0) != selectedRow {
  41. view.selectRow(selectedRow, inComponent: 0, animated: false)
  42. }
  43. }
  44. public class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
  45. private var picker: ResizeablePicker
  46. private var data: [SelectionValue]
  47. init(_ pickerView: ResizeablePicker) {
  48. self.picker = pickerView
  49. self.data = pickerView.data
  50. }
  51. func updateData(newData: [SelectionValue]) {
  52. self.data = newData
  53. }
  54. public func numberOfComponents(in pickerView: UIPickerView) -> Int {
  55. 1
  56. }
  57. public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
  58. data.count
  59. }
  60. public func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
  61. let text = self.picker.formatter(data[row])
  62. let result = view as? UILabel ?? UILabel()
  63. result.text = text
  64. result.font = UIFont.preferredFont(forTextStyle: .title2)
  65. result.textAlignment = .center
  66. result.textColor = UIColor(picker.colorer(data[row]))
  67. result.accessibilityHint = text
  68. result.lineBreakMode = .byClipping
  69. result.adjustsFontSizeToFitWidth = true
  70. return result
  71. }
  72. public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
  73. picker.selectedRow = row
  74. picker.selection.wrappedValue = data[row]
  75. }
  76. }
  77. }
  78. class UIPickerViewResizeable: UIPickerView {
  79. override var intrinsicContentSize: CGSize {
  80. return CGSize(width: UIView.noIntrinsicMetric, height: super.intrinsicContentSize.height)
  81. }
  82. }