MultiValueSlider.swift 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. //
  2. // MultiValueSlider.swift
  3. //
  4. // Created by Yonat Sharon on 16/09/2019.
  5. //
  6. #if canImport(SwiftUI)
  7. import SweeterSwift
  8. import SwiftUI
  9. /// Slider clone with multiple thumbs and values, range highlight, optional snap intervals, optional value labels.
  10. @available(iOS 13.0, *) public struct MultiValueSlider: UIViewRepresentable {
  11. public typealias UIViewType = MultiSlider
  12. private let uiView = MultiSlider()
  13. @Binding var value: [CGFloat]
  14. public init(
  15. value: Binding<[CGFloat]>,
  16. minimumValue: CGFloat? = nil,
  17. maximumValue: CGFloat? = nil,
  18. isContinuous: Bool? = nil,
  19. snapStepSize: CGFloat? = nil,
  20. isHapticSnap: Bool? = nil,
  21. valueLabelPosition: NSLayoutConstraint.Attribute? = nil,
  22. valueLabelAlternatePosition: Bool? = nil,
  23. isValueLabelRelative: Bool? = nil,
  24. orientation: NSLayoutConstraint.Axis? = nil,
  25. outerTrackColor: UIColor? = nil,
  26. valueLabelColor: UIColor? = nil,
  27. valueLabelFont: UIFont? = nil,
  28. thumbImage: UIImage? = nil,
  29. showsThumbImageShadow: Bool? = nil,
  30. minimumImage: UIImage? = nil,
  31. maximumImage: UIImage? = nil,
  32. trackWidth: CGFloat? = nil,
  33. hasRoundTrackEnds: Bool? = nil,
  34. distanceBetweenThumbs: CGFloat? = nil,
  35. keepsDistanceBetweenThumbs: Bool? = nil,
  36. valueLabelFormatter: NumberFormatter? = nil
  37. ) {
  38. _value = value
  39. uiView.minimumValue =? minimumValue
  40. uiView.maximumValue =? maximumValue
  41. uiView.isContinuous =? isContinuous
  42. uiView.snapStepSize =? snapStepSize
  43. uiView.isHapticSnap =? isHapticSnap
  44. uiView.valueLabelPosition =? valueLabelPosition
  45. uiView.valueLabelAlternatePosition =? valueLabelAlternatePosition
  46. uiView.isValueLabelRelative =? isValueLabelRelative
  47. uiView.orientation =? orientation
  48. uiView.outerTrackColor =? outerTrackColor
  49. uiView.valueLabelColor =? valueLabelColor
  50. uiView.valueLabelFont =? valueLabelFont
  51. uiView.thumbImage =? thumbImage
  52. uiView.showsThumbImageShadow =? showsThumbImageShadow
  53. uiView.minimumImage =? minimumImage
  54. uiView.maximumImage =? maximumImage
  55. uiView.trackWidth =? trackWidth
  56. uiView.hasRoundTrackEnds =? hasRoundTrackEnds
  57. uiView.distanceBetweenThumbs =? distanceBetweenThumbs
  58. uiView.keepsDistanceBetweenThumbs =? keepsDistanceBetweenThumbs
  59. uiView.valueLabelFormatter =? valueLabelFormatter
  60. }
  61. public func makeUIView(context: UIViewRepresentableContext<MultiValueSlider>) -> MultiSlider {
  62. uiView.addTarget(context.coordinator, action: #selector(Coordinator.valueChanged), for: .valueChanged)
  63. return uiView
  64. }
  65. public func updateUIView(_ uiView: MultiSlider, context: UIViewRepresentableContext<MultiValueSlider>) {
  66. uiView.value = value
  67. }
  68. public func makeCoordinator() -> Coordinator {
  69. Coordinator(self)
  70. }
  71. public class Coordinator: NSObject {
  72. let parent: MultiValueSlider
  73. init(_ parent: MultiValueSlider) {
  74. self.parent = parent
  75. }
  76. @objc func valueChanged(_ sender: MultiSlider) {
  77. parent.value = sender.value
  78. }
  79. }
  80. }
  81. @available(iOS 13.0, *) public extension MultiValueSlider {
  82. func minimumValue(_ value: CGFloat) -> Self {
  83. uiView.minimumValue = value
  84. return self
  85. }
  86. func maximumValue(_ value: CGFloat) -> Self {
  87. uiView.maximumValue = value
  88. return self
  89. }
  90. /// snap thumbs to specific values, evenly spaced. (default = 0: allow any value)
  91. func snapStepSize(_ value: CGFloat) -> Self {
  92. uiView.snapStepSize = value
  93. return self
  94. }
  95. /// snap thumbs to specific values. changes `minimumValue` and `maximumValue`. (default = []: allow any value)
  96. func snapValues(_ value: [CGFloat]) -> Self {
  97. uiView.snapValues = value
  98. return self
  99. }
  100. /// generate haptic feedback when hitting snap steps
  101. func isHapticSnap(_ value: Bool) -> Self {
  102. uiView.isHapticSnap = value
  103. return self
  104. }
  105. /// show value labels next to thumbs
  106. func valueLabelPosition(_ value: NSLayoutConstraint.Attribute) -> Self {
  107. uiView.valueLabelPosition = value
  108. return self
  109. }
  110. /// show every other value label opposite of the value label position.
  111. func valueLabelAlternatePosition(_ value: Bool) -> Self {
  112. uiView.valueLabelAlternatePosition = value
  113. return self
  114. }
  115. /// value label shows difference from previous thumb value (true) or absolute value (false = default)
  116. func isValueLabelRelative(_ value: Bool) -> Self {
  117. uiView.isValueLabelRelative = value
  118. return self
  119. }
  120. // MARK: - Appearance
  121. func orientation(_ value: NSLayoutConstraint.Axis) -> Self {
  122. uiView.orientation = value
  123. return self
  124. }
  125. /// track color before first thumb and after last thumb. `nil` means to use the tintColor, like the rest of the track.
  126. func outerTrackColor(_ value: UIColor?) -> Self {
  127. uiView.outerTrackColor = value
  128. return self
  129. }
  130. func valueLabelColor(_ value: UIColor?) -> Self {
  131. uiView.valueLabelColor = value
  132. return self
  133. }
  134. func valueLabelFont(_ value: UIFont?) -> Self {
  135. uiView.valueLabelFont = value
  136. return self
  137. }
  138. func thumbTintColor(_ value: UIColor?) -> Self {
  139. uiView.thumbTintColor = value
  140. return self
  141. }
  142. func thumbImage(_ value: UIImage?) -> Self {
  143. uiView.thumbImage = value
  144. return self
  145. }
  146. func thumbTouchExpansionRadius(_ value: CGFloat) -> Self {
  147. uiView.thumbTouchExpansionRadius = value
  148. return self
  149. }
  150. func showsThumbImageShadow(_ value: Bool) -> Self {
  151. uiView.showsThumbImageShadow = value
  152. return self
  153. }
  154. func minimumImage(_ value: UIImage?) -> Self {
  155. uiView.minimumImage = value
  156. return self
  157. }
  158. func maximumImage(_ value: UIImage?) -> Self {
  159. uiView.maximumImage = value
  160. return self
  161. }
  162. func snapImage(_ value: UIImage?) -> Self {
  163. uiView.snapImage = value
  164. return self
  165. }
  166. func trackWidth(_ value: CGFloat) -> Self {
  167. uiView.trackWidth = value
  168. return self
  169. }
  170. func hasRoundTrackEnds(_ value: Bool) -> Self {
  171. uiView.hasRoundTrackEnds = value
  172. return self
  173. }
  174. /// when thumb value is minimum or maximum, align it's center with the track end instead of its edge.
  175. func centerThumbOnTrackEnd(_ value: Bool) -> Self {
  176. uiView.centerThumbOnTrackEnd = value
  177. return self
  178. }
  179. /// minimal distance to keep between thumbs (half a thumb by default)
  180. func distanceBetweenThumbs(_ value: CGFloat) -> Self {
  181. uiView.distanceBetweenThumbs = value
  182. return self
  183. }
  184. func keepsDistanceBetweenThumbs(_ value: Bool) -> Self {
  185. uiView.keepsDistanceBetweenThumbs = value
  186. return self
  187. }
  188. func valueLabelFormatter(_ value: NumberFormatter) -> Self {
  189. uiView.valueLabelFormatter = value
  190. return self
  191. }
  192. func valueLabelTextForThumb(_ value: ((Int, CGFloat) -> String?)?) -> Self {
  193. uiView.valueLabelTextForThumb = value
  194. return self
  195. }
  196. }
  197. #endif