Skip to content

Commit

Permalink
Improve display to avoid rounding imprecision when using non-integer …
Browse files Browse the repository at this point in the history
…step values. Fix for issue #36.
  • Loading branch information
bwhtmn committed Jul 13, 2018
1 parent ae98a8c commit 338fa1e
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 42 deletions.
88 changes: 47 additions & 41 deletions GMStepper/GMStepper.swift
Expand Up @@ -15,26 +15,26 @@ import UIKit
didSet {
value = min(maximumValue, max(minimumValue, value))

let isInteger = floor(value) == value

//
// If we have items, we will display them as steps
//

if isInteger && stepValue == 1.0 && items.count > 0 {
label.text = items[Int(value)]
}
else if showIntegerIfDoubleIsInteger && isInteger {
label.text = String(stringInterpolationSegment: Int(value))
} else {
label.text = String(stringInterpolationSegment: value)
}
label.text = formattedValue

if oldValue != value {
sendActions(for: .valueChanged)
}
}
}

private var formattedValue: String? {
let isInteger = Decimal(value).exponent >= 0

// If we have items, we will display them as steps
if isInteger && stepValue == 1.0 && items.count > 0 {
return items[Int(value)]
}
else {
return formatter.string(from: NSNumber(value: value))
}
}


/// Minimum value. Must be less than maximumValue. Defaults to 0.
@objc @IBInspectable public var minimumValue: Double = 0 {
Expand All @@ -51,13 +51,21 @@ import UIKit
}

/// Step/Increment value as in UIStepper. Defaults to 1.
@objc @IBInspectable public var stepValue: Double = 1
@objc @IBInspectable public var stepValue: Double = 1 {
didSet {
setupNumberFormatter()
}
}

/// The same as UIStepper's autorepeat. If true, holding on the buttons or keeping the pan gesture alters the value repeatedly. Defaults to true.
@objc @IBInspectable public var autorepeat: Bool = true

/// If the value is integer, it is shown without floating point.
@objc @IBInspectable public var showIntegerIfDoubleIsInteger: Bool = true
@objc @IBInspectable public var showIntegerIfDoubleIsInteger: Bool = true {
didSet {
setupNumberFormatter()
}
}

/// Text on the left button. Be sure that it fits in the button. Defaults to "−".
@objc @IBInspectable public var leftButtonText: String = "" {
Expand Down Expand Up @@ -164,6 +172,9 @@ import UIKit
/// Color of the flashing animation on the buttons in case the value hit the limit.
@objc @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)

/// Formatter for displaying the current value
let formatter = NumberFormatter()

/**
Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
*/
Expand Down Expand Up @@ -204,11 +215,7 @@ import UIKit
lazy var label: UILabel = {
let label = UILabel()
label.textAlignment = .center
if self.showIntegerIfDoubleIsInteger && floor(self.value) == self.value {
label.text = String(stringInterpolationSegment: Int(self.value))
} else {
label.text = String(stringInterpolationSegment: self.value)
}
label.text = formattedValue
label.textColor = self.labelTextColor
label.backgroundColor = self.labelBackgroundColor
label.font = self.labelFont
Expand Down Expand Up @@ -247,24 +254,7 @@ import UIKit

@objc public var items : [String] = [] {
didSet {
let isInteger = floor(value) == value

//
// If we have items, we will display them as steps
//

if isInteger && stepValue == 1.0 && items.count > 0 {

var value = Int(self.value)

if value >= items.count {
value = items.count - 1
self.value = Double(value)
}
else {
label.text = items[value]
}
}
label.text = formattedValue
}
}

Expand Down Expand Up @@ -301,7 +291,7 @@ import UIKit
setup()
}

func setup() {
fileprivate func setup() {
addSubview(leftButton)
addSubview(rightButton)
addSubview(label)
Expand All @@ -311,8 +301,18 @@ import UIKit
clipsToBounds = true
labelOriginalCenter = label.center

setupNumberFormatter()

NotificationCenter.default.addObserver(self, selector: #selector(GMStepper.reset), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
}

func setupNumberFormatter() {
let decValue = Decimal(stepValue)
let digits = decValue.significantFractionalDecimalDigits
formatter.minimumIntegerDigits = 1
formatter.minimumFractionDigits = showIntegerIfDoubleIsInteger ? 0 : digits
formatter.maximumFractionDigits = digits
}

public override func layoutSubviews() {
let buttonWidth = bounds.size.width * ((1 - labelWidthWeight) / 2)
Expand All @@ -332,8 +332,9 @@ import UIKit
value += stepValue
} else if stepperState == .ShouldDecrease {
value -= stepValue
}
}
}


deinit {
resetTimer()
Expand Down Expand Up @@ -516,6 +517,11 @@ extension GMStepper {
timerFireCount = 0
}
}
}


extension Decimal {
var significantFractionalDecimalDigits: Int {
return max(-exponent, 0)
}
}
Expand Up @@ -56,7 +56,7 @@
<color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="stepValue">
<real key="value" value="0.5"/>
<real key="value" value="0.1"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
Expand Down

0 comments on commit 338fa1e

Please sign in to comment.