@@ -11,72 +11,79 @@ import UIKit
11@IBDesignable public class GMStepper: UIControl {
11@IBDesignable public class GMStepper: UIControl {
13 /// Current value of the stepper. Defaults to 0.
13 /// Current value of the stepper. Defaults to 0.
14− @IBInspectable public var value: Double = 0 {
14+ @objc @IBInspectable public var value: Double = 0 {
16 value = min(maximumValue, max(minimumValue, value))
16 value = min(maximumValue, max(minimumValue, value))
18 let isInteger = floor(value) == 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)]
20− if showIntegerIfDoubleIsInteger && isInteger {
27+ else if showIntegerIfDoubleIsInteger && isInteger {
21 label.text = String(stringInterpolationSegment: Int(value))
28 label.text = String(stringInterpolationSegment: Int(value))
23 label.text = String(stringInterpolationSegment: value)
30 label.text = String(stringInterpolationSegment: value)
26 if oldValue != value {
33 if oldValue != value {
27− sendActionsForControlEvents(.ValueChanged)
34+ sendActions(for: .valueChanged)
32 /// Minimum value. Must be less than maximumValue. Defaults to 0.
39 /// Minimum value. Must be less than maximumValue. Defaults to 0.
33− @IBInspectable public var minimumValue: Double = 0 {
40+ @objc @IBInspectable public var minimumValue: Double = 0 {
35 value = min(maximumValue, max(minimumValue, value))
42 value = min(maximumValue, max(minimumValue, value))
39 /// Maximum value. Must be more than minimumValue. Defaults to 100.
46 /// Maximum value. Must be more than minimumValue. Defaults to 100.
40− @IBInspectable public var maximumValue: Double = 100 {
47+ @objc @IBInspectable public var maximumValue: Double = 100 {
42 value = min(maximumValue, max(minimumValue, value))
49 value = min(maximumValue, max(minimumValue, value))
46 /// Step/Increment value as in UIStepper. Defaults to 1.
53 /// Step/Increment value as in UIStepper. Defaults to 1.
47− @IBInspectable public var stepValue: Double = 1
54+ @objc @IBInspectable public var stepValue: Double = 1
49 /// The same as UIStepper's autorepeat. If true, holding on the buttons or keeping the pan gesture alters the value repeatedly. Defaults to true.
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.
50− @IBInspectable public var autorepeat: Bool = true
57+ @objc @IBInspectable public var autorepeat: Bool = true
52 /// If the value is integer, it is shown without floating point.
59 /// If the value is integer, it is shown without floating point.
53− @IBInspectable public var showIntegerIfDoubleIsInteger: Bool = true
60+ @objc @IBInspectable public var showIntegerIfDoubleIsInteger: Bool = true
55 /// Text on the left button. Be sure that it fits in the button. Defaults to "−".
62 /// Text on the left button. Be sure that it fits in the button. Defaults to "−".
56− @IBInspectable public var leftButtonText: String = "−" {
63+ @objc @IBInspectable public var leftButtonText: String = "−" {
58− leftButton.setTitle(leftButtonText, forState: .Normal)
65+ leftButton.setTitle(leftButtonText, for: .normal)
62 /// Text on the right button. Be sure that it fits in the button. Defaults to "+".
69 /// Text on the right button. Be sure that it fits in the button. Defaults to "+".
63− @IBInspectable public var rightButtonText: String = "+" {
70+ @objc @IBInspectable public var rightButtonText: String = "+" {
65− rightButton.setTitle(rightButtonText, forState: .Normal)
72+ rightButton.setTitle(rightButtonText, for: .normal)
69 /// Text color of the buttons. Defaults to white.
76 /// Text color of the buttons. Defaults to white.
70− @IBInspectable public var buttonsTextColor: UIColor = UIColor.whiteColor() {
77+ @objc @IBInspectable public var buttonsTextColor: UIColor = UIColor.white {
72 for button in [leftButton, rightButton] {
79 for button in [leftButton, rightButton] {
73− button.setTitleColor(buttonsTextColor, forState: .Normal)
80+ button.setTitleColor(buttonsTextColor, for: .normal)
78 /// Background color of the buttons. Defaults to dark blue.
85 /// Background color of the buttons. Defaults to dark blue.
79− @IBInspectable public var buttonsBackgroundColor: UIColor = UIColor(red:0.21, green:0.5, blue:0.74, alpha:1) {
86+ @objc @IBInspectable public var buttonsBackgroundColor: UIColor = UIColor(red:0.21, green:0.5, blue:0.74, alpha:1) {
81 for button in [leftButton, rightButton] {
88 for button in [leftButton, rightButton] {
82 button.backgroundColor = buttonsBackgroundColor
89 button.backgroundColor = buttonsBackgroundColor
@@ -86,7 +93,7 @@ import UIKit
88 /// Font of the buttons. Defaults to AvenirNext-Bold, 20.0 points in size.
95 /// Font of the buttons. Defaults to AvenirNext-Bold, 20.0 points in size.
89− public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
96+ @objc public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
91 for button in [leftButton, rightButton] {
98 for button in [leftButton, rightButton] {
92 button.titleLabel?.font = buttonsFont
99 button.titleLabel?.font = buttonsFont
@@ -95,28 +102,35 @@ import UIKit
97 /// Text color of the middle label. Defaults to white.
104 /// Text color of the middle label. Defaults to white.
98− @IBInspectable public var labelTextColor: UIColor = UIColor.whiteColor() {
105+ @objc @IBInspectable public var labelTextColor: UIColor = UIColor.white {
100 label.textColor = labelTextColor
107 label.textColor = labelTextColor
104 /// Text color of the middle label. Defaults to lighter blue.
111 /// Text color of the middle label. Defaults to lighter blue.
105− @IBInspectable public var labelBackgroundColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1) {
112+ @objc @IBInspectable public var labelBackgroundColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1) {
107 label.backgroundColor = labelBackgroundColor
114 label.backgroundColor = labelBackgroundColor
111 /// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
118 /// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
112− public var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)! {
119+ @objc public var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)! {
114 label.font = labelFont
121 label.font = labelFont
124+ /// Corner radius of the middle label. Defaults to 0.
125+ @objc @IBInspectable public var labelCornerRadius: CGFloat = 0 {
127+ label.layer.cornerRadius = labelCornerRadius
118 /// Corner radius of the stepper's layer. Defaults to 4.0.
132 /// Corner radius of the stepper's layer. Defaults to 4.0.
119− @IBInspectable public var cornerRadius: CGFloat = 4.0 {
133+ @objc @IBInspectable public var cornerRadius: CGFloat = 4.0 {
121 layer.cornerRadius = cornerRadius
135 layer.cornerRadius = cornerRadius
122 clipsToBounds = true
136 clipsToBounds = true
@@ -124,7 +138,7 @@ import UIKit
126 /// Border width of the stepper and middle label's layer. Defaults to 0.0.
140 /// Border width of the stepper and middle label's layer. Defaults to 0.0.
127− @IBInspectable public var borderWidth: CGFloat = 0.0 {
141+ @objc @IBInspectable public var borderWidth: CGFloat = 0.0 {
129 layer.borderWidth = borderWidth
143 layer.borderWidth = borderWidth
130 label.layer.borderWidth = borderWidth
144 label.layer.borderWidth = borderWidth
@@ -132,15 +146,15 @@ import UIKit
134 /// Color of the border of the stepper and middle label's layer. Defaults to clear color.
148 /// Color of the border of the stepper and middle label's layer. Defaults to clear color.
135− @IBInspectable public var borderColor: UIColor = UIColor.clearColor() {
149+ @objc @IBInspectable public var borderColor: UIColor = UIColor.clear {
137− layer.borderColor = borderColor.CGColor
151+ layer.borderColor = borderColor.cgColor
138− label.layer.borderColor = borderColor.CGColor
152+ label.layer.borderColor = borderColor.cgColor
142 /// Percentage of the middle label's width. Must be between 0 and 1. Defaults to 0.5. Be sure that it is wide enough to show the value.
156 /// Percentage of the middle label's width. Must be between 0 and 1. Defaults to 0.5. Be sure that it is wide enough to show the value.
143− @IBInspectable public var labelWidthWeight: CGFloat = 0.5 {
157+ @objc @IBInspectable public var labelWidthWeight: CGFloat = 0.5 {
145 labelWidthWeight = min(1, max(0, labelWidthWeight))
159 labelWidthWeight = min(1, max(0, labelWidthWeight))
146 setNeedsLayout()
160 setNeedsLayout()
@@ -148,7 +162,7 @@ import UIKit
150 /// Color of the flashing animation on the buttons in case the value hit the limit.
164 /// Color of the flashing animation on the buttons in case the value hit the limit.
151− @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
165+ @objc @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
154 Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
168 Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
@@ -156,38 +170,40 @@ import UIKit
156 let labelSlideLength: CGFloat = 5
170 let labelSlideLength: CGFloat = 5
158 /// Duration of the sliding animation
172 /// Duration of the sliding animation
159− let labelSlideDuration = NSTimeInterval(0.1)
173+ let labelSlideDuration = TimeInterval(0.1)
161 /// Duration of the animation when the value hits the limit.
175 /// Duration of the animation when the value hits the limit.
162− let limitHitAnimationDuration = NSTimeInterval(0.1)
176+ let limitHitAnimationDuration = TimeInterval(0.1)
164 lazy var leftButton: UIButton = {
178 lazy var leftButton: UIButton = {
165 let button = UIButton()
179 let button = UIButton()
166− button.setTitle(self.leftButtonText, forState: .Normal)
180+ button.setTitle(self.leftButtonText, for: .normal)
167− button.setTitleColor(self.buttonsTextColor, forState: .Normal)
181+ button.setTitleColor(self.buttonsTextColor, for: .normal)
168 button.backgroundColor = self.buttonsBackgroundColor
182 button.backgroundColor = self.buttonsBackgroundColor
169 button.titleLabel?.font = self.buttonsFont
183 button.titleLabel?.font = self.buttonsFont
170− button.addTarget(self, action: "leftButtonTouchDown:", forControlEvents: .TouchDown)
184+ button.addTarget(self, action: #selector(GMStepper.leftButtonTouchDown), for: .touchDown)
185+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpInside)
171− button.addTarget(self, action: "buttonTouchUp:", forControlEvents: UIControlEvents.TouchUpInside)
186+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpOutside)
172− button.addTarget(self, action: "buttonTouchUp:", forControlEvents: UIControlEvents.TouchUpOutside)
187+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchCancel)
173 return button
188 return button
176 lazy var rightButton: UIButton = {
191 lazy var rightButton: UIButton = {
177 let button = UIButton()
192 let button = UIButton()
178− button.setTitle(self.rightButtonText, forState: .Normal)
193+ button.setTitle(self.rightButtonText, for: .normal)
179− button.setTitleColor(self.buttonsTextColor, forState: .Normal)
194+ button.setTitleColor(self.buttonsTextColor, for: .normal)
180 button.backgroundColor = self.buttonsBackgroundColor
195 button.backgroundColor = self.buttonsBackgroundColor
181 button.titleLabel?.font = self.buttonsFont
196 button.titleLabel?.font = self.buttonsFont
182− button.addTarget(self, action: "rightButtonTouchDown:", forControlEvents: .TouchDown)
197+ button.addTarget(self, action: #selector(GMStepper.rightButtonTouchDown), for: .touchDown)
198+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpInside)
183− button.addTarget(self, action: "buttonTouchUp:", forControlEvents: UIControlEvents.TouchUpInside)
199+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpOutside)
184− button.addTarget(self, action: "buttonTouchUp:", forControlEvents: UIControlEvents.TouchUpOutside)
200+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchCancel)
185 return button
201 return button
188 lazy var label: UILabel = {
204 lazy var label: UILabel = {
189 let label = UILabel()
205 let label = UILabel()
190− label.textAlignment = .Center
206+ label.textAlignment = .center
191 if self.showIntegerIfDoubleIsInteger && floor(self.value) == self.value {
207 if self.showIntegerIfDoubleIsInteger && floor(self.value) == self.value {
192 label.text = String(stringInterpolationSegment: Int(self.value))
208 label.text = String(stringInterpolationSegment: Int(self.value))
@@ -196,8 +212,10 @@ import UIKit
196 label.textColor = self.labelTextColor
212 label.textColor = self.labelTextColor
197 label.backgroundColor = self.labelBackgroundColor
213 label.backgroundColor = self.labelBackgroundColor
198 label.font = self.labelFont
214 label.font = self.labelFont
199− label.userInteractionEnabled = true
215+ label.layer.cornerRadius = self.labelCornerRadius
216+ label.layer.masksToBounds = true
217+ label.isUserInteractionEnabled = true
200− let panRecognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
218+ let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(GMStepper.handlePan))
201 panRecognizer.maximumNumberOfTouches = 1
219 panRecognizer.maximumNumberOfTouches = 1
202 label.addGestureRecognizer(panRecognizer)
220 label.addGestureRecognizer(panRecognizer)
203 return label
221 return label
@@ -225,9 +243,33 @@ import UIKit
248+ @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]
229 /// Timer used for autorepeat option
271 /// Timer used for autorepeat option
230− var timer: NSTimer?
272+ var timer: Timer?
232 /** When UIStepper reaches its top speed, it alters the value with a time interval of ~0.05 sec.
274 /** When UIStepper reaches its top speed, it alters the value with a time interval of ~0.05 sec.
233 The user pressing and holding on the stepper repeatedly:
275 The user pressing and holding on the stepper repeatedly:
@@ -235,7 +277,7 @@ import UIKit
235 - For the next 1.5 sec, it changes the value every 0.1 sec.
277 - For the next 1.5 sec, it changes the value every 0.1 sec.
236 - Then, every 0.05 sec.
278 - Then, every 0.05 sec.
238− let timerInterval = NSTimeInterval(0.05)
280+ let timerInterval = TimeInterval(0.05)
240 /// Check the handleTimerFire: function. While it is counting the number of fires, it decreases the mod value so that the value is altered more frequently.
282 /// Check the handleTimerFire: function. While it is counting the number of fires, it decreases the mod value so that the value is altered more frequently.
241 var timerFireCount = 0
283 var timerFireCount = 0
@@ -249,12 +291,12 @@ import UIKit
252− required public init?(coder aDecoder: NSCoder) {
294+ @objc required public init?(coder aDecoder: NSCoder) {
253 super.init(coder: aDecoder)
295 super.init(coder: aDecoder)
257− public override init(frame: CGRect) {
299+ @objc public override init(frame: CGRect) {
258 super.init(frame: frame)
300 super.init(frame: frame)
@@ -267,8 +309,9 @@ import UIKit
267 backgroundColor = buttonsBackgroundColor
309 backgroundColor = buttonsBackgroundColor
268 layer.cornerRadius = cornerRadius
310 layer.cornerRadius = cornerRadius
269 clipsToBounds = true
311 clipsToBounds = true
312+ labelOriginalCenter = label.center
271− NSNotificationCenter.defaultCenter().addObserver(self, selector: "reset", name: UIApplicationWillResignActiveNotification, object: nil)
314+ NotificationCenter.default.addObserver(self, selector: #selector(GMStepper.reset), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
274 public override func layoutSubviews() {
317 public override func layoutSubviews() {
@@ -294,7 +337,7 @@ import UIKit
296 resetTimer()
339 resetTimer()
297− NSNotificationCenter.defaultCenter().removeObserver(self)
340+ NotificationCenter.default.removeObserver(self)
300 /// Useful closure for logging the timer interval. You can call this in the timer handler to test the autorepeat option. Not used in the current implementation.
343 /// Useful closure for logging the timer interval. You can call this in the timer handler to test the autorepeat option. Not used in the current implementation.
@@ -313,17 +356,17 @@ import UIKit
314// MARK: Pan Gesture
357// MARK: Pan Gesture
315extension GMStepper {
358extension GMStepper {
316− func handlePan(gesture: UIPanGestureRecognizer) {
359+ @objc func handlePan(gesture: UIPanGestureRecognizer) {
317 switch gesture.state {
360 switch gesture.state {
318− case .Began:
361+ case .began:
319− leftButton.enabled = false
362+ leftButton.isEnabled = false
320− rightButton.enabled = false
363+ rightButton.isEnabled = false
321− case .Changed:
364+ case .changed:
322− let translation = gesture.translationInView(label)
365+ let translation = gesture.translation(in: label)
323− gesture.setTranslation(CGPointZero, inView: label)
366+ gesture.setTranslation(CGPoint.zero, in: label)
325− let slidingRight = gesture.velocityInView(label).x > 0
368+ let slidingRight = gesture.velocity(in: label).x > 0
326− let slidingLeft = gesture.velocityInView(label).x < 0
369+ let slidingLeft = gesture.velocity(in: label).x < 0
328 // Move the label with pan
371 // Move the label with pan
329 if slidingRight {
372 if slidingRight {
@@ -356,23 +399,23 @@ extension GMStepper {
356 self.rightButton.backgroundColor = self.buttonsBackgroundColor
399 self.rightButton.backgroundColor = self.buttonsBackgroundColor
357 self.leftButton.backgroundColor = self.buttonsBackgroundColor
400 self.leftButton.backgroundColor = self.buttonsBackgroundColor
359− case .Ended, .Cancelled, .Failed:
402+ case .ended, .cancelled, .failed:
366− func reset() {
409+ @objc func reset() {
367 panState = .Stable
410 panState = .Stable
368 stepperState = .Stable
411 stepperState = .Stable
369 resetTimer()
412 resetTimer()
371− leftButton.enabled = true
414+ leftButton.isEnabled = true
372− rightButton.enabled = true
415+ rightButton.isEnabled = true
373− label.userInteractionEnabled = true
416+ label.isUserInteractionEnabled = true
375− UIView.animateWithDuration(self.labelSlideDuration, animations: {
418+ UIView.animate(withDuration: self.labelSlideDuration, animations: {
376 self.label.center = self.labelOriginalCenter
419 self.label.center = self.labelOriginalCenter
377 self.rightButton.backgroundColor = self.buttonsBackgroundColor
420 self.rightButton.backgroundColor = self.buttonsBackgroundColor
378 self.leftButton.backgroundColor = self.buttonsBackgroundColor
421 self.leftButton.backgroundColor = self.buttonsBackgroundColor
@@ -382,9 +425,9 @@ extension GMStepper {
383// MARK: Button Events
426// MARK: Button Events
384extension GMStepper {
427extension GMStepper {
385− func leftButtonTouchDown(button: UIButton) {
428+ @objc func leftButtonTouchDown(button: UIButton) {
386− rightButton.enabled = false
429+ rightButton.isEnabled = false
387− label.userInteractionEnabled = false
430+ label.isUserInteractionEnabled = false
388 resetTimer()
431 resetTimer()
390 if value == minimumValue {
433 if value == minimumValue {
@@ -396,9 +439,9 @@ extension GMStepper {
399− func rightButtonTouchDown(button: UIButton) {
442+ @objc func rightButtonTouchDown(button: UIButton) {
400− leftButton.enabled = false
443+ leftButton.isEnabled = false
401− label.userInteractionEnabled = false
444+ label.isUserInteractionEnabled = false
402 resetTimer()
445 resetTimer()
404 if value == maximumValue {
447 if value == maximumValue {
@@ -409,7 +452,7 @@ extension GMStepper {
412− func buttonTouchUp(button: UIButton) {
455+ @objc func buttonTouchUp(button: UIButton) {
@@ -418,20 +461,20 @@ extension GMStepper {
418extension GMStepper {
461extension GMStepper {
420 func animateSlideLeft() {
463 func animateSlideLeft() {
421− UIView.animateWithDuration(labelSlideDuration) {
464+ UIView.animate(withDuration: labelSlideDuration) {
422 self.label.center.x -= self.labelSlideLength
465 self.label.center.x -= self.labelSlideLength
426 func animateSlideRight() {
469 func animateSlideRight() {
427− UIView.animateWithDuration(labelSlideDuration) {
470+ UIView.animate(withDuration: labelSlideDuration) {
428 self.label.center.x += self.labelSlideLength
471 self.label.center.x += self.labelSlideLength
432 func animateToOriginalPosition() {
475 func animateToOriginalPosition() {
433 if self.label.center != self.labelOriginalCenter {
476 if self.label.center != self.labelOriginalCenter {
434− UIView.animateWithDuration(labelSlideDuration) {
477+ UIView.animate(withDuration: labelSlideDuration) {
435 self.label.center = self.labelOriginalCenter
478 self.label.center = self.labelOriginalCenter
@@ -439,14 +482,14 @@ extension GMStepper {
440 func animateLimitHitIfNeeded() {
483 func animateLimitHitIfNeeded() {
441 if value == minimumValue {
484 if value == minimumValue {
442− animateLimitHitForButton(leftButton)
485+ animateLimitHitForButton(button: leftButton)
443 } else if value == maximumValue {
486 } else if value == maximumValue {
444− animateLimitHitForButton(rightButton)
487+ animateLimitHitForButton(button: rightButton)
448 func animateLimitHitForButton(button: UIButton){
491 func animateLimitHitForButton(button: UIButton){
449− UIView.animateWithDuration(limitHitAnimationDuration) {
492+ UIView.animate(withDuration: limitHitAnimationDuration) {
450 button.backgroundColor = self.limitHitAnimationColor
493 button.backgroundColor = self.limitHitAnimationColor
@@ -454,7 +497,7 @@ extension GMStepper {
455// MARK: Timer
498// MARK: Timer
456extension GMStepper {
499extension GMStepper {
457− func handleTimerFire(timer: NSTimer) {
500+ @objc func handleTimerFire(timer: Timer) {
458 timerFireCount += 1
501 timerFireCount += 1
460 if timerFireCount % timerFireCountModulo == 0 {
503 if timerFireCount % timerFireCountModulo == 0 {
@@ -463,7 +506,7 @@ extension GMStepper {
465 func scheduleTimer() {
508 func scheduleTimer() {
466− timer = NSTimer.scheduledTimerWithTimeInterval(timerInterval, target: self, selector: "handleTimerFire:", userInfo: nil, repeats: true)
509+ timer = Timer.scheduledTimer(timeInterval: timerInterval, target: self, selector: #selector(GMStepper.handleTimerFire), userInfo: nil, repeats: true)
469 func resetTimer() {
512 func resetTimer() {