SliderRow.swift 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // SliderRow.swift
  2. // Eureka ( https://github.com/xmartlabs/Eureka )
  3. //
  4. // Copyright (c) 2016 Xmartlabs ( http://xmartlabs.com )
  5. //
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. // THE SOFTWARE.
  24. import UIKit
  25. /// The cell of the SliderRow
  26. open class SliderCell: Cell<Float>, CellType {
  27. private var awakeFromNibCalled = false
  28. @IBOutlet open weak var titleLabel: UILabel!
  29. @IBOutlet open weak var valueLabel: UILabel!
  30. @IBOutlet open weak var slider: UISlider!
  31. open var formatter: NumberFormatter?
  32. public required init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
  33. super.init(style: .value1, reuseIdentifier: reuseIdentifier)
  34. NotificationCenter.default.addObserver(forName: UIContentSizeCategory.didChangeNotification, object: nil, queue: nil) { [weak self] _ in
  35. guard let me = self else { return }
  36. if me.shouldShowTitle {
  37. me.titleLabel = me.textLabel
  38. me.valueLabel = me.detailTextLabel
  39. me.setNeedsUpdateConstraints()
  40. }
  41. }
  42. }
  43. deinit {
  44. guard !awakeFromNibCalled else { return }
  45. NotificationCenter.default.removeObserver(self, name: UIContentSizeCategory.didChangeNotification, object: nil)
  46. }
  47. required public init?(coder aDecoder: NSCoder) {
  48. super.init(coder: aDecoder)
  49. awakeFromNibCalled = true
  50. }
  51. open override func setup() {
  52. super.setup()
  53. if !awakeFromNibCalled {
  54. let title = textLabel
  55. textLabel?.translatesAutoresizingMaskIntoConstraints = false
  56. textLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 500), for: .horizontal)
  57. self.titleLabel = title
  58. let value = detailTextLabel
  59. value?.translatesAutoresizingMaskIntoConstraints = false
  60. value?.setContentHuggingPriority(UILayoutPriority(500), for: .horizontal)
  61. value?.adjustsFontSizeToFitWidth = true
  62. value?.minimumScaleFactor = 0.5
  63. self.valueLabel = value
  64. let slider = UISlider()
  65. slider.translatesAutoresizingMaskIntoConstraints = false
  66. slider.setContentHuggingPriority(UILayoutPriority(rawValue: 500), for: .horizontal)
  67. self.slider = slider
  68. if shouldShowTitle {
  69. contentView.addSubview(titleLabel)
  70. }
  71. if !sliderRow.shouldHideValue {
  72. contentView.addSubview(valueLabel)
  73. }
  74. contentView.addSubview(slider)
  75. setNeedsUpdateConstraints()
  76. }
  77. selectionStyle = .none
  78. slider.minimumValue = 0
  79. slider.maximumValue = 10
  80. slider.addTarget(self, action: #selector(SliderCell.valueChanged), for: .valueChanged)
  81. }
  82. open override func update() {
  83. super.update()
  84. titleLabel.text = row.title
  85. titleLabel.isHidden = !shouldShowTitle
  86. valueLabel.text = row.displayValueFor?(row.value)
  87. valueLabel.isHidden = sliderRow.shouldHideValue
  88. slider.value = row.value ?? slider.minimumValue
  89. slider.isEnabled = !row.isDisabled
  90. }
  91. @objc func valueChanged() {
  92. let roundedValue: Float
  93. let steps = Float(sliderRow.steps)
  94. if steps > 0 {
  95. let stepValue = round((slider.value - slider.minimumValue) / (slider.maximumValue - slider.minimumValue) * steps)
  96. let stepAmount = (slider.maximumValue - slider.minimumValue) / steps
  97. roundedValue = stepValue * stepAmount + self.slider.minimumValue
  98. } else {
  99. roundedValue = slider.value
  100. }
  101. row.value = roundedValue
  102. row.updateCell()
  103. }
  104. var shouldShowTitle: Bool {
  105. return row?.title?.isEmpty == false
  106. }
  107. private var sliderRow: SliderRow {
  108. return row as! SliderRow
  109. }
  110. open override func updateConstraints() {
  111. customConstraints()
  112. super.updateConstraints()
  113. }
  114. open var dynamicConstraints = [NSLayoutConstraint]()
  115. open func customConstraints() {
  116. guard !awakeFromNibCalled else { return }
  117. contentView.removeConstraints(dynamicConstraints)
  118. dynamicConstraints = []
  119. var views: [String : Any] = ["titleLabel": titleLabel!, "slider": slider!, "valueLabel": valueLabel!]
  120. let metrics = ["spacing": 15.0]
  121. valueLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
  122. titleLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
  123. let title = shouldShowTitle ? "[titleLabel]-spacing-" : ""
  124. let value = !sliderRow.shouldHideValue ? "-[valueLabel]" : ""
  125. if let imageView = imageView, let _ = imageView.image {
  126. views["imageView"] = imageView
  127. let hContraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-[imageView]-(15)-\(title)[slider]\(value)-|", options: .alignAllCenterY, metrics: metrics, views: views)
  128. imageView.translatesAutoresizingMaskIntoConstraints = false
  129. dynamicConstraints.append(contentsOf: hContraints)
  130. } else {
  131. let hContraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-\(title)[slider]\(value)-|", options: .alignAllCenterY, metrics: metrics, views: views)
  132. dynamicConstraints.append(contentsOf: hContraints)
  133. }
  134. let vContraint = NSLayoutConstraint(item: slider!, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)
  135. dynamicConstraints.append(vContraint)
  136. contentView.addConstraints(dynamicConstraints)
  137. }
  138. }
  139. /// A row that displays a UISlider. If there is a title set then the title and value will appear above the UISlider.
  140. public final class SliderRow: Row<SliderCell>, RowType {
  141. public var steps: UInt = 20
  142. public var shouldHideValue = false
  143. required public init(tag: String?) {
  144. super.init(tag: tag)
  145. }
  146. }