InsulinSensitivityScalingTableViewCell.swift 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. //
  2. // InsulinSensitivityScalingTableViewCell.swift
  3. // LoopKitUI
  4. //
  5. // Created by Michael Pangburn on 3/22/19.
  6. // Copyright © 2019 LoopKit Authors. All rights reserved.
  7. //
  8. import UIKit
  9. protocol InsulinSensitivityScalingTableViewCellDelegate: AnyObject {
  10. func insulinSensitivityScalingTableViewCellDidUpdateScaleFactor(_ cell: InsulinSensitivityScalingTableViewCell)
  11. }
  12. final class InsulinSensitivityScalingTableViewCell: UITableViewCell {
  13. private let allScaleFactorPercentages = Array(stride(from: 10, through: 200, by: 10))
  14. @IBOutlet private weak var titleLabel: UILabel!
  15. @IBOutlet private weak var valueLabel: UILabel!
  16. @IBOutlet weak var gaugeBar: SegmentedGaugeBarView! {
  17. didSet {
  18. if #available(iOSApplicationExtension 13.0, *) {
  19. gaugeBar.backgroundColor = .systemGray6
  20. } else {
  21. gaugeBar.backgroundColor = .white
  22. }
  23. gaugeBar.delegate = self
  24. }
  25. }
  26. @IBOutlet private weak var scaleFactorPicker: UIPickerView! {
  27. didSet {
  28. scaleFactorPicker.dataSource = self
  29. scaleFactorPicker.delegate = self
  30. }
  31. }
  32. @IBOutlet private weak var scaleFactorPickerHeightConstraint: NSLayoutConstraint! {
  33. didSet {
  34. pickerExpandedHeight = scaleFactorPickerHeightConstraint.constant
  35. }
  36. }
  37. private var pickerExpandedHeight: CGFloat = 0
  38. @IBOutlet private weak var footerLabel: UILabel!
  39. private lazy var percentFormatter: NumberFormatter = {
  40. let formatter = NumberFormatter()
  41. formatter.numberStyle = .percent
  42. formatter.maximumFractionDigits = 0
  43. return formatter
  44. }()
  45. var isPickerHidden: Bool {
  46. get {
  47. return scaleFactorPicker.isHidden
  48. }
  49. set {
  50. scaleFactorPicker.isHidden = newValue
  51. scaleFactorPickerHeightConstraint.constant = newValue ? 0 : pickerExpandedHeight
  52. if !newValue {
  53. updatePickerRow(animated: false)
  54. }
  55. }
  56. }
  57. private func updatePickerRow(animated: Bool) {
  58. var selectedRow = allScaleFactorPercentages.firstIndex(of: selectedPercentage)
  59. if selectedRow == nil {
  60. let truncatedPercentage = allScaleFactorPercentages
  61. .adjacentPairs()
  62. .first(where: { lower, upper in
  63. (lower..<upper).contains(selectedPercentage)
  64. })?.0 ?? 100
  65. selectedRow = allScaleFactorPercentages.firstIndex(of: truncatedPercentage)
  66. }
  67. scaleFactorPicker.selectRow(selectedRow!, inComponent: 0, animated: animated)
  68. }
  69. private var selectedPercentage = 100 {
  70. didSet {
  71. gaugeBar.progress = Double(self.selectedPercentage) / 100
  72. valueLabel.text = percentageString(from: selectedPercentage)
  73. updateFooterLabel()
  74. delegate?.insulinSensitivityScalingTableViewCellDidUpdateScaleFactor(self)
  75. }
  76. }
  77. var scaleFactor: Double {
  78. get {
  79. return Double(selectedPercentage) / 100
  80. }
  81. set {
  82. let domain = allScaleFactorPercentages.first!...allScaleFactorPercentages.last!
  83. selectedPercentage = Int(round(newValue * 100)).clamped(to: domain)
  84. }
  85. }
  86. weak var delegate: InsulinSensitivityScalingTableViewCellDelegate?
  87. override func awakeFromNib() {
  88. super.awakeFromNib()
  89. titleLabel.text = LocalizedString("Overall Insulin Needs", comment: "The title text for the insulin sensitivity scaling setting")
  90. selectedPercentage = 100
  91. setSelected(true, animated: false)
  92. }
  93. override func setSelected(_ selected: Bool, animated: Bool) {
  94. // Save and reassign the background color to avoid propagated transparency during the animation.
  95. let gaugeBarBackgroundColor = gaugeBar.backgroundColor
  96. super.setSelected(selected, animated: animated)
  97. if selected {
  98. gaugeBar.backgroundColor = gaugeBarBackgroundColor
  99. isPickerHidden.toggle()
  100. }
  101. }
  102. override func setHighlighted(_ highlighted: Bool, animated: Bool) {
  103. // Save and reassign the background color to avoid propagated transparency during the animation.
  104. let gaugeBarBackgroundColor = gaugeBar.backgroundColor
  105. super.setHighlighted(highlighted, animated: animated)
  106. if highlighted {
  107. gaugeBar.backgroundColor = gaugeBarBackgroundColor
  108. }
  109. }
  110. private func updateFooterLabel() {
  111. let footerText: String
  112. let delta = selectedPercentage - 100
  113. if delta < 0 {
  114. footerText = String(
  115. format: LocalizedString("Basal, bolus, and correction insulin dose amounts are decreased by %@%%.", comment: "Describes a percentage decrease in overall insulin needs"),
  116. String(abs(delta))
  117. )
  118. } else if delta > 0 {
  119. footerText = String(
  120. format: LocalizedString("Basal, bolus, and correction insulin dose amounts are increased by %@%%.", comment: "Describes a percentage increase in overall insulin needs"),
  121. String(delta)
  122. )
  123. } else {
  124. footerText = LocalizedString("Basal, bolus, and correction insulin dose amounts are unaffected.", comment: "Describes a lack of change in overall insulin needs")
  125. }
  126. footerLabel.text = footerText
  127. }
  128. private func percentageString(from percentage: Int) -> String? {
  129. return percentFormatter.string(from: Double(percentage) / 100)
  130. }
  131. }
  132. extension InsulinSensitivityScalingTableViewCell: UIPickerViewDataSource {
  133. func numberOfComponents(in pickerView: UIPickerView) -> Int {
  134. return 1
  135. }
  136. func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
  137. return allScaleFactorPercentages.count
  138. }
  139. }
  140. extension InsulinSensitivityScalingTableViewCell: UIPickerViewDelegate {
  141. func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
  142. return percentageString(from: allScaleFactorPercentages[row])
  143. }
  144. func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
  145. selectedPercentage = allScaleFactorPercentages[row]
  146. }
  147. }
  148. extension InsulinSensitivityScalingTableViewCell: SegmentedGaugeBarViewDelegate {
  149. func segmentedGaugeBarView(_ view: SegmentedGaugeBarView, didUpdateProgressFrom oldValue: Double, to newValue: Double) {
  150. scaleFactor = newValue
  151. updatePickerRow(animated: true)
  152. }
  153. }
  154. extension InsulinSensitivityScalingTableViewCellDelegate where Self: UITableViewController {
  155. func collapseInsulinSensitivityScalingCells(excluding indexPath: IndexPath? = nil) {
  156. for case let cell as InsulinSensitivityScalingTableViewCell in tableView.visibleCells where tableView.indexPath(for: cell) != indexPath && !cell.isPickerHidden {
  157. cell.isPickerHidden = true
  158. }
  159. }
  160. }