TriplePickerRow.swift 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. //
  2. // TriplePickerRow.swift
  3. // Eureka
  4. //
  5. // Created by Mathias Claassen on 5/9/18.
  6. // Copyright © 2018 Xmartlabs. All rights reserved.
  7. //
  8. import Foundation
  9. import UIKit
  10. public struct Tuple3<A: Equatable, B: Equatable, C: Equatable> {
  11. public let a: A
  12. public let b: B
  13. public let c: C
  14. public init(a: A, b: B, c: C) {
  15. self.a = a
  16. self.b = b
  17. self.c = c
  18. }
  19. }
  20. extension Tuple3: Equatable {}
  21. public func == <A: Equatable, B: Equatable, C: Equatable>(lhs: Tuple3<A, B, C>, rhs: Tuple3<A, B, C>) -> Bool {
  22. return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c
  23. }
  24. // MARK: MultiplePickerCell
  25. open class TriplePickerCell<A, B, C> : _PickerCell<Tuple3<A, B, C>> where A: Equatable, B: Equatable, C: Equatable {
  26. private var pickerRow: _TriplePickerRow<A, B, C>? { return row as? _TriplePickerRow<A, B, C> }
  27. public required init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
  28. super.init(style: style, reuseIdentifier: reuseIdentifier)
  29. }
  30. required public init?(coder aDecoder: NSCoder) {
  31. super.init(coder: aDecoder)
  32. }
  33. open override func update() {
  34. super.update()
  35. if let selectedValue = pickerRow?.value, let indexA = pickerRow?.firstOptions().firstIndex(of: selectedValue.a),
  36. let indexB = pickerRow?.secondOptions(selectedValue.a).firstIndex(of: selectedValue.b),
  37. let indexC = pickerRow?.thirdOptions(selectedValue.a, selectedValue.b).firstIndex(of: selectedValue.c) {
  38. picker.selectRow(indexA, inComponent: 0, animated: true)
  39. picker.selectRow(indexB, inComponent: 1, animated: true)
  40. picker.selectRow(indexC, inComponent: 2, animated: true)
  41. }
  42. }
  43. open override func numberOfComponents(in pickerView: UIPickerView) -> Int {
  44. return 3
  45. }
  46. open override func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
  47. guard let pickerRow = pickerRow else { return 0 }
  48. if component == 0 {
  49. return pickerRow.firstOptions().count
  50. } else if component == 1 {
  51. return pickerRow.secondOptions(pickerRow.selectedFirst()).count
  52. } else {
  53. return pickerRow.thirdOptions(pickerRow.selectedFirst(), pickerRow.selectedSecond()).count
  54. }
  55. }
  56. open override func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
  57. guard let pickerRow = pickerRow else { return "" }
  58. if component == 0 {
  59. return pickerRow.displayValueForFirstRow(pickerRow.firstOptions()[row])
  60. } else if component == 1 {
  61. return pickerRow.displayValueForSecondRow(pickerRow.secondOptions(pickerRow.selectedFirst())[row])
  62. } else {
  63. return pickerRow.displayValueForThirdRow(pickerRow.thirdOptions(pickerRow.selectedFirst(), pickerRow.selectedSecond())[row])
  64. }
  65. }
  66. open override func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
  67. guard let pickerRow = pickerRow else { return }
  68. if component == 0 {
  69. let a = pickerRow.firstOptions()[row]
  70. if let value = pickerRow.value {
  71. guard value.a != a else {
  72. return
  73. }
  74. let b: B = pickerRow.secondOptions(a).contains(value.b) ? value.b : pickerRow.secondOptions(a)[0]
  75. let c: C = pickerRow.thirdOptions(a, b).contains(value.c) ? value.c : pickerRow.thirdOptions(a, b)[0]
  76. pickerRow.value = Tuple3(a: a, b: b, c: c)
  77. pickerView.reloadComponent(1)
  78. pickerView.reloadComponent(2)
  79. if b != value.b {
  80. pickerView.selectRow(0, inComponent: 1, animated: true)
  81. }
  82. if c != value.c {
  83. pickerView.selectRow(0, inComponent: 2, animated: true)
  84. }
  85. } else {
  86. let b = pickerRow.secondOptions(a)[0]
  87. pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[0])
  88. pickerView.reloadComponent(1)
  89. pickerView.reloadComponent(2)
  90. pickerView.selectRow(0, inComponent: 1, animated: true)
  91. pickerView.selectRow(0, inComponent: 2, animated: true)
  92. }
  93. } else if component == 1 {
  94. let a = pickerRow.selectedFirst()
  95. let b = pickerRow.secondOptions(a)[row]
  96. if let value = pickerRow.value {
  97. guard value.b != b else {
  98. return
  99. }
  100. if pickerRow.thirdOptions(a, b).contains(value.c) {
  101. pickerRow.value = Tuple3(a: a, b: b, c: value.c)
  102. pickerView.reloadComponent(2)
  103. return
  104. } else {
  105. pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[0])
  106. }
  107. } else {
  108. pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[0])
  109. }
  110. pickerView.reloadComponent(2)
  111. pickerView.selectRow(0, inComponent: 2, animated: true)
  112. } else {
  113. let a = pickerRow.selectedFirst()
  114. let b = pickerRow.selectedSecond()
  115. pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[row])
  116. }
  117. }
  118. }
  119. // MARK: PickerRow
  120. open class _TriplePickerRow<A, B, C> : Row<TriplePickerCell<A, B, C>> where A: Equatable, B: Equatable, C: Equatable {
  121. /// Options for first component. Will be called often so should be O(1)
  122. public var firstOptions: (() -> [A]) = {[]}
  123. /// Options for second component given the selected value from the first component. Will be called often so should be O(1)
  124. public var secondOptions: ((A) -> [B]) = {_ in []}
  125. /// Options for third component given the selected value from the first and second components. Will be called often so should be O(1)
  126. public var thirdOptions: ((A, B) -> [C]) = {_, _ in []}
  127. /// Modify the displayed values for the first picker row.
  128. public var displayValueForFirstRow: ((A) -> (String)) = { a in return String(describing: a) }
  129. /// Modify the displayed values for the second picker row.
  130. public var displayValueForSecondRow: ((B) -> (String)) = { b in return String(describing: b) }
  131. /// Modify the displayed values for the third picker row.
  132. public var displayValueForThirdRow: ((C) -> (String)) = { c in return String(describing: c) }
  133. required public init(tag: String?) {
  134. super.init(tag: tag)
  135. }
  136. func selectedFirst() -> A {
  137. return value?.a ?? firstOptions()[0]
  138. }
  139. func selectedSecond() -> B {
  140. return value?.b ?? secondOptions(selectedFirst())[0]
  141. }
  142. }
  143. /// A generic row where the user can pick an option from a picker view
  144. public final class TriplePickerRow<A, B, C>: _TriplePickerRow<A, B, C>, RowType where A: Equatable, B: Equatable, C: Equatable {
  145. required public init(tag: String?) {
  146. super.init(tag: tag)
  147. }
  148. }