|
|
|
|
@ -15,6 +15,8 @@ import UIKit
|
|
|
|
|
didSet {
|
|
|
|
|
value = min(maximumValue, max(minimumValue, value))
|
|
|
|
|
|
|
|
|
|
handleIsLimitReached()
|
|
|
|
|
|
|
|
|
|
label.text = formattedValue
|
|
|
|
|
|
|
|
|
|
if oldValue != value {
|
|
|
|
|
@ -35,7 +37,6 @@ import UIKit
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Minimum value. Must be less than maximumValue. Defaults to 0.
|
|
|
|
|
@objc @IBInspectable public var minimumValue: Double = 0 {
|
|
|
|
|
didSet {
|
|
|
|
|
@ -67,29 +68,37 @@ import UIKit
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Text on the left button. Be sure that it fits in the button. Defaults to "−".
|
|
|
|
|
@objc @IBInspectable public var leftButtonText: String = "−" {
|
|
|
|
|
/// Image on the left button. Defaults to nil.
|
|
|
|
|
@objc @IBInspectable public var leftButtonImage: UIImage? = nil {
|
|
|
|
|
didSet {
|
|
|
|
|
leftButton.setTitle(leftButtonText, for: .normal)
|
|
|
|
|
leftButton.setImage(leftButtonImage, for: .normal)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Text on the right button. Be sure that it fits in the button. Defaults to "+".
|
|
|
|
|
@objc @IBInspectable public var rightButtonText: String = "+" {
|
|
|
|
|
/// Image on the right button. Defaults to nil.
|
|
|
|
|
@objc @IBInspectable public var rightButtonImage: UIImage? = nil {
|
|
|
|
|
didSet {
|
|
|
|
|
rightButton.setTitle(rightButtonText, for: .normal)
|
|
|
|
|
rightButton.setImage(rightButtonImage, for: .normal)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Text color of the buttons. Defaults to white.
|
|
|
|
|
@objc @IBInspectable public var buttonsTextColor: UIColor = UIColor.white {
|
|
|
|
|
/// Left button content insets. Defaults to ".zero".
|
|
|
|
|
@objc @IBInspectable public var leftButtonContentInsets: UIEdgeInsets = .zero {
|
|
|
|
|
didSet {
|
|
|
|
|
for button in [leftButton, rightButton] {
|
|
|
|
|
button.setTitleColor(buttonsTextColor, for: .normal)
|
|
|
|
|
}
|
|
|
|
|
leftButton.contentEdgeInsets = leftButtonContentInsets
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Right button content insets. Defaults to ".zero".
|
|
|
|
|
@objc @IBInspectable public var rightButtonContentInsets: UIEdgeInsets = .zero {
|
|
|
|
|
didSet {
|
|
|
|
|
rightButton.contentEdgeInsets = rightButtonContentInsets
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Left button limit opacity. Defaults to "0.4".
|
|
|
|
|
@objc @IBInspectable public var leftButtonLimitOpacity: CGFloat = 0.4
|
|
|
|
|
|
|
|
|
|
/// Background color of the buttons. Defaults to dark blue.
|
|
|
|
|
@objc @IBInspectable public var buttonsBackgroundColor: UIColor = UIColor(red:0.21, green:0.5, blue:0.74, alpha:1) {
|
|
|
|
|
didSet {
|
|
|
|
|
@ -100,15 +109,12 @@ import UIKit
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Font of the buttons. Defaults to AvenirNext-Bold, 20.0 points in size.
|
|
|
|
|
@objc public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
|
|
|
|
|
didSet {
|
|
|
|
|
for button in [leftButton, rightButton] {
|
|
|
|
|
button.titleLabel?.font = buttonsFont
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Label tap closure
|
|
|
|
|
@objc public var didTouchLabel: ((Double) -> Void)?
|
|
|
|
|
|
|
|
|
|
/// Block is called when the minimum is exceeded
|
|
|
|
|
@objc public var minimumExceeded: (() -> Void)?
|
|
|
|
|
|
|
|
|
|
/// Text color of the middle label. Defaults to white.
|
|
|
|
|
@objc @IBInspectable public var labelTextColor: UIColor = UIColor.white {
|
|
|
|
|
didSet {
|
|
|
|
|
@ -188,10 +194,8 @@ import UIKit
|
|
|
|
|
|
|
|
|
|
lazy var leftButton: UIButton = {
|
|
|
|
|
let button = UIButton()
|
|
|
|
|
button.setTitle(self.leftButtonText, for: .normal)
|
|
|
|
|
button.setTitleColor(self.buttonsTextColor, for: .normal)
|
|
|
|
|
button.setImage(self.leftButtonImage, for: .normal)
|
|
|
|
|
button.backgroundColor = self.buttonsBackgroundColor
|
|
|
|
|
button.titleLabel?.font = self.buttonsFont
|
|
|
|
|
button.addTarget(self, action: #selector(GMStepper.leftButtonTouchDown), for: .touchDown)
|
|
|
|
|
button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpInside)
|
|
|
|
|
button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpOutside)
|
|
|
|
|
@ -201,10 +205,8 @@ import UIKit
|
|
|
|
|
|
|
|
|
|
lazy var rightButton: UIButton = {
|
|
|
|
|
let button = UIButton()
|
|
|
|
|
button.setTitle(self.rightButtonText, for: .normal)
|
|
|
|
|
button.setTitleColor(self.buttonsTextColor, for: .normal)
|
|
|
|
|
button.setImage(self.rightButtonImage, for: .normal)
|
|
|
|
|
button.backgroundColor = self.buttonsBackgroundColor
|
|
|
|
|
button.titleLabel?.font = self.buttonsFont
|
|
|
|
|
button.addTarget(self, action: #selector(GMStepper.rightButtonTouchDown), for: .touchDown)
|
|
|
|
|
button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpInside)
|
|
|
|
|
button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpOutside)
|
|
|
|
|
@ -225,6 +227,8 @@ import UIKit
|
|
|
|
|
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(GMStepper.handlePan))
|
|
|
|
|
panRecognizer.maximumNumberOfTouches = 1
|
|
|
|
|
label.addGestureRecognizer(panRecognizer)
|
|
|
|
|
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(GMStepper.handleLabelTap))
|
|
|
|
|
label.addGestureRecognizer(tapRecognizer)
|
|
|
|
|
return label
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
@ -296,6 +300,7 @@ import UIKit
|
|
|
|
|
addSubview(rightButton)
|
|
|
|
|
addSubview(label)
|
|
|
|
|
|
|
|
|
|
handleIsLimitReached()
|
|
|
|
|
backgroundColor = buttonsBackgroundColor
|
|
|
|
|
layer.cornerRadius = cornerRadius
|
|
|
|
|
clipsToBounds = true
|
|
|
|
|
@ -407,6 +412,10 @@ extension GMStepper {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func handleLabelTap() {
|
|
|
|
|
didTouchLabel?(value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func reset() {
|
|
|
|
|
panState = .Stable
|
|
|
|
|
stepperState = .Stable
|
|
|
|
|
@ -433,11 +442,11 @@ extension GMStepper {
|
|
|
|
|
|
|
|
|
|
if value == minimumValue {
|
|
|
|
|
animateLimitHitIfNeeded()
|
|
|
|
|
minimumExceeded?()
|
|
|
|
|
} else {
|
|
|
|
|
stepperState = .ShouldDecrease
|
|
|
|
|
animateSlideLeft()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func rightButtonTouchDown(button: UIButton) {
|
|
|
|
|
@ -519,8 +528,16 @@ extension GMStepper {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private extension GMStepper {
|
|
|
|
|
|
|
|
|
|
func handleIsLimitReached() {
|
|
|
|
|
let isLimitReached = value == minimumValue
|
|
|
|
|
leftButton.alpha = isLimitReached ? leftButtonLimitOpacity : 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension Decimal {
|
|
|
|
|
|
|
|
|
|
var significantFractionalDecimalDigits: Int {
|
|
|
|
|
return max(-exponent, 0)
|
|
|
|
|
}
|
|
|
|
|
|