@@ -15,26 +15,26 @@ import UIKit
16 value = min(maximumValue, max(minimumValue, value))
16 value = min(maximumValue, max(minimumValue, value))
18− let isInteger = floor(value) == value
21− // If we have items, we will display them as steps
24− if isInteger && stepValue == 1.0 && items.count > 0 {
25− label.text = items[Int(value)]
18+ label.text = formattedValue
27− else if showIntegerIfDoubleIsInteger && isInteger {
28− label.text = String(stringInterpolationSegment: Int(value))
30− label.text = String(stringInterpolationSegment: value)
33 if oldValue != value {
20 if oldValue != value {
34 sendActions(for: .valueChanged)
21 sendActions(for: .valueChanged)
26+ private var formattedValue: String? {
27+ let isInteger = Decimal(value).exponent >= 0
29+ // If we have items, we will display them as steps
30+ if isInteger && stepValue == 1.0 && items.count > 0 {
31+ return items[Int(value)]
34+ return formatter.string(from: NSNumber(value: value))
39 /// Minimum value. Must be less than maximumValue. Defaults to 0.
39 /// Minimum value. Must be less than maximumValue. Defaults to 0.
40 @objc @IBInspectable public var minimumValue: Double = 0 {
40 @objc @IBInspectable public var minimumValue: Double = 0 {
@@ -51,13 +51,21 @@ import UIKit
53 /// Step/Increment value as in UIStepper. Defaults to 1.
53 /// Step/Increment value as in UIStepper. Defaults to 1.
54− @objc @IBInspectable public var stepValue: Double = 1
54+ @objc @IBInspectable public var stepValue: Double = 1 {
56+ setupNumberFormatter()
56 /// The same as UIStepper's autorepeat. If true, holding on the buttons or keeping the pan gesture alters the value repeatedly. Defaults to true.
60 /// The same as UIStepper's autorepeat. If true, holding on the buttons or keeping the pan gesture alters the value repeatedly. Defaults to true.
57 @objc @IBInspectable public var autorepeat: Bool = true
61 @objc @IBInspectable public var autorepeat: Bool = true
59 /// If the value is integer, it is shown without floating point.
63 /// If the value is integer, it is shown without floating point.
60− @objc @IBInspectable public var showIntegerIfDoubleIsInteger: Bool = true
64+ @objc @IBInspectable public var showIntegerIfDoubleIsInteger: Bool = true {
66+ setupNumberFormatter()
62 /// Text on the left button. Be sure that it fits in the button. Defaults to "−".
70 /// Text on the left button. Be sure that it fits in the button. Defaults to "−".
63 @objc @IBInspectable public var leftButtonText: String = "−" {
71 @objc @IBInspectable public var leftButtonText: String = "−" {
@@ -164,6 +172,9 @@ import UIKit
164 /// Color of the flashing animation on the buttons in case the value hit the limit.
172 /// Color of the flashing animation on the buttons in case the value hit the limit.
165 @objc @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
173 @objc @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
175+ /// Formatter for displaying the current value
176+ let formatter = NumberFormatter()
168 Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
179 Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
@@ -204,11 +215,7 @@ import UIKit
204 lazy var label: UILabel = {
215 lazy var label: UILabel = {
205 let label = UILabel()
216 let label = UILabel()
206 label.textAlignment = .center
217 label.textAlignment = .center
207− if self.showIntegerIfDoubleIsInteger && floor(self.value) == self.value {
218+ label.text = formattedValue
208− label.text = String(stringInterpolationSegment: Int(self.value))
210− label.text = String(stringInterpolationSegment: self.value)
212 label.textColor = self.labelTextColor
219 label.textColor = self.labelTextColor
213 label.backgroundColor = self.labelBackgroundColor
220 label.backgroundColor = self.labelBackgroundColor
214 label.font = self.labelFont
221 label.font = self.labelFont
@@ -247,24 +254,7 @@ import UIKit
248 @objc public var items : [String] = [] {
255 @objc public var items : [String] = [] {
250− let isInteger = floor(value) == value
253− // If we have items, we will display them as steps
256− if isInteger && stepValue == 1.0 && items.count > 0 {
258− var value = Int(self.value)
260− if value >= items.count {
261− value = items.count - 1
262− self.value = Double(value)
265− label.text = items[value]
257+ label.text = formattedValue
@@ -301,7 +291,7 @@ import UIKit
304− func setup() {
294+ fileprivate func setup() {
305 addSubview(leftButton)
295 addSubview(leftButton)
306 addSubview(rightButton)
296 addSubview(rightButton)
307 addSubview(label)
297 addSubview(label)
@@ -311,8 +301,18 @@ import UIKit
311 clipsToBounds = true
301 clipsToBounds = true
312 labelOriginalCenter = label.center
302 labelOriginalCenter = label.center
304+ setupNumberFormatter()
314 NotificationCenter.default.addObserver(self, selector: #selector(GMStepper.reset), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
306 NotificationCenter.default.addObserver(self, selector: #selector(GMStepper.reset), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
309+ func setupNumberFormatter() {
310+ let decValue = Decimal(stepValue)
311+ let digits = decValue.significantFractionalDecimalDigits
312+ formatter.minimumIntegerDigits = 1
313+ formatter.minimumFractionDigits = showIntegerIfDoubleIsInteger ? 0 : digits
314+ formatter.maximumFractionDigits = digits
317 public override func layoutSubviews() {
317 public override func layoutSubviews() {
318 let buttonWidth = bounds.size.width * ((1 - labelWidthWeight) / 2)
318 let buttonWidth = bounds.size.width * ((1 - labelWidthWeight) / 2)
@@ -332,8 +332,9 @@ import UIKit
332 value += stepValue
332 value += stepValue
333 } else if stepperState == .ShouldDecrease {
333 } else if stepperState == .ShouldDecrease {
334 value -= stepValue
334 value -= stepValue
339 resetTimer()
340 resetTimer()
@@ -516,6 +517,11 @@ extension GMStepper {
516 timerFireCount = 0
517 timerFireCount = 0
524+ var significantFractionalDecimalDigits: Int {
525+ return max(-exponent, 0)