on master help sign in
16 files changed+536−335
since splitsnapshots
unifiedsplit
.gitignore+2 .swift-version+1 GMStepper.podspec+2−2 GMStepper/GMStepper.swift+269−148 GMStepperExample/GMStepperExample.xcodeproj/project.pbxproj+136−109 GMStepperExample/GMStepperExample/AppDelegate.swift+10−10 GMStepperExample/GMStepperExample/Base.lproj/LaunchScreen.xib+6−2 GMStepperExample/GMStepperExample/Base.lproj/Main.storyboard+71−43 GMStepperExample/GMStepperExample/Info.plist+1−1 GMStepperExample/GMStepperExample/ViewController.swift+3−3 GMStepperExample/GMStepperExampleTests/GMStepperExampleTests.swift+4−4 GMStepperExample/GMStepperExampleTests/Info.plist+1−3 README.md+30−10 Screenshots/screenshot_0.gif Screenshots/screenshot_0.png Screenshots/screenshot_1.gif
.gitignore+2
@@ -31,3 +31,5 @@ DerivedData
3131# Carthage/Checkouts
3232
3333Carthage/Build
34+
35+.DS_Store
.swift-versionnew file+1
@@ -0,0 +1 @@
1+4.0
GMStepper.podspec+2−2
@@ -1,9 +1,9 @@
11Pod::Spec.new do |s|
22 s.name = "GMStepper"
3− s.version = "0.2.0"
3+ s.version = "2.1"
44 s.summary = "A stepper with a sliding label in the middle."
55 s.homepage = "https://github.com/gmertk/GMStepper"
6− s.screenshots = "https://dl.dropboxusercontent.com/u/4397140/pod-screenshots/screenshot_1.png"
6+ s.screenshots = "https://raw.githubusercontent.com/gmertk/GMStepper/master/Screenshots/screenshot_1.gif"
77 s.license = 'MIT'
88 s.author = { "Gunay Mert Karadogan" => "mertkaradogan@gmail.com" }
99 s.source = { :git => "https://github.com/gmertk/GMStepper.git", :tag => s.version.to_s }
GMStepper/GMStepper.swift+269−148
@@ -11,59 +11,79 @@ import UIKit
1111@IBDesignable public class GMStepper: UIControl {
1212
1313 /// Current value of the stepper. Defaults to 0.
14− public var value = 0 {
14+ @objc @IBInspectable public var value: Double = 0 {
1515 didSet {
1616 value = min(maximumValue, max(minimumValue, value))
17− label.text = String(value)
17+
18+ let isInteger = floor(value) == value
19+
20+ //
21+ // If we have items, we will display them as steps
22+ //
23+
24+ if isInteger && stepValue == 1.0 && items.count > 0 {
25+ label.text = items[Int(value)]
26+ }
27+ else if showIntegerIfDoubleIsInteger && isInteger {
28+ label.text = String(stringInterpolationSegment: Int(value))
29+ } else {
30+ label.text = String(stringInterpolationSegment: value)
31+ }
1832
1933 if oldValue != value {
20− sendActionsForControlEvents(.ValueChanged)
34+ sendActions(for: .valueChanged)
2135 }
2236 }
2337 }
2438
2539 /// Minimum value. Must be less than maximumValue. Defaults to 0.
26− @IBInspectable public var minimumValue: Int = 0 {
40+ @objc @IBInspectable public var minimumValue: Double = 0 {
2741 didSet {
28− value = 0
42+ value = min(maximumValue, max(minimumValue, value))
2943 }
3044 }
3145
32− /// Maximum value. Must be more than minimumValue. Defaults to 10.
33− @IBInspectable public var maximumValue: Int = 10 {
46+ /// Maximum value. Must be more than minimumValue. Defaults to 100.
47+ @objc @IBInspectable public var maximumValue: Double = 100 {
3448 didSet {
35− value = 0
49+ value = min(maximumValue, max(minimumValue, value))
3650 }
3751 }
3852
39− /// The same as UIStepper's autorepeat. If true, holding on the buttons or the pan gesture repeatedly alters value. Defaults to true.
40− @IBInspectable public var autorepeat: Bool = true
53+ /// Step/Increment value as in UIStepper. Defaults to 1.
54+ @objc @IBInspectable public var stepValue: Double = 1
55+
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.
57+ @objc @IBInspectable public var autorepeat: Bool = true
58+
59+ /// If the value is integer, it is shown without floating point.
60+ @objc @IBInspectable public var showIntegerIfDoubleIsInteger: Bool = true
4161
42− /// Text on the left button. Be sure that it fits in the button. Defaults to "-".
43− @IBInspectable public var leftButtonText: String = "-" {
62+ /// Text on the left button. Be sure that it fits in the button. Defaults to "−".
63+ @objc @IBInspectable public var leftButtonText: String = "−" {
4464 didSet {
45− leftButton.setTitle(leftButtonText, forState: .Normal)
65+ leftButton.setTitle(leftButtonText, for: .normal)
4666 }
4767 }
4868
4969 /// Text on the right button. Be sure that it fits in the button. Defaults to "+".
50− @IBInspectable public var rightButtonText: String = "+" {
70+ @objc @IBInspectable public var rightButtonText: String = "+" {
5171 didSet {
52− rightButton.setTitle(rightButtonText, forState: .Normal)
72+ rightButton.setTitle(rightButtonText, for: .normal)
5373 }
5474 }
5575
5676 /// Text color of the buttons. Defaults to white.
57− @IBInspectable public var buttonsTextColor: UIColor = UIColor.whiteColor() {
77+ @objc @IBInspectable public var buttonsTextColor: UIColor = UIColor.white {
5878 didSet {
5979 for button in [leftButton, rightButton] {
60− button.setTitleColor(buttonsTextColor, forState: .Normal)
80+ button.setTitleColor(buttonsTextColor, for: .normal)
6181 }
6282 }
6383 }
6484
6585 /// Background color of the buttons. Defaults to dark blue.
66− @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) {
6787 didSet {
6888 for button in [leftButton, rightButton] {
6989 button.backgroundColor = buttonsBackgroundColor
@@ -73,7 +93,7 @@ import UIKit
7393 }
7494
7595 /// Font of the buttons. Defaults to AvenirNext-Bold, 20.0 points in size.
76− public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
96+ @objc public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
7797 didSet {
7898 for button in [leftButton, rightButton] {
7999 button.titleLabel?.font = buttonsFont
@@ -82,70 +102,174 @@ import UIKit
82102 }
83103
84104 /// Text color of the middle label. Defaults to white.
85− @IBInspectable public var labelTextColor: UIColor = UIColor.whiteColor() {
105+ @objc @IBInspectable public var labelTextColor: UIColor = UIColor.white {
86106 didSet {
87107 label.textColor = labelTextColor
88108 }
89109 }
90110
91111 /// Text color of the middle label. Defaults to lighter blue.
92− @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) {
93113 didSet {
94114 label.backgroundColor = labelBackgroundColor
95115 }
96116 }
97117
98118 /// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
99− public var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)! {
119+ @objc public var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)! {
100120 didSet {
101121 label.font = labelFont
102122 }
103123 }
124+ /// Corner radius of the middle label. Defaults to 0.
125+ @objc @IBInspectable public var labelCornerRadius: CGFloat = 0 {
126+ didSet {
127+ label.layer.cornerRadius = labelCornerRadius
128+
129+ }
130+ }
104131
105132 /// Corner radius of the stepper's layer. Defaults to 4.0.
106− @IBInspectable public var cornerRadius: CGFloat = 4.0 {
133+ @objc @IBInspectable public var cornerRadius: CGFloat = 4.0 {
107134 didSet {
108135 layer.cornerRadius = cornerRadius
109136 clipsToBounds = true
110137 }
111138 }
139+
140+ /// Border width of the stepper and middle label's layer. Defaults to 0.0.
141+ @objc @IBInspectable public var borderWidth: CGFloat = 0.0 {
142+ didSet {
143+ layer.borderWidth = borderWidth
144+ label.layer.borderWidth = borderWidth
145+ }
146+ }
147+
148+ /// Color of the border of the stepper and middle label's layer. Defaults to clear color.
149+ @objc @IBInspectable public var borderColor: UIColor = UIColor.clear {
150+ didSet {
151+ layer.borderColor = borderColor.cgColor
152+ label.layer.borderColor = borderColor.cgColor
153+ }
154+ }
112155
113156 /// 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.
114− @IBInspectable public var labelWidthWeight: CGFloat = 0.5 {
157+ @objc @IBInspectable public var labelWidthWeight: CGFloat = 0.5 {
115158 didSet {
116159 labelWidthWeight = min(1, max(0, labelWidthWeight))
117160 setNeedsLayout()
118161 }
119162 }
120163
164+ /// 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)
166+
121167 /**
122168 Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
123169 */
124− private let labelSlideLength: CGFloat = 5
170+ let labelSlideLength: CGFloat = 5
125171
126172 /// Duration of the sliding animation
127− private let labelSlideDuration = NSTimeInterval(0.1)
173+ let labelSlideDuration = TimeInterval(0.1)
174+
175+ /// Duration of the animation when the value hits the limit.
176+ let limitHitAnimationDuration = TimeInterval(0.1)
177+
178+ lazy var leftButton: UIButton = {
179+ let button = UIButton()
180+ button.setTitle(self.leftButtonText, for: .normal)
181+ button.setTitleColor(self.buttonsTextColor, for: .normal)
182+ button.backgroundColor = self.buttonsBackgroundColor
183+ button.titleLabel?.font = self.buttonsFont
184+ button.addTarget(self, action: #selector(GMStepper.leftButtonTouchDown), for: .touchDown)
185+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpInside)
186+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpOutside)
187+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchCancel)
188+ return button
189+ }()
128190
129− /// Duration of the flashing animation when the value hits the limit.
130− private let limitHitAnimationDuration = NSTimeInterval(0.1)
191+ lazy var rightButton: UIButton = {
192+ let button = UIButton()
193+ button.setTitle(self.rightButtonText, for: .normal)
194+ button.setTitleColor(self.buttonsTextColor, for: .normal)
195+ button.backgroundColor = self.buttonsBackgroundColor
196+ button.titleLabel?.font = self.buttonsFont
197+ button.addTarget(self, action: #selector(GMStepper.rightButtonTouchDown), for: .touchDown)
198+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpInside)
199+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpOutside)
200+ button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchCancel)
201+ return button
202+ }()
131203
132− /// Color of the flashing animation on the buttons in case the value hit the limit.
133− @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
204+ lazy var label: UILabel = {
205+ let label = UILabel()
206+ label.textAlignment = .center
207+ if self.showIntegerIfDoubleIsInteger && floor(self.value) == self.value {
208+ label.text = String(stringInterpolationSegment: Int(self.value))
209+ } else {
210+ label.text = String(stringInterpolationSegment: self.value)
211+ }
212+ label.textColor = self.labelTextColor
213+ label.backgroundColor = self.labelBackgroundColor
214+ label.font = self.labelFont
215+ label.layer.cornerRadius = self.labelCornerRadius
216+ label.layer.masksToBounds = true
217+ label.isUserInteractionEnabled = true
218+ let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(GMStepper.handlePan))
219+ panRecognizer.maximumNumberOfTouches = 1
220+ label.addGestureRecognizer(panRecognizer)
221+ return label
222+ }()
134223
135− private let leftButton = UIButton()
136− private let rightButton = UIButton()
137− private let label = UILabel()
224+ var labelOriginalCenter: CGPoint!
225+ var labelMaximumCenterX: CGFloat!
226+ var labelMinimumCenterX: CGFloat!
138227
139− private var labelOriginalCenter: CGPoint!
140− private var labelMaximumCenterX: CGFloat!
141− private var labelMinimumCenterX: CGFloat!
228+ enum LabelPanState {
229+ case Stable, HitRightEdge, HitLeftEdge
230+ }
231+ var panState = LabelPanState.Stable
142232
143− private enum LabelPanState {
144− case Stale, HitRightEdge, HitLeftEdge
233+ enum StepperState {
234+ case Stable, ShouldIncrease, ShouldDecrease
235+ }
236+ var stepperState = StepperState.Stable {
237+ didSet {
238+ if stepperState != .Stable {
239+ updateValue()
240+ if autorepeat {
241+ scheduleTimer()
242+ }
243+ }
244+ }
245+ }
246+
247+
248+ @objc public var items : [String] = [] {
249+ didSet {
250+ let isInteger = floor(value) == value
251+
252+ //
253+ // If we have items, we will display them as steps
254+ //
255+
256+ if isInteger && stepValue == 1.0 && items.count > 0 {
257+
258+ var value = Int(self.value)
259+
260+ if value >= items.count {
261+ value = items.count - 1
262+ self.value = Double(value)
263+ }
264+ else {
265+ label.text = items[value]
266+ }
267+ }
268+ }
145269 }
146− private var panState = LabelPanState.Stale
147270
148− private var timer: NSTimer?
271+ /// Timer used for autorepeat option
272+ var timer: Timer?
149273
150274 /** When UIStepper reaches its top speed, it alters the value with a time interval of ~0.05 sec.
151275 The user pressing and holding on the stepper repeatedly:
@@ -153,11 +277,11 @@ import UIKit
153277 - For the next 1.5 sec, it changes the value every 0.1 sec.
154278 - Then, every 0.05 sec.
155279 */
156− private let timerInterval = NSTimeInterval(0.05)
280+ let timerInterval = TimeInterval(0.05)
157281
158282 /// 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.
159− private var timerFireCount = 0
160− private var timerFireCountModulo: Int {
283+ var timerFireCount = 0
284+ var timerFireCountModulo: Int {
161285 if timerFireCount > 80 {
162286 return 1 // 0.05 sec * 1 = 0.05 sec
163287 } else if timerFireCount > 50 {
@@ -167,61 +291,27 @@ import UIKit
167291 }
168292 }
169293
170− lazy var printTimerGaps: () -> () = {
171− var prevTime: CFAbsoluteTime?
172−
173− return { _ in
174− var now = CFAbsoluteTimeGetCurrent()
175− if let prevTime = prevTime {
176− println(now - prevTime)
177− }
178− prevTime = now
179− }
180− }()
181−
182− required public init(coder aDecoder: NSCoder) {
294+ @objc required public init?(coder aDecoder: NSCoder) {
183295 super.init(coder: aDecoder)
184296 setup()
185297 }
186298
187− public override init(frame: CGRect) {
299+ @objc public override init(frame: CGRect) {
188300 super.init(frame: frame)
189301 setup()
190302 }
191303
192− private func setup() {
193− leftButton.setTitle(leftButtonText, forState: .Normal)
194− leftButton.setTitleColor(buttonsTextColor, forState: .Normal)
195− leftButton.backgroundColor = buttonsBackgroundColor
196− leftButton.titleLabel?.font = buttonsFont
197− leftButton.addTarget(self, action: "leftButtonTouchDown:", forControlEvents: .TouchDown)
198− leftButton.addTarget(self, action: "leftButtonTouchUp:", forControlEvents: UIControlEvents.TouchUpInside)
199− leftButton.addTarget(self, action: "leftButtonTouchUp:", forControlEvents: UIControlEvents.TouchUpOutside)
304+ func setup() {
200305 addSubview(leftButton)
201−
202− rightButton.setTitle(rightButtonText, forState: .Normal)
203− rightButton.setTitleColor(buttonsTextColor, forState: .Normal)
204− rightButton.backgroundColor = buttonsBackgroundColor
205− rightButton.titleLabel?.font = buttonsFont
206− rightButton.addTarget(self, action: "rightButtonTapped:", forControlEvents: .TouchDown)
207− rightButton.addTarget(self, action: "rightButtonTouchUp:", forControlEvents: UIControlEvents.TouchUpInside)
208− rightButton.addTarget(self, action: "rightButtonTouchUp:", forControlEvents: UIControlEvents.TouchUpOutside)
209306 addSubview(rightButton)
210−
211− label.textAlignment = .Center
212− label.text = String(value)
213− label.textColor = labelTextColor
214− label.backgroundColor = labelBackgroundColor
215− label.font = labelFont
216307 addSubview(label)
217308
218309 backgroundColor = buttonsBackgroundColor
219310 layer.cornerRadius = cornerRadius
220311 clipsToBounds = true
312+ labelOriginalCenter = label.center
221313
222− let panRecognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
223− label.userInteractionEnabled = true
224− label.addGestureRecognizer(panRecognizer)
314+ NotificationCenter.default.addObserver(self, selector: #selector(GMStepper.reset), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
225315 }
226316
227317 public override func layoutSubviews() {
@@ -237,16 +327,46 @@ import UIKit
237327 labelOriginalCenter = label.center
238328 }
239329
330+ func updateValue() {
331+ if stepperState == .ShouldIncrease {
332+ value += stepValue
333+ } else if stepperState == .ShouldDecrease {
334+ value -= stepValue
335+ }
336+ }
240337
338+ deinit {
339+ resetTimer()
340+ NotificationCenter.default.removeObserver(self)
341+ }
241342
242− @objc private func handlePan(gesture: UIPanGestureRecognizer) {
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.
344+// lazy var printTimerGaps: () -> () = {
345+// var prevTime: CFAbsoluteTime?
346+//
347+// return { _ in
348+// var now = CFAbsoluteTimeGetCurrent()
349+// if let prevTime = prevTime {
350+// print(now - prevTime)
351+// }
352+// prevTime = now
353+// }
354+// }()
355+}
356+
357+// MARK: Pan Gesture
358+extension GMStepper {
359+ @objc func handlePan(gesture: UIPanGestureRecognizer) {
243360 switch gesture.state {
244− case .Changed:
245− var translation = gesture.translationInView(label)
246− gesture.setTranslation(CGPointZero, inView: label)
361+ case .began:
362+ leftButton.isEnabled = false
363+ rightButton.isEnabled = false
364+ case .changed:
365+ let translation = gesture.translation(in: label)
366+ gesture.setTranslation(CGPoint.zero, in: label)
247367
248− var slidingRight = gesture.velocityInView(label).x > 0
249− var slidingLeft = gesture.velocityInView(label).x < 0
368+ let slidingRight = gesture.velocity(in: label).x > 0
369+ let slidingLeft = gesture.velocity(in: label).x < 0
250370
251371 // Move the label with pan
252372 if slidingRight {
@@ -257,42 +377,45 @@ import UIKit
257377
258378 // When the label hits the edges, increase/decrease value and change button backgrounds
259379 if label.center.x == labelMaximumCenterX {
260−
261− // Increase only once for each pan gesture
380+ // If not hit the right edge before, increase the value and start the timer. If already hit the edge, do nothing. Timer will handle it.
262381 if panState != .HitRightEdge {
263− value += 1
382+ stepperState = .ShouldIncrease
264383 panState = .HitRightEdge
265384 }
266−
267− if value == maximumValue {
268− animateLimitHitForButton(rightButton)
269− }
385+
386+ animateLimitHitIfNeeded()
270387 } else if label.center.x == labelMinimumCenterX {
271−
272388 if panState != .HitLeftEdge {
273− value -= 1
389+ stepperState = .ShouldDecrease
274390 panState = .HitLeftEdge
275391 }
276392
277− if value == minimumValue {
278− animateLimitHitForButton(leftButton)
279− }
393+ animateLimitHitIfNeeded()
280394 } else {
281− leftButton.backgroundColor = buttonsBackgroundColor
282− rightButton.backgroundColor = buttonsBackgroundColor
283− }
395+ panState = .Stable
396+ stepperState = .Stable
397+ resetTimer()
284398
285− case .Ended:
399+ self.rightButton.backgroundColor = self.buttonsBackgroundColor
400+ self.leftButton.backgroundColor = self.buttonsBackgroundColor
401+ }
402+ case .ended, .cancelled, .failed:
286403 reset()
287404 default:
288405 break
289406 }
290407 }
291408
292− private func reset() {
293− panState = .Stale
409+ @objc func reset() {
410+ panState = .Stable
411+ stepperState = .Stable
412+ resetTimer()
413+
414+ leftButton.isEnabled = true
415+ rightButton.isEnabled = true
416+ label.isUserInteractionEnabled = true
294417
295− UIView.animateWithDuration(self.labelSlideDuration, animations: {
418+ UIView.animate(withDuration: self.labelSlideDuration, animations: {
296419 self.label.center = self.labelOriginalCenter
297420 self.rightButton.backgroundColor = self.buttonsBackgroundColor
298421 self.leftButton.backgroundColor = self.buttonsBackgroundColor
@@ -302,93 +425,91 @@ import UIKit
302425
303426// MARK: Button Events
304427extension GMStepper {
305− @objc private func leftButtonTouchDown(button: UIButton) {
428+ @objc func leftButtonTouchDown(button: UIButton) {
429+ rightButton.isEnabled = false
430+ label.isUserInteractionEnabled = false
431+ resetTimer()
432+
306433 if value == minimumValue {
307− animateLimitHitForButton(leftButton)
434+ animateLimitHitIfNeeded()
308435 } else {
309− value -= 1
310− if autorepeat {
311− scheduleTimerWithUserInfo(leftButton)
312− }
436+ stepperState = .ShouldDecrease
313437 animateSlideLeft()
314438 }
315− }
316439
317− @objc private func leftButtonTouchUp(button: UIButton) {
318− reset()
319− stopTimer()
320440 }
321441
322− @objc private func rightButtonTapped(button: UIButton) {
442+ @objc func rightButtonTouchDown(button: UIButton) {
443+ leftButton.isEnabled = false
444+ label.isUserInteractionEnabled = false
445+ resetTimer()
446+
323447 if value == maximumValue {
324− animateLimitHitForButton(rightButton)
448+ animateLimitHitIfNeeded()
325449 } else {
326− value += 1
327− if autorepeat {
328− scheduleTimerWithUserInfo(rightButton)
329− }
450+ stepperState = .ShouldIncrease
330451 animateSlideRight()
331452 }
332453 }
333454
334− @objc private func rightButtonTouchUp(button: UIButton) {
455+ @objc func buttonTouchUp(button: UIButton) {
335456 reset()
336− stopTimer()
337457 }
338458}
339459
340460// MARK: Animations
341461extension GMStepper {
342462
343− private func animateSlideLeft() {
344− UIView.animateWithDuration(labelSlideDuration) {
463+ func animateSlideLeft() {
464+ UIView.animate(withDuration: labelSlideDuration) {
345465 self.label.center.x -= self.labelSlideLength
346466 }
347467 }
348468
349− private func animateSlideRight() {
350− UIView.animateWithDuration(labelSlideDuration) {
469+ func animateSlideRight() {
470+ UIView.animate(withDuration: labelSlideDuration) {
351471 self.label.center.x += self.labelSlideLength
352472 }
353473 }
354474
355− private func animateToOriginalPosition() {
475+ func animateToOriginalPosition() {
356476 if self.label.center != self.labelOriginalCenter {
357− UIView.animateWithDuration(labelSlideDuration) {
477+ UIView.animate(withDuration: labelSlideDuration) {
358478 self.label.center = self.labelOriginalCenter
359479 }
360480 }
361481 }
362482
363− private func animateLimitHitForButton(button: UIButton){
364− UIView.animateWithDuration(limitHitAnimationDuration) {
365− button.backgroundColor = self.limitHitAnimationColor
483+ func animateLimitHitIfNeeded() {
484+ if value == minimumValue {
485+ animateLimitHitForButton(button: leftButton)
486+ } else if value == maximumValue {
487+ animateLimitHitForButton(button: rightButton)
366488 }
367489 }
368490
491+ func animateLimitHitForButton(button: UIButton){
492+ UIView.animate(withDuration: limitHitAnimationDuration) {
493+ button.backgroundColor = self.limitHitAnimationColor
494+ }
495+ }
369496}
370497
498+// MARK: Timer
371499extension GMStepper {
372− func handleTimerFire(timer: NSTimer) {
500+ @objc func handleTimerFire(timer: Timer) {
373501 timerFireCount += 1
374− if timerFireCount % timerFireCountModulo == 0 {
375502
376− if let button = timer.userInfo as? UIButton {
377− if button == leftButton {
378− value -= 1
379− } else {
380− value += 1
381− }
382− }
383−// printTimerGaps()
503+ if timerFireCount % timerFireCountModulo == 0 {
504+ updateValue()
384505 }
385506 }
386507
387− func scheduleTimerWithUserInfo(userInfo: AnyObject?) {
388− timer = NSTimer.scheduledTimerWithTimeInterval(timerInterval, target: self, selector: "handleTimerFire:", userInfo: userInfo, repeats: true)
508+ func scheduleTimer() {
509+ timer = Timer.scheduledTimer(timeInterval: timerInterval, target: self, selector: #selector(GMStepper.handleTimerFire), userInfo: nil, repeats: true)
389510 }
390511
391− func stopTimer() {
512+ func resetTimer() {
392513 if let timer = timer {
393514 timer.invalidate()
394515 self.timer = nil
@@ -397,4 +518,4 @@ extension GMStepper {
397518 }
398519
399520
400−}
521+}
GMStepperExample/GMStepperExample.xcodeproj/project.pbxproj+136−109
@@ -7,18 +7,18 @@
77 objects = {
88
99/* Begin PBXBuildFile section */
10− 3169DD3E1B496C33009791D1 /* GMStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C195B1B48445E005EFEE5 /* GMStepper.swift */; };
10+ 3116C0DB1DA4DD360015AC69 /* GMStepperExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3116C0DA1DA4DD360015AC69 /* GMStepperExampleTests.swift */; };
11+ 3198AC771DA4DEE500311FB3 /* GMStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C195B1B48445E005EFEE5 /* GMStepper.swift */; };
1112 319C19381B4843EB005EFEE5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C19371B4843EB005EFEE5 /* AppDelegate.swift */; };
1213 319C193A1B4843EB005EFEE5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C19391B4843EB005EFEE5 /* ViewController.swift */; };
1314 319C193D1B4843EB005EFEE5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 319C193B1B4843EB005EFEE5 /* Main.storyboard */; };
1415 319C193F1B4843EB005EFEE5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 319C193E1B4843EB005EFEE5 /* Images.xcassets */; };
1516 319C19421B4843EB005EFEE5 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 319C19401B4843EB005EFEE5 /* LaunchScreen.xib */; };
16− 319C194E1B4843EB005EFEE5 /* GMStepperExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C194D1B4843EB005EFEE5 /* GMStepperExampleTests.swift */; };
1717 319C195C1B48445E005EFEE5 /* GMStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C195B1B48445E005EFEE5 /* GMStepper.swift */; };
1818/* End PBXBuildFile section */
1919
2020/* Begin PBXContainerItemProxy section */
21− 319C19481B4843EB005EFEE5 /* PBXContainerItemProxy */ = {
21+ 3116C0DD1DA4DD360015AC69 /* PBXContainerItemProxy */ = {
2222 isa = PBXContainerItemProxy;
2323 containerPortal = 319C192A1B4843EB005EFEE5 /* Project object */;
2424 proxyType = 1;
@@ -28,6 +28,9 @@
2828/* End PBXContainerItemProxy section */
2929
3030/* Begin PBXFileReference section */
31+ 3116C0D81DA4DD360015AC69 /* GMStepperExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GMStepperExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
32+ 3116C0DA1DA4DD360015AC69 /* GMStepperExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GMStepperExampleTests.swift; sourceTree = "<group>"; };
33+ 3116C0DC1DA4DD360015AC69 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3134 319C19321B4843EB005EFEE5 /* GMStepperExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GMStepperExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
3235 319C19361B4843EB005EFEE5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3336 319C19371B4843EB005EFEE5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -35,21 +38,18 @@
3538 319C193C1B4843EB005EFEE5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
3639 319C193E1B4843EB005EFEE5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
3740 319C19411B4843EB005EFEE5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
38− 319C19471B4843EB005EFEE5 /* GMStepperExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GMStepperExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
39− 319C194C1B4843EB005EFEE5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
40− 319C194D1B4843EB005EFEE5 /* GMStepperExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GMStepperExampleTests.swift; sourceTree = "<group>"; };
4141 319C195B1B48445E005EFEE5 /* GMStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMStepper.swift; sourceTree = "<group>"; };
4242/* End PBXFileReference section */
4343
4444/* Begin PBXFrameworksBuildPhase section */
45− 319C192F1B4843EB005EFEE5 /* Frameworks */ = {
45+ 3116C0D51DA4DD360015AC69 /* Frameworks */ = {
4646 isa = PBXFrameworksBuildPhase;
4747 buildActionMask = 2147483647;
4848 files = (
4949 );
5050 runOnlyForDeploymentPostprocessing = 0;
5151 };
52− 319C19441B4843EB005EFEE5 /* Frameworks */ = {
52+ 319C192F1B4843EB005EFEE5 /* Frameworks */ = {
5353 isa = PBXFrameworksBuildPhase;
5454 buildActionMask = 2147483647;
5555 files = (
@@ -59,12 +59,21 @@
5959/* End PBXFrameworksBuildPhase section */
6060
6161/* Begin PBXGroup section */
62+ 3116C0D91DA4DD360015AC69 /* GMStepperExampleTests */ = {
63+ isa = PBXGroup;
64+ children = (
65+ 3116C0DA1DA4DD360015AC69 /* GMStepperExampleTests.swift */,
66+ 3116C0DC1DA4DD360015AC69 /* Info.plist */,
67+ );
68+ path = GMStepperExampleTests;
69+ sourceTree = "<group>";
70+ };
6271 319C19291B4843EB005EFEE5 = {
6372 isa = PBXGroup;
6473 children = (
6574 319C195A1B48445E005EFEE5 /* GMStepper */,
6675 319C19341B4843EB005EFEE5 /* GMStepperExample */,
67− 319C194A1B4843EB005EFEE5 /* GMStepperExampleTests */,
76+ 3116C0D91DA4DD360015AC69 /* GMStepperExampleTests */,
6877 319C19331B4843EB005EFEE5 /* Products */,
6978 );
7079 sourceTree = "<group>";
@@ -73,7 +82,7 @@
7382 isa = PBXGroup;
7483 children = (
7584 319C19321B4843EB005EFEE5 /* GMStepperExample.app */,
76− 319C19471B4843EB005EFEE5 /* GMStepperExampleTests.xctest */,
85+ 3116C0D81DA4DD360015AC69 /* GMStepperExampleTests.xctest */,
7786 );
7887 name = Products;
7988 sourceTree = "<group>";
@@ -99,23 +108,6 @@
99108 name = "Supporting Files";
100109 sourceTree = "<group>";
101110 };
102− 319C194A1B4843EB005EFEE5 /* GMStepperExampleTests */ = {
103− isa = PBXGroup;
104− children = (
105− 319C194D1B4843EB005EFEE5 /* GMStepperExampleTests.swift */,
106− 319C194B1B4843EB005EFEE5 /* Supporting Files */,
107− );
108− path = GMStepperExampleTests;
109− sourceTree = "<group>";
110− };
111− 319C194B1B4843EB005EFEE5 /* Supporting Files */ = {
112− isa = PBXGroup;
113− children = (
114− 319C194C1B4843EB005EFEE5 /* Info.plist */,
115− );
116− name = "Supporting Files";
117− sourceTree = "<group>";
118− };
119111 319C195A1B48445E005EFEE5 /* GMStepper */ = {
120112 isa = PBXGroup;
121113 children = (
@@ -128,6 +120,24 @@
128120/* End PBXGroup section */
129121
130122/* Begin PBXNativeTarget section */
123+ 3116C0D71DA4DD360015AC69 /* GMStepperExampleTests */ = {
124+ isa = PBXNativeTarget;
125+ buildConfigurationList = 3116C0DF1DA4DD360015AC69 /* Build configuration list for PBXNativeTarget "GMStepperExampleTests" */;
126+ buildPhases = (
127+ 3116C0D41DA4DD360015AC69 /* Sources */,
128+ 3116C0D51DA4DD360015AC69 /* Frameworks */,
129+ 3116C0D61DA4DD360015AC69 /* Resources */,
130+ );
131+ buildRules = (
132+ );
133+ dependencies = (
134+ 3116C0DE1DA4DD360015AC69 /* PBXTargetDependency */,
135+ );
136+ name = GMStepperExampleTests;
137+ productName = GMStepperExampleTests;
138+ productReference = 3116C0D81DA4DD360015AC69 /* GMStepperExampleTests.xctest */;
139+ productType = "com.apple.product-type.bundle.unit-test";
140+ };
131141 319C19311B4843EB005EFEE5 /* GMStepperExample */ = {
132142 isa = PBXNativeTarget;
133143 buildConfigurationList = 319C19511B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExample" */;
@@ -145,40 +155,27 @@
145155 productReference = 319C19321B4843EB005EFEE5 /* GMStepperExample.app */;
146156 productType = "com.apple.product-type.application";
147157 };
148− 319C19461B4843EB005EFEE5 /* GMStepperExampleTests */ = {
149− isa = PBXNativeTarget;
150− buildConfigurationList = 319C19541B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExampleTests" */;
151− buildPhases = (
152− 319C19431B4843EB005EFEE5 /* Sources */,
153− 319C19441B4843EB005EFEE5 /* Frameworks */,
154− 319C19451B4843EB005EFEE5 /* Resources */,
155− );
156− buildRules = (
157− );
158− dependencies = (
159− 319C19491B4843EB005EFEE5 /* PBXTargetDependency */,
160− );
161− name = GMStepperExampleTests;
162− productName = GMStepperExampleTests;
163− productReference = 319C19471B4843EB005EFEE5 /* GMStepperExampleTests.xctest */;
164− productType = "com.apple.product-type.bundle.unit-test";
165− };
166158/* End PBXNativeTarget section */
167159
168160/* Begin PBXProject section */
169161 319C192A1B4843EB005EFEE5 /* Project object */ = {
170162 isa = PBXProject;
171163 attributes = {
172− LastSwiftUpdateCheck = 0700;
173− LastUpgradeCheck = 0640;
164+ LastSwiftMigration = 0700;
165+ LastSwiftUpdateCheck = 0800;
166+ LastUpgradeCheck = 0900;
174167 ORGANIZATIONNAME = "Gunay Mert Karadogan";
175168 TargetAttributes = {
176− 319C19311B4843EB005EFEE5 = {
177− CreatedOnToolsVersion = 6.4;
169+ 3116C0D71DA4DD360015AC69 = {
170+ CreatedOnToolsVersion = 8.0;
171+ LastSwiftMigration = 0900;
172+ ProvisioningStyle = Automatic;
173+ TestTargetID = 319C19311B4843EB005EFEE5;
178174 };
179− 319C19461B4843EB005EFEE5 = {
175+ 319C19311B4843EB005EFEE5 = {
180176 CreatedOnToolsVersion = 6.4;
181− TestTargetID = 319C19311B4843EB005EFEE5;
177+ DevelopmentTeam = 289M6XEDV4;
178+ LastSwiftMigration = 0900;
182179 };
183180 };
184181 };
@@ -196,58 +193,58 @@
196193 projectRoot = "";
197194 targets = (
198195 319C19311B4843EB005EFEE5 /* GMStepperExample */,
199− 319C19461B4843EB005EFEE5 /* GMStepperExampleTests */,
196+ 3116C0D71DA4DD360015AC69 /* GMStepperExampleTests */,
200197 );
201198 };
202199/* End PBXProject section */
203200
204201/* Begin PBXResourcesBuildPhase section */
205− 319C19301B4843EB005EFEE5 /* Resources */ = {
202+ 3116C0D61DA4DD360015AC69 /* Resources */ = {
206203 isa = PBXResourcesBuildPhase;
207204 buildActionMask = 2147483647;
208205 files = (
209− 319C193D1B4843EB005EFEE5 /* Main.storyboard in Resources */,
210− 319C19421B4843EB005EFEE5 /* LaunchScreen.xib in Resources */,
211− 319C193F1B4843EB005EFEE5 /* Images.xcassets in Resources */,
212206 );
213207 runOnlyForDeploymentPostprocessing = 0;
214208 };
215− 319C19451B4843EB005EFEE5 /* Resources */ = {
209+ 319C19301B4843EB005EFEE5 /* Resources */ = {
216210 isa = PBXResourcesBuildPhase;
217211 buildActionMask = 2147483647;
218212 files = (
213+ 319C193D1B4843EB005EFEE5 /* Main.storyboard in Resources */,
214+ 319C19421B4843EB005EFEE5 /* LaunchScreen.xib in Resources */,
215+ 319C193F1B4843EB005EFEE5 /* Images.xcassets in Resources */,
219216 );
220217 runOnlyForDeploymentPostprocessing = 0;
221218 };
222219/* End PBXResourcesBuildPhase section */
223220
224221/* Begin PBXSourcesBuildPhase section */
225− 319C192E1B4843EB005EFEE5 /* Sources */ = {
222+ 3116C0D41DA4DD360015AC69 /* Sources */ = {
226223 isa = PBXSourcesBuildPhase;
227224 buildActionMask = 2147483647;
228225 files = (
229− 319C193A1B4843EB005EFEE5 /* ViewController.swift in Sources */,
230− 319C195C1B48445E005EFEE5 /* GMStepper.swift in Sources */,
231− 319C19381B4843EB005EFEE5 /* AppDelegate.swift in Sources */,
226+ 3116C0DB1DA4DD360015AC69 /* GMStepperExampleTests.swift in Sources */,
227+ 3198AC771DA4DEE500311FB3 /* GMStepper.swift in Sources */,
232228 );
233229 runOnlyForDeploymentPostprocessing = 0;
234230 };
235− 319C19431B4843EB005EFEE5 /* Sources */ = {
231+ 319C192E1B4843EB005EFEE5 /* Sources */ = {
236232 isa = PBXSourcesBuildPhase;
237233 buildActionMask = 2147483647;
238234 files = (
239− 319C194E1B4843EB005EFEE5 /* GMStepperExampleTests.swift in Sources */,
240− 3169DD3E1B496C33009791D1 /* GMStepper.swift in Sources */,
235+ 319C193A1B4843EB005EFEE5 /* ViewController.swift in Sources */,
236+ 319C195C1B48445E005EFEE5 /* GMStepper.swift in Sources */,
237+ 319C19381B4843EB005EFEE5 /* AppDelegate.swift in Sources */,
241238 );
242239 runOnlyForDeploymentPostprocessing = 0;
243240 };
244241/* End PBXSourcesBuildPhase section */
245242
246243/* Begin PBXTargetDependency section */
247− 319C19491B4843EB005EFEE5 /* PBXTargetDependency */ = {
244+ 3116C0DE1DA4DD360015AC69 /* PBXTargetDependency */ = {
248245 isa = PBXTargetDependency;
249246 target = 319C19311B4843EB005EFEE5 /* GMStepperExample */;
250− targetProxy = 319C19481B4843EB005EFEE5 /* PBXContainerItemProxy */;
247+ targetProxy = 3116C0DD1DA4DD360015AC69 /* PBXContainerItemProxy */;
251248 };
252249/* End PBXTargetDependency section */
253250
@@ -271,6 +268,44 @@
271268/* End PBXVariantGroup section */
272269
273270/* Begin XCBuildConfiguration section */
271+ 3116C0E01DA4DD360015AC69 /* Debug */ = {
272+ isa = XCBuildConfiguration;
273+ buildSettings = {
274+ BUNDLE_LOADER = "$(TEST_HOST)";
275+ CLANG_ANALYZER_NONNULL = YES;
276+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
277+ CLANG_WARN_SUSPICIOUS_MOVES = YES;
278+ DEBUG_INFORMATION_FORMAT = dwarf;
279+ INFOPLIST_FILE = GMStepperExampleTests/Info.plist;
280+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
281+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
282+ PRODUCT_BUNDLE_IDENTIFIER = com.gmertk.GMStepperExampleTests;
283+ PRODUCT_NAME = "$(TARGET_NAME)";
284+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
285+ SWIFT_SWIFT3_OBJC_INFERENCE = On;
286+ SWIFT_VERSION = 4.0;
287+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GMStepperExample.app/GMStepperExample";
288+ };
289+ name = Debug;
290+ };
291+ 3116C0E11DA4DD360015AC69 /* Release */ = {
292+ isa = XCBuildConfiguration;
293+ buildSettings = {
294+ BUNDLE_LOADER = "$(TEST_HOST)";
295+ CLANG_ANALYZER_NONNULL = YES;
296+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
297+ CLANG_WARN_SUSPICIOUS_MOVES = YES;
298+ INFOPLIST_FILE = GMStepperExampleTests/Info.plist;
299+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
300+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
301+ PRODUCT_BUNDLE_IDENTIFIER = com.gmertk.GMStepperExampleTests;
302+ PRODUCT_NAME = "$(TARGET_NAME)";
303+ SWIFT_SWIFT3_OBJC_INFERENCE = On;
304+ SWIFT_VERSION = 4.0;
305+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GMStepperExample.app/GMStepperExample";
306+ };
307+ name = Release;
308+ };
274309 319C194F1B4843EB005EFEE5 /* Debug */ = {
275310 isa = XCBuildConfiguration;
276311 buildSettings = {
@@ -279,19 +314,28 @@
279314 CLANG_CXX_LIBRARY = "libc++";
280315 CLANG_ENABLE_MODULES = YES;
281316 CLANG_ENABLE_OBJC_ARC = YES;
317+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
282318 CLANG_WARN_BOOL_CONVERSION = YES;
319+ CLANG_WARN_COMMA = YES;
283320 CLANG_WARN_CONSTANT_CONVERSION = YES;
284321 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
285322 CLANG_WARN_EMPTY_BODY = YES;
286323 CLANG_WARN_ENUM_CONVERSION = YES;
324+ CLANG_WARN_INFINITE_RECURSION = YES;
287325 CLANG_WARN_INT_CONVERSION = YES;
326+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
327+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
288328 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
329+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
330+ CLANG_WARN_STRICT_PROTOTYPES = YES;
331+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
289332 CLANG_WARN_UNREACHABLE_CODE = YES;
290333 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
291334 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
292335 COPY_PHASE_STRIP = NO;
293336 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
294337 ENABLE_STRICT_OBJC_MSGSEND = YES;
338+ ENABLE_TESTABILITY = YES;
295339 GCC_C_LANGUAGE_STANDARD = gnu99;
296340 GCC_DYNAMIC_NO_PIC = NO;
297341 GCC_NO_COMMON_BLOCKS = YES;
@@ -323,13 +367,21 @@
323367 CLANG_CXX_LIBRARY = "libc++";
324368 CLANG_ENABLE_MODULES = YES;
325369 CLANG_ENABLE_OBJC_ARC = YES;
370+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
326371 CLANG_WARN_BOOL_CONVERSION = YES;
372+ CLANG_WARN_COMMA = YES;
327373 CLANG_WARN_CONSTANT_CONVERSION = YES;
328374 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
329375 CLANG_WARN_EMPTY_BODY = YES;
330376 CLANG_WARN_ENUM_CONVERSION = YES;
377+ CLANG_WARN_INFINITE_RECURSION = YES;
331378 CLANG_WARN_INT_CONVERSION = YES;
379+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
380+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
332381 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
382+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
383+ CLANG_WARN_STRICT_PROTOTYPES = YES;
384+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
333385 CLANG_WARN_UNREACHABLE_CODE = YES;
334386 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
335387 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@@ -348,6 +400,7 @@
348400 IPHONEOS_DEPLOYMENT_TARGET = 8.4;
349401 MTL_ENABLE_DEBUG_INFO = NO;
350402 SDKROOT = iphoneos;
403+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
351404 VALIDATE_PRODUCT = YES;
352405 };
353406 name = Release;
@@ -356,9 +409,13 @@
356409 isa = XCBuildConfiguration;
357410 buildSettings = {
358411 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
412+ DEVELOPMENT_TEAM = 289M6XEDV4;
359413 INFOPLIST_FILE = GMStepperExample/Info.plist;
360414 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
415+ PRODUCT_BUNDLE_IDENTIFIER = "com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)";
361416 PRODUCT_NAME = "$(TARGET_NAME)";
417+ SWIFT_SWIFT3_OBJC_INFERENCE = Default;
418+ SWIFT_VERSION = 4.0;
362419 };
363420 name = Debug;
364421 };
@@ -366,72 +423,42 @@
366423 isa = XCBuildConfiguration;
367424 buildSettings = {
368425 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
426+ DEVELOPMENT_TEAM = 289M6XEDV4;
369427 INFOPLIST_FILE = GMStepperExample/Info.plist;
370428 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
429+ PRODUCT_BUNDLE_IDENTIFIER = "com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)";
371430 PRODUCT_NAME = "$(TARGET_NAME)";
372− };
373− name = Release;
374− };
375− 319C19551B4843EB005EFEE5 /* Debug */ = {
376− isa = XCBuildConfiguration;
377− buildSettings = {
378− BUNDLE_LOADER = "$(TEST_HOST)";
379− FRAMEWORK_SEARCH_PATHS = (
380− "$(SDKROOT)/Developer/Library/Frameworks",
381− "$(inherited)",
382− );
383− GCC_PREPROCESSOR_DEFINITIONS = (
384− "DEBUG=1",
385− "$(inherited)",
386− );
387− INFOPLIST_FILE = GMStepperExampleTests/Info.plist;
388− LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
389− PRODUCT_NAME = "$(TARGET_NAME)";
390− TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GMStepperExample.app/GMStepperExample";
391− };
392− name = Debug;
393− };
394− 319C19561B4843EB005EFEE5 /* Release */ = {
395− isa = XCBuildConfiguration;
396− buildSettings = {
397− BUNDLE_LOADER = "$(TEST_HOST)";
398− FRAMEWORK_SEARCH_PATHS = (
399− "$(SDKROOT)/Developer/Library/Frameworks",
400− "$(inherited)",
401− );
402− INFOPLIST_FILE = GMStepperExampleTests/Info.plist;
403− LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
404− PRODUCT_NAME = "$(TARGET_NAME)";
405− TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GMStepperExample.app/GMStepperExample";
431+ SWIFT_SWIFT3_OBJC_INFERENCE = Default;
432+ SWIFT_VERSION = 4.0;
406433 };
407434 name = Release;
408435 };
409436/* End XCBuildConfiguration section */
410437
411438/* Begin XCConfigurationList section */
412− 319C192D1B4843EB005EFEE5 /* Build configuration list for PBXProject "GMStepperExample" */ = {
439+ 3116C0DF1DA4DD360015AC69 /* Build configuration list for PBXNativeTarget "GMStepperExampleTests" */ = {
413440 isa = XCConfigurationList;
414441 buildConfigurations = (
415− 319C194F1B4843EB005EFEE5 /* Debug */,
416− 319C19501B4843EB005EFEE5 /* Release */,
442+ 3116C0E01DA4DD360015AC69 /* Debug */,
443+ 3116C0E11DA4DD360015AC69 /* Release */,
417444 );
418445 defaultConfigurationIsVisible = 0;
419446 defaultConfigurationName = Release;
420447 };
421− 319C19511B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExample" */ = {
448+ 319C192D1B4843EB005EFEE5 /* Build configuration list for PBXProject "GMStepperExample" */ = {
422449 isa = XCConfigurationList;
423450 buildConfigurations = (
424− 319C19521B4843EB005EFEE5 /* Debug */,
425− 319C19531B4843EB005EFEE5 /* Release */,
451+ 319C194F1B4843EB005EFEE5 /* Debug */,
452+ 319C19501B4843EB005EFEE5 /* Release */,
426453 );
427454 defaultConfigurationIsVisible = 0;
428455 defaultConfigurationName = Release;
429456 };
430− 319C19541B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExampleTests" */ = {
457+ 319C19511B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExample" */ = {
431458 isa = XCConfigurationList;
432459 buildConfigurations = (
433− 319C19551B4843EB005EFEE5 /* Debug */,
434− 319C19561B4843EB005EFEE5 /* Release */,
460+ 319C19521B4843EB005EFEE5 /* Debug */,
461+ 319C19531B4843EB005EFEE5 /* Release */,
435462 );
436463 defaultConfigurationIsVisible = 0;
437464 defaultConfigurationName = Release;
GMStepperExample/GMStepperExample/AppDelegate.swift+10−10
@@ -14,33 +14,33 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
1414 var window: UIWindow?
1515
1616
17− func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
17+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
1818 // Override point for customization after application launch.
1919 return true
2020 }
2121
22− func applicationWillResignActive(application: UIApplication) {
22+ func applicationWillResignActive(_ application: UIApplication) {
2323 // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24− // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
24+ // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
2525 }
2626
27− func applicationDidEnterBackground(application: UIApplication) {
27+ func applicationDidEnterBackground(_ application: UIApplication) {
2828 // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
2929 // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
3030 }
3131
32− func applicationWillEnterForeground(application: UIApplication) {
33− // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
32+ func applicationWillEnterForeground(_ application: UIApplication) {
33+ // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
3434 }
3535
36− func applicationDidBecomeActive(application: UIApplication) {
36+ func applicationDidBecomeActive(_ application: UIApplication) {
3737 // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
3838 }
3939
40− func applicationWillTerminate(application: UIApplication) {
40+ func applicationWillTerminate(_ application: UIApplication) {
4141 // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
4242 }
43−
44−
43+
44+
4545}
4646
GMStepperExample/GMStepperExample/Base.lproj/LaunchScreen.xib+6−2
@@ -1,7 +1,8 @@
11<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="8191" systemVersion="15A279b" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
33 <dependencies>
4− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
4+ <deployment identifier="iOS"/>
5+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
56 <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
67 </dependencies>
78 <objects>
@@ -13,17 +14,20 @@
1314 <subviews>
1415 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 Gunay Mert Karadogan. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
1516 <rect key="frame" x="20" y="439" width="441" height="21"/>
17+ <animations/>
1618 <fontDescription key="fontDescription" type="system" pointSize="17"/>
1719 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
1820 <nil key="highlightedColor"/>
1921 </label>
2022 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="GMStepperExample" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
2123 <rect key="frame" x="20" y="140" width="441" height="43"/>
24+ <animations/>
2225 <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
2326 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
2427 <nil key="highlightedColor"/>
2528 </label>
2629 </subviews>
30+ <animations/>
2731 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
2832 <constraints>
2933 <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
GMStepperExample/GMStepperExample/Base.lproj/Main.storyboard+71−43
@@ -1,8 +1,10 @@
11<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11201" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="vXZ-lx-hvc">
33 <dependencies>
4− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
4+ <deployment identifier="iOS"/>
5+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
56 <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
7+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
68 </dependencies>
79 <scenes>
810 <!--View Controller-->
@@ -14,105 +16,92 @@
1416 <viewControllerLayoutGuide type="bottom" id="2fi-mo-0CV"/>
1517 </layoutGuides>
1618 <view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
17− <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
19+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
1820 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1921 <subviews>
2022 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AU6-4C-B2F" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
21− <rect key="frame" x="24" y="28" width="552" height="44"/>
22− <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
23+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
2324 <constraints>
2425 <constraint firstAttribute="height" constant="44" id="6mC-hP-wFD"/>
2526 </constraints>
2627 <userDefinedRuntimeAttributes>
27− <userDefinedRuntimeAttribute type="number" keyPath="minimumValue">
28− <integer key="value" value="0"/>
29− </userDefinedRuntimeAttribute>
3028 <userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
31− <integer key="value" value="100"/>
29+ <real key="value" value="25"/>
30+ </userDefinedRuntimeAttribute>
31+ <userDefinedRuntimeAttribute type="number" keyPath="stepValue">
32+ <real key="value" value="5"/>
3233 </userDefinedRuntimeAttribute>
3334 </userDefinedRuntimeAttributes>
3435 </view>
3536 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="if7-U5-bAM" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
36− <rect key="frame" x="140" y="116" width="320" height="66"/>
37− <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
37+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
3838 <constraints>
3939 <constraint firstAttribute="height" constant="66" id="iQi-6P-vt9"/>
4040 <constraint firstAttribute="width" constant="320" id="ji4-Pf-1fZ"/>
4141 </constraints>
4242 <userDefinedRuntimeAttributes>
43− <userDefinedRuntimeAttribute type="number" keyPath="minimumValue">
44− <integer key="value" value="0"/>
45− </userDefinedRuntimeAttribute>
46− <userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
47− <integer key="value" value="100"/>
48− </userDefinedRuntimeAttribute>
4943 <userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor">
50− <color key="value" red="1" green="0.3591497541" blue="0.36842127060000002" alpha="1" colorSpace="calibratedRGB"/>
44+ <color key="value" red="1" green="0.3591497541" blue="0.36842127060000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
5145 </userDefinedRuntimeAttribute>
5246 <userDefinedRuntimeAttribute type="color" keyPath="labelTextColor">
53− <color key="value" red="1" green="0.3591497541" blue="0.36842127060000002" alpha="1" colorSpace="calibratedRGB"/>
47+ <color key="value" red="1" green="0.3591497541" blue="0.36842127060000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
5448 </userDefinedRuntimeAttribute>
5549 <userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor">
56− <color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="calibratedRGB"/>
50+ <color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
5751 </userDefinedRuntimeAttribute>
5852 <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
59− <color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="calibratedRGB"/>
53+ <color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
6054 </userDefinedRuntimeAttribute>
6155 <userDefinedRuntimeAttribute type="color" keyPath="limitHitAnimationColor">
62− <color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="calibratedRGB"/>
56+ <color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
57+ </userDefinedRuntimeAttribute>
58+ <userDefinedRuntimeAttribute type="number" keyPath="stepValue">
59+ <real key="value" value="0.5"/>
6360 </userDefinedRuntimeAttribute>
6461 </userDefinedRuntimeAttributes>
6562 </view>
6663 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="R9n-I0-fq7" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
67− <rect key="frame" x="200" y="226" width="200" height="66"/>
68− <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
64+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
6965 <constraints>
7066 <constraint firstAttribute="width" constant="200" id="dVu-w0-mf0"/>
7167 <constraint firstAttribute="height" constant="66" id="ie8-vl-NAV"/>
7268 </constraints>
7369 <userDefinedRuntimeAttributes>
7470 <userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor">
75− <color key="value" red="0.1819814891" green="0.69426733259999995" blue="0.53024792669999998" alpha="1" colorSpace="calibratedRGB"/>
71+ <color key="value" red="0.1819814891" green="0.69426733259999995" blue="0.53024792669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
7672 </userDefinedRuntimeAttribute>
7773 <userDefinedRuntimeAttribute type="color" keyPath="labelTextColor">
78− <color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
74+ <color key="value" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
7975 </userDefinedRuntimeAttribute>
8076 <userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor">
81− <color key="value" red="0.6588235294" green="0.85882352939999995" blue="0.6588235294" alpha="1" colorSpace="calibratedRGB"/>
77+ <color key="value" red="0.6588235294" green="0.85882352939999995" blue="0.6588235294" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
8278 </userDefinedRuntimeAttribute>
8379 <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
84− <color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
85− </userDefinedRuntimeAttribute>
86− <userDefinedRuntimeAttribute type="number" keyPath="minimumValue">
87− <integer key="value" value="0"/>
88− </userDefinedRuntimeAttribute>
89− <userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
90− <integer key="value" value="100"/>
80+ <color key="value" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
9181 </userDefinedRuntimeAttribute>
9282 <userDefinedRuntimeAttribute type="color" keyPath="limitHitAnimationColor">
93− <color key="value" red="0.6588235294" green="0.85882352939999995" blue="0.6588235294" alpha="1" colorSpace="calibratedRGB"/>
83+ <color key="value" red="0.6588235294" green="0.85882352939999995" blue="0.6588235294" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
9484 </userDefinedRuntimeAttribute>
9585 </userDefinedRuntimeAttributes>
9686 </view>
9787 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="U8X-8Z-7Mo" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
98− <rect key="frame" x="24" y="336" width="552" height="44"/>
99− <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
88+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
10089 <constraints>
10190 <constraint firstAttribute="height" constant="44" id="aF3-T7-dIP"/>
10291 <constraint firstAttribute="width" constant="200" id="vhP-7K-vOu"/>
10392 </constraints>
10493 <userDefinedRuntimeAttributes>
10594 <userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor">
106− <color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="calibratedRGB"/>
95+ <color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
10796 </userDefinedRuntimeAttribute>
10897 <userDefinedRuntimeAttribute type="color" keyPath="labelTextColor">
109− <color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
98+ <color key="value" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
11099 </userDefinedRuntimeAttribute>
111100 <userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor">
112− <color key="value" red="0.98624199629999998" green="0.77968657019999998" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
101+ <color key="value" red="0.98624199629999998" green="0.77968657019999998" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
113102 </userDefinedRuntimeAttribute>
114103 <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
115− <color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
104+ <color key="value" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
116105 </userDefinedRuntimeAttribute>
117106 <userDefinedRuntimeAttribute type="number" keyPath="labelWidthWeight">
118107 <real key="value" value="0.69999999999999996"/>
@@ -123,7 +112,7 @@
123112 <integer key="value" value="0"/>
124113 </userDefinedRuntimeAttribute>
125114 <userDefinedRuntimeAttribute type="color" keyPath="limitHitAnimationColor">
126− <color key="value" red="0.98624199629999998" green="0.77968657019999998" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
115+ <color key="value" red="0.98624199629999998" green="0.77968657019999998" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
127116 </userDefinedRuntimeAttribute>
128117 <userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
129118 <integer key="value" value="100"/>
@@ -135,8 +124,44 @@
135124 </mask>
136125 </variation>
137126 </view>
127+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9Ri-Yr-sRs" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
128+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
129+ <constraints>
130+ <constraint firstAttribute="width" constant="180" id="dhO-Nd-SFk"/>
131+ <constraint firstAttribute="height" constant="50" id="xPS-vU-cZc"/>
132+ </constraints>
133+ <userDefinedRuntimeAttributes>
134+ <userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
135+ <real key="value" value="10"/>
136+ </userDefinedRuntimeAttribute>
137+ <userDefinedRuntimeAttribute type="number" keyPath="stepValue">
138+ <real key="value" value="1"/>
139+ </userDefinedRuntimeAttribute>
140+ <userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
141+ <real key="value" value="2"/>
142+ </userDefinedRuntimeAttribute>
143+ <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
144+ <real key="value" value="4"/>
145+ </userDefinedRuntimeAttribute>
146+ <userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor">
147+ <color key="value" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
148+ </userDefinedRuntimeAttribute>
149+ <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
150+ <color key="value" red="1" green="0.47058823529999999" blue="0.15686274510000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
151+ </userDefinedRuntimeAttribute>
152+ <userDefinedRuntimeAttribute type="color" keyPath="borderColor">
153+ <color key="value" red="1" green="0.47058823529999999" blue="0.15686274510000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
154+ </userDefinedRuntimeAttribute>
155+ <userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor">
156+ <color key="value" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
157+ </userDefinedRuntimeAttribute>
158+ <userDefinedRuntimeAttribute type="color" keyPath="labelTextColor">
159+ <color key="value" red="1" green="0.47058823529999999" blue="0.15686274510000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
160+ </userDefinedRuntimeAttribute>
161+ </userDefinedRuntimeAttributes>
162+ </view>
138163 </subviews>
139− <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
164+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
140165 <constraints>
141166 <constraint firstItem="R9n-I0-fq7" firstAttribute="centerX" secondItem="U8X-8Z-7Mo" secondAttribute="centerX" id="3cD-WW-Qg4"/>
142167 <constraint firstItem="if7-U5-bAM" firstAttribute="top" secondItem="AU6-4C-B2F" secondAttribute="bottom" constant="44" id="662-W9-CUn"/>
@@ -146,7 +171,9 @@
146171 <constraint firstItem="if7-U5-bAM" firstAttribute="centerX" secondItem="R9n-I0-fq7" secondAttribute="centerX" id="K7g-rC-ehJ"/>
147172 <constraint firstItem="U8X-8Z-7Mo" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" constant="8" id="S1b-pD-UXA"/>
148173 <constraint firstItem="U8X-8Z-7Mo" firstAttribute="top" secondItem="R9n-I0-fq7" secondAttribute="bottom" constant="44" id="fBW-Xx-pa0"/>
174+ <constraint firstItem="9Ri-Yr-sRs" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="fay-qT-jKR"/>
149175 <constraint firstItem="AU6-4C-B2F" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" constant="8" id="iIM-b2-jzV"/>
176+ <constraint firstItem="9Ri-Yr-sRs" firstAttribute="top" secondItem="U8X-8Z-7Mo" secondAttribute="bottom" constant="44" id="nKi-Fp-8B7"/>
150177 <constraint firstAttribute="trailingMargin" secondItem="U8X-8Z-7Mo" secondAttribute="trailing" constant="8" id="ppd-nb-fL8"/>
151178 <constraint firstItem="AU6-4C-B2F" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" constant="8" symbolic="YES" id="zDK-gF-kms"/>
152179 </constraints>
@@ -157,6 +184,7 @@
157184 </viewController>
158185 <placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
159186 </objects>
187+ <point key="canvasLocation" x="274" y="363"/>
160188 </scene>
161189 </scenes>
162190</document>
GMStepperExample/GMStepperExample/Info.plist+1−1
@@ -7,7 +7,7 @@
77 <key>CFBundleExecutable</key>
88 <string>$(EXECUTABLE_NAME)</string>
99 <key>CFBundleIdentifier</key>
10− <string>com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)</string>
10+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
1111 <key>CFBundleInfoDictionaryVersion</key>
1212 <string>6.0</string>
1313 <key>CFBundleName</key>
GMStepperExample/GMStepperExample/ViewController.swift+3−3
@@ -13,10 +13,10 @@ class ViewController: UIViewController {
1313 @IBOutlet weak var stepper: GMStepper!
1414 override func viewDidLoad() {
1515 super.viewDidLoad()
16− stepper.addTarget(self, action: "stepperValueChanged:", forControlEvents: .ValueChanged)
16+ stepper.addTarget(self, action: #selector(ViewController.stepperValueChanged), for: .valueChanged)
1717 }
1818
19− func stepperValueChanged(stepper: GMStepper) {
20− println(stepper.value)
19+ @objc func stepperValueChanged(stepper: GMStepper) {
20+ print(stepper.value, terminator: "")
2121 }
2222}
GMStepperExample/GMStepperExampleTests/GMStepperExampleTests.swift+4−4
@@ -6,8 +6,8 @@
66// Copyright (c) 2015 Gunay Mert Karadogan. All rights reserved.
77//
88
9−import UIKit
109import XCTest
10+import UIKit
1111
1212class GMStepperExampleTests: XCTestCase {
1313 var stepper: GMStepper!
@@ -19,7 +19,7 @@ class GMStepperExampleTests: XCTestCase {
1919
2020 func testDefaults() {
2121 XCTAssert(stepper.minimumValue == 0, "minimumValue defaults 0")
22− XCTAssert(stepper.maximumValue == 10, "maximumValue defaults to 10")
22+ XCTAssert(stepper.maximumValue == 100, "maximumValue defaults to 100")
2323 XCTAssert(stepper.value == 0, "value defaults to 0")
2424 }
2525
@@ -37,6 +37,6 @@ class GMStepperExampleTests: XCTestCase {
3737
3838 XCTAssert(stepper.value == stepper.maximumValue, "Pass")
3939 }
40−
41−
40+
41+
4242}
GMStepperExample/GMStepperExampleTests/Info.plist+1−3
@@ -7,7 +7,7 @@
77 <key>CFBundleExecutable</key>
88 <string>$(EXECUTABLE_NAME)</string>
99 <key>CFBundleIdentifier</key>
10− <string>com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)</string>
10+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
1111 <key>CFBundleInfoDictionaryVersion</key>
1212 <string>6.0</string>
1313 <key>CFBundleName</key>
@@ -16,8 +16,6 @@
1616 <string>BNDL</string>
1717 <key>CFBundleShortVersionString</key>
1818 <string>1.0</string>
19− <key>CFBundleSignature</key>
20− <string>????</string>
2119 <key>CFBundleVersion</key>
2220 <string>1</string>
2321</dict>
README.md+30−10
@@ -3,11 +3,12 @@
33[![License](https://img.shields.io/cocoapods/l/GMStepper.svg?style=flat)](http://cocoapods.org/pods/GMStepper)
44[![Platform](https://img.shields.io/cocoapods/p/GMStepper.svg?style=flat)](http://cocoapods.org/pods/GMStepper)
55
6−A stepper with a sliding label in the middle. Pan the label or tap the buttons.
6+A stepper with a sliding label in the middle. Pan the label or tap the buttons. Check out the tutorial, [How to Build a Custom Stepper - Part 1](http://gmertk.github.io/custom-stepper-part-1/).
7+
78
89## Screenshot
910
10−![Screenshot_0.gif](https://raw.githubusercontent.com/gmertk/GMStepper/master/Screenshots/screenshot_0.gif)
11+![Screenshot_1.gif](https://raw.githubusercontent.com/gmertk/GMStepper/master/Screenshots/screenshot_1.gif)
1112
1213
1314## Usage
@@ -19,7 +20,7 @@ it, simply add the following lines to your Podfile:
1920use_frameworks!
2021pod "GMStepper"
2122```
22−
23+
2324## Setup
2425
2526If you want to use storyboards, just drop a UIView into your storyboard and set its class to `GMStepper`. Then customize through the attributes inspector. Otherwise, you can write the code to initialize with frame and set the properties.
@@ -27,13 +28,22 @@ If you want to use storyboards, just drop a UIView into your storyboard and set
2728## Properties
2829```swift
2930/// Current value of the stepper. Defaults to 0.
30−var value: Int = 0
31+var value: Double = 0
3132
3233/// Minimum value. Must be less than maximumValue. Defaults to 0.
33−var minimumValue: Int = 0
34+var minimumValue: Double = 0
35+
36+/// Maximum value. Must be more than minimumValue. Defaults to 100.
37+var maximumValue: Double = 100
38+
39+/// Step/Increment value as in UIStepper. Defaults to 1.
40+var stepValue: Double = 1
3441
35−/// Maximum value. Must be more than minimumValue. Defaults to 10.
36−var maximumValue: Int = 10
42+/// The same as UIStepper's autorepeat. If true, holding on the buttons or keeping the pan gesture alters the value repeatedly. Defaults to true.
43+var autorepeat: Bool = true
44+
45+/// If the value is integer, it is shown without floating point.
46+var showIntegerIfDoubleIsInteger: Bool = true
3747
3848/// Text on the left button. Be sure that it fits in the button. Defaults to "-".
3949var leftButtonText: String = "-"
@@ -59,20 +69,30 @@ var labelBackgroundColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alph
5969/// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
6070var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)
6171
72+/// Corner radius of the middle label's layer. Defaults to 0.0.
73+var labelCornerRadius: CGFloat = 0.0
74+
6275/// Corner radius of the stepper's layer. Defaults to 4.0.
6376var cornerRadius: CGFloat = 4.0
6477
78+/// Border width of the stepper and middle label's layer. Defaults to 0.0.
79+var borderWidth: CGFloat = 0.0
80+
81+/// Color of the border of the stepper and middle label's layer. Defaults to clear color.
82+var borderColor: UIColor = UIColor.clearColor()
83+
6584/// 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.
66−var labelWidthWeight: CGFloat = 0.5
85+var labelWidthWeight: CGFloat = 0.5
6786
6887/// Color of the flashing animation on the buttons in case the value hit the limit.
6988var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
7089```
7190
7291
73−## Author
92+## Authors
7493
75−Günay Mert Karadoğan, mertkaradogan@gmail.com
94+* [Brent Whitman](https://github.com/bwhtmn) - Maintainer
95+* [Günay Mert Karadoğan](https://github.com/gmertk) - Creator
7696
7797## License
7898
Screenshots/screenshot_0.gifdeleted

Binary file not shown.

Screenshots/screenshot_0.pngdeleted

Binary file not shown.

Screenshots/screenshot_1.gifnew file

Binary file not shown.