on master help sign in
16 files changed+726−264
since splitsnapshots
unifiedsplit
.gitignore+2 .swift-version+1 GMStepper.podspec+2−2 GMStepper/GMStepper.swift+370−76 GMStepperExample/GMStepperExample.xcodeproj/project.pbxproj+136−106 GMStepperExample/GMStepperExample/AppDelegate.swift+10−10 GMStepperExample/GMStepperExample/Base.lproj/LaunchScreen.xib+6−2 GMStepperExample/GMStepperExample/Base.lproj/Main.storyboard+76−42 GMStepperExample/GMStepperExample/Info.plist+1−1 GMStepperExample/GMStepperExample/ViewController.swift+3−3 GMStepperExample/GMStepperExampleTests/GMStepperExampleTests.swift+23−17 GMStepperExample/GMStepperExampleTests/Info.plist+1−3 README.md+95−2 Screenshots/screencast_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.1.1"
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+370−76
@@ -11,56 +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− /// Text on the left button. Be sure that it fits in the button. Defaults to "-".
40− @IBInspectable public var leftButtonText: String = "-" {
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
61+
62+ /// Text on the left button. Be sure that it fits in the button. Defaults to "−".
63+ @objc @IBInspectable public var leftButtonText: String = "−" {
4164 didSet {
42− leftButton.setTitle(leftButtonText, forState: .Normal)
65+ leftButton.setTitle(leftButtonText, for: .normal)
4366 }
4467 }
4568
4669 /// Text on the right button. Be sure that it fits in the button. Defaults to "+".
47− @IBInspectable public var rightButtonText: String = "+" {
70+ @objc @IBInspectable public var rightButtonText: String = "+" {
4871 didSet {
49− rightButton.setTitle(rightButtonText, forState: .Normal)
72+ rightButton.setTitle(rightButtonText, for: .normal)
5073 }
5174 }
5275
5376 /// Text color of the buttons. Defaults to white.
54− @IBInspectable public var buttonsTextColor: UIColor = UIColor.whiteColor() {
77+ @objc @IBInspectable public var buttonsTextColor: UIColor = UIColor.white {
5578 didSet {
5679 for button in [leftButton, rightButton] {
57− button.setTitleColor(buttonsTextColor, forState: .Normal)
80+ button.setTitleColor(buttonsTextColor, for: .normal)
5881 }
5982 }
6083 }
6184
6285 /// Background color of the buttons. Defaults to dark blue.
63− @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) {
6487 didSet {
6588 for button in [leftButton, rightButton] {
6689 button.backgroundColor = buttonsBackgroundColor
@@ -70,7 +93,7 @@ import UIKit
7093 }
7194
7295 /// Font of the buttons. Defaults to AvenirNext-Bold, 20.0 points in size.
73− public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
96+ @objc public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
7497 didSet {
7598 for button in [leftButton, rightButton] {
7699 button.titleLabel?.font = buttonsFont
@@ -79,95 +102,216 @@ import UIKit
79102 }
80103
81104 /// Text color of the middle label. Defaults to white.
82− @IBInspectable public var labelTextColor: UIColor = UIColor.whiteColor() {
105+ @objc @IBInspectable public var labelTextColor: UIColor = UIColor.white {
83106 didSet {
84107 label.textColor = labelTextColor
85108 }
86109 }
87110
88111 /// Text color of the middle label. Defaults to lighter blue.
89− @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) {
90113 didSet {
91114 label.backgroundColor = labelBackgroundColor
92115 }
93116 }
94117
95118 /// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
96− public var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)! {
119+ @objc public var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)! {
97120 didSet {
98121 label.font = labelFont
99122 }
100123 }
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+ }
101131
102132 /// Corner radius of the stepper's layer. Defaults to 4.0.
103− @IBInspectable public var cornerRadius: CGFloat = 4.0 {
133+ @objc @IBInspectable public var cornerRadius: CGFloat = 4.0 {
104134 didSet {
105135 layer.cornerRadius = cornerRadius
106136 clipsToBounds = true
107137 }
108138 }
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+ }
109155
110156 /// 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.
111− @IBInspectable public var labelWidthWeight: CGFloat = 0.5 {
157+ @objc @IBInspectable public var labelWidthWeight: CGFloat = 0.5 {
112158 didSet {
113159 labelWidthWeight = min(1, max(0, labelWidthWeight))
114160 setNeedsLayout()
115161 }
116162 }
117163
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+
118167 /**
119168 Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
120169 */
121− private let labelSlideLength: CGFloat = 5
170+ let labelSlideLength: CGFloat = 5
122171
123172 /// Duration of the sliding animation
124− 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+ }()
190+
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+ }()
203+
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+ }()
223+
224+ var labelOriginalCenter: CGPoint!
225+ var labelMaximumCenterX: CGFloat!
226+ var labelMinimumCenterX: CGFloat!
227+
228+ enum LabelPanState {
229+ case Stable, HitRightEdge, HitLeftEdge
230+ }
231+ var panState = LabelPanState.Stable
125232
126− /// Duration of the flashing animation when the value hits the limit.
127− private let limitHitAnimationDuration: NSTimeInterval = 0.1
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+ }
269+ }
128270
129− /// Color of the flashing animation on the buttons in case the value hit the limit.
130− @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
271+ /// Timer used for autorepeat option
272+ var timer: Timer?
131273
132− private let leftButton = UIButton()
133− private let rightButton = UIButton()
134− private let label = UILabel()
274+ /** When UIStepper reaches its top speed, it alters the value with a time interval of ~0.05 sec.
275+ The user pressing and holding on the stepper repeatedly:
276+ - First 2.5 sec, the stepper changes the value every 0.5 sec.
277+ - For the next 1.5 sec, it changes the value every 0.1 sec.
278+ - Then, every 0.05 sec.
279+ */
280+ let timerInterval = TimeInterval(0.05)
281+
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.
283+ var timerFireCount = 0
284+ var timerFireCountModulo: Int {
285+ if timerFireCount > 80 {
286+ return 1 // 0.05 sec * 1 = 0.05 sec
287+ } else if timerFireCount > 50 {
288+ return 2 // 0.05 sec * 2 = 0.1 sec
289+ } else {
290+ return 10 // 0.05 sec * 10 = 0.5 sec
291+ }
292+ }
135293
136− required public init(coder aDecoder: NSCoder) {
294+ @objc required public init?(coder aDecoder: NSCoder) {
137295 super.init(coder: aDecoder)
138296 setup()
139297 }
140298
141− public override init(frame: CGRect) {
299+ @objc public override init(frame: CGRect) {
142300 super.init(frame: frame)
143301 setup()
144302 }
145303
146− private func setup() {
147− leftButton.setTitle(leftButtonText, forState: .Normal)
148− leftButton.setTitleColor(buttonsTextColor, forState: .Normal)
149− leftButton.backgroundColor = buttonsBackgroundColor
150− leftButton.titleLabel?.font = buttonsFont
151− leftButton.addTarget(self, action: "leftButtonTapped:", forControlEvents: .TouchDown)
304+ func setup() {
152305 addSubview(leftButton)
153−
154− rightButton.setTitle(rightButtonText, forState: .Normal)
155− rightButton.setTitleColor(buttonsTextColor, forState: .Normal)
156− rightButton.backgroundColor = buttonsBackgroundColor
157− rightButton.titleLabel?.font = buttonsFont
158− rightButton.addTarget(self, action: "rightButtonTapped:", forControlEvents: .TouchDown)
159306 addSubview(rightButton)
160−
161− label.textAlignment = .Center
162− label.text = String(value)
163− label.textColor = labelTextColor
164− label.backgroundColor = labelBackgroundColor
165− label.font = labelFont
166307 addSubview(label)
167308
168309 backgroundColor = buttonsBackgroundColor
169310 layer.cornerRadius = cornerRadius
170311 clipsToBounds = true
312+ labelOriginalCenter = label.center
313+
314+ NotificationCenter.default.addObserver(self, selector: #selector(GMStepper.reset), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
171315 }
172316
173317 public override func layoutSubviews() {
@@ -177,51 +321,201 @@ import UIKit
177321 leftButton.frame = CGRect(x: 0, y: 0, width: buttonWidth, height: bounds.size.height)
178322 label.frame = CGRect(x: buttonWidth, y: 0, width: labelWidth, height: bounds.size.height)
179323 rightButton.frame = CGRect(x: labelWidth + buttonWidth, y: 0, width: buttonWidth, height: bounds.size.height)
324+
325+ labelMaximumCenterX = label.center.x + labelSlideLength
326+ labelMinimumCenterX = label.center.x - labelSlideLength
327+ labelOriginalCenter = label.center
328+ }
329+
330+ func updateValue() {
331+ if stepperState == .ShouldIncrease {
332+ value += stepValue
333+ } else if stepperState == .ShouldDecrease {
334+ value -= stepValue
335+ }
336+ }
337+
338+ deinit {
339+ resetTimer()
340+ NotificationCenter.default.removeObserver(self)
341+ }
342+
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) {
360+ switch gesture.state {
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)
367+
368+ let slidingRight = gesture.velocity(in: label).x > 0
369+ let slidingLeft = gesture.velocity(in: label).x < 0
370+
371+ // Move the label with pan
372+ if slidingRight {
373+ label.center.x = min(labelMaximumCenterX, label.center.x + translation.x)
374+ } else if slidingLeft {
375+ label.center.x = max(labelMinimumCenterX, label.center.x + translation.x)
376+ }
377+
378+ // When the label hits the edges, increase/decrease value and change button backgrounds
379+ if label.center.x == labelMaximumCenterX {
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.
381+ if panState != .HitRightEdge {
382+ stepperState = .ShouldIncrease
383+ panState = .HitRightEdge
384+ }
385+
386+ animateLimitHitIfNeeded()
387+ } else if label.center.x == labelMinimumCenterX {
388+ if panState != .HitLeftEdge {
389+ stepperState = .ShouldDecrease
390+ panState = .HitLeftEdge
391+ }
392+
393+ animateLimitHitIfNeeded()
394+ } else {
395+ panState = .Stable
396+ stepperState = .Stable
397+ resetTimer()
398+
399+ self.rightButton.backgroundColor = self.buttonsBackgroundColor
400+ self.leftButton.backgroundColor = self.buttonsBackgroundColor
401+ }
402+ case .ended, .cancelled, .failed:
403+ reset()
404+ default:
405+ break
406+ }
180407 }
181408
182− @objc private func leftButtonTapped(button: UIButton) {
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
417+
418+ UIView.animate(withDuration: self.labelSlideDuration, animations: {
419+ self.label.center = self.labelOriginalCenter
420+ self.rightButton.backgroundColor = self.buttonsBackgroundColor
421+ self.leftButton.backgroundColor = self.buttonsBackgroundColor
422+ })
423+ }
424+}
425+
426+// MARK: Button Events
427+extension GMStepper {
428+ @objc func leftButtonTouchDown(button: UIButton) {
429+ rightButton.isEnabled = false
430+ label.isUserInteractionEnabled = false
431+ resetTimer()
432+
183433 if value == minimumValue {
184− animateLimitHitForButton(leftButton)
434+ animateLimitHitIfNeeded()
185435 } else {
186− value -= 1
436+ stepperState = .ShouldDecrease
187437 animateSlideLeft()
188438 }
439+
189440 }
190441
191− @objc private func rightButtonTapped(button: UIButton) {
442+ @objc func rightButtonTouchDown(button: UIButton) {
443+ leftButton.isEnabled = false
444+ label.isUserInteractionEnabled = false
445+ resetTimer()
446+
192447 if value == maximumValue {
193− animateLimitHitForButton(rightButton)
448+ animateLimitHitIfNeeded()
194449 } else {
195− value += 1
450+ stepperState = .ShouldIncrease
196451 animateSlideRight()
197452 }
198453 }
199454
200− private func animateSlideLeft() {
201− UIView.animateWithDuration(labelSlideDuration, animations: {
455+ @objc func buttonTouchUp(button: UIButton) {
456+ reset()
457+ }
458+}
459+
460+// MARK: Animations
461+extension GMStepper {
462+
463+ func animateSlideLeft() {
464+ UIView.animate(withDuration: labelSlideDuration) {
202465 self.label.center.x -= self.labelSlideLength
203− }, completion: { _ in
204− UIView.animateWithDuration(self.labelSlideDuration, animations: {
205− self.label.center.x += self.labelSlideLength
206− })
207− })
466+ }
208467 }
209468
210− private func animateSlideRight() {
211− UIView.animateWithDuration(labelSlideDuration, animations: {
469+ func animateSlideRight() {
470+ UIView.animate(withDuration: labelSlideDuration) {
212471 self.label.center.x += self.labelSlideLength
213− }, completion: { _ in
214− UIView.animateWithDuration(self.labelSlideDuration, animations: {
215− self.label.center.x -= self.labelSlideLength
216− })
217− })
472+ }
473+ }
474+
475+ func animateToOriginalPosition() {
476+ if self.label.center != self.labelOriginalCenter {
477+ UIView.animate(withDuration: labelSlideDuration) {
478+ self.label.center = self.labelOriginalCenter
479+ }
480+ }
481+ }
482+
483+ func animateLimitHitIfNeeded() {
484+ if value == minimumValue {
485+ animateLimitHitForButton(button: leftButton)
486+ } else if value == maximumValue {
487+ animateLimitHitForButton(button: rightButton)
488+ }
218489 }
219490
220− private func animateLimitHitForButton(button: UIButton){
221− UIView.animateWithDuration(limitHitAnimationDuration, animations: {
491+ func animateLimitHitForButton(button: UIButton){
492+ UIView.animate(withDuration: limitHitAnimationDuration) {
222493 button.backgroundColor = self.limitHitAnimationColor
223− }, completion: { _ in
224− button.backgroundColor = self.buttonsBackgroundColor
225− })
494+ }
226495 }
227496}
497+
498+// MARK: Timer
499+extension GMStepper {
500+ @objc func handleTimerFire(timer: Timer) {
501+ timerFireCount += 1
502+
503+ if timerFireCount % timerFireCountModulo == 0 {
504+ updateValue()
505+ }
506+ }
507+
508+ func scheduleTimer() {
509+ timer = Timer.scheduledTimer(timeInterval: timerInterval, target: self, selector: #selector(GMStepper.handleTimerFire), userInfo: nil, repeats: true)
510+ }
511+
512+ func resetTimer() {
513+ if let timer = timer {
514+ timer.invalidate()
515+ self.timer = nil
516+ timerFireCount = 0
517+ }
518+ }
519+
520+
521+}
GMStepperExample/GMStepperExample.xcodeproj/project.pbxproj+136−106
@@ -7,17 +7,18 @@
77 objects = {
88
99/* Begin PBXBuildFile section */
10+ 3116C0DB1DA4DD360015AC69 /* GMStepperExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3116C0DA1DA4DD360015AC69 /* GMStepperExampleTests.swift */; };
11+ 3198AC771DA4DEE500311FB3 /* GMStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C195B1B48445E005EFEE5 /* GMStepper.swift */; };
1012 319C19381B4843EB005EFEE5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C19371B4843EB005EFEE5 /* AppDelegate.swift */; };
1113 319C193A1B4843EB005EFEE5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C19391B4843EB005EFEE5 /* ViewController.swift */; };
1214 319C193D1B4843EB005EFEE5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 319C193B1B4843EB005EFEE5 /* Main.storyboard */; };
1315 319C193F1B4843EB005EFEE5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 319C193E1B4843EB005EFEE5 /* Images.xcassets */; };
1416 319C19421B4843EB005EFEE5 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 319C19401B4843EB005EFEE5 /* LaunchScreen.xib */; };
15− 319C194E1B4843EB005EFEE5 /* GMStepperExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C194D1B4843EB005EFEE5 /* GMStepperExampleTests.swift */; };
1617 319C195C1B48445E005EFEE5 /* GMStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C195B1B48445E005EFEE5 /* GMStepper.swift */; };
1718/* End PBXBuildFile section */
1819
1920/* Begin PBXContainerItemProxy section */
20− 319C19481B4843EB005EFEE5 /* PBXContainerItemProxy */ = {
21+ 3116C0DD1DA4DD360015AC69 /* PBXContainerItemProxy */ = {
2122 isa = PBXContainerItemProxy;
2223 containerPortal = 319C192A1B4843EB005EFEE5 /* Project object */;
2324 proxyType = 1;
@@ -27,6 +28,9 @@
2728/* End PBXContainerItemProxy section */
2829
2930/* 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>"; };
3034 319C19321B4843EB005EFEE5 /* GMStepperExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GMStepperExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
3135 319C19361B4843EB005EFEE5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3236 319C19371B4843EB005EFEE5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -34,21 +38,18 @@
3438 319C193C1B4843EB005EFEE5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
3539 319C193E1B4843EB005EFEE5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
3640 319C19411B4843EB005EFEE5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
37− 319C19471B4843EB005EFEE5 /* GMStepperExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GMStepperExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
38− 319C194C1B4843EB005EFEE5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
39− 319C194D1B4843EB005EFEE5 /* GMStepperExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GMStepperExampleTests.swift; sourceTree = "<group>"; };
4041 319C195B1B48445E005EFEE5 /* GMStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMStepper.swift; sourceTree = "<group>"; };
4142/* End PBXFileReference section */
4243
4344/* Begin PBXFrameworksBuildPhase section */
44− 319C192F1B4843EB005EFEE5 /* Frameworks */ = {
45+ 3116C0D51DA4DD360015AC69 /* Frameworks */ = {
4546 isa = PBXFrameworksBuildPhase;
4647 buildActionMask = 2147483647;
4748 files = (
4849 );
4950 runOnlyForDeploymentPostprocessing = 0;
5051 };
51− 319C19441B4843EB005EFEE5 /* Frameworks */ = {
52+ 319C192F1B4843EB005EFEE5 /* Frameworks */ = {
5253 isa = PBXFrameworksBuildPhase;
5354 buildActionMask = 2147483647;
5455 files = (
@@ -58,12 +59,21 @@
5859/* End PBXFrameworksBuildPhase section */
5960
6061/* 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+ };
6171 319C19291B4843EB005EFEE5 = {
6272 isa = PBXGroup;
6373 children = (
6474 319C195A1B48445E005EFEE5 /* GMStepper */,
6575 319C19341B4843EB005EFEE5 /* GMStepperExample */,
66− 319C194A1B4843EB005EFEE5 /* GMStepperExampleTests */,
76+ 3116C0D91DA4DD360015AC69 /* GMStepperExampleTests */,
6777 319C19331B4843EB005EFEE5 /* Products */,
6878 );
6979 sourceTree = "<group>";
@@ -72,7 +82,7 @@
7282 isa = PBXGroup;
7383 children = (
7484 319C19321B4843EB005EFEE5 /* GMStepperExample.app */,
75− 319C19471B4843EB005EFEE5 /* GMStepperExampleTests.xctest */,
85+ 3116C0D81DA4DD360015AC69 /* GMStepperExampleTests.xctest */,
7686 );
7787 name = Products;
7888 sourceTree = "<group>";
@@ -98,23 +108,6 @@
98108 name = "Supporting Files";
99109 sourceTree = "<group>";
100110 };
101− 319C194A1B4843EB005EFEE5 /* GMStepperExampleTests */ = {
102− isa = PBXGroup;
103− children = (
104− 319C194D1B4843EB005EFEE5 /* GMStepperExampleTests.swift */,
105− 319C194B1B4843EB005EFEE5 /* Supporting Files */,
106− );
107− path = GMStepperExampleTests;
108− sourceTree = "<group>";
109− };
110− 319C194B1B4843EB005EFEE5 /* Supporting Files */ = {
111− isa = PBXGroup;
112− children = (
113− 319C194C1B4843EB005EFEE5 /* Info.plist */,
114− );
115− name = "Supporting Files";
116− sourceTree = "<group>";
117− };
118111 319C195A1B48445E005EFEE5 /* GMStepper */ = {
119112 isa = PBXGroup;
120113 children = (
@@ -127,6 +120,24 @@
127120/* End PBXGroup section */
128121
129122/* 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+ };
130141 319C19311B4843EB005EFEE5 /* GMStepperExample */ = {
131142 isa = PBXNativeTarget;
132143 buildConfigurationList = 319C19511B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExample" */;
@@ -144,39 +155,27 @@
144155 productReference = 319C19321B4843EB005EFEE5 /* GMStepperExample.app */;
145156 productType = "com.apple.product-type.application";
146157 };
147− 319C19461B4843EB005EFEE5 /* GMStepperExampleTests */ = {
148− isa = PBXNativeTarget;
149− buildConfigurationList = 319C19541B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExampleTests" */;
150− buildPhases = (
151− 319C19431B4843EB005EFEE5 /* Sources */,
152− 319C19441B4843EB005EFEE5 /* Frameworks */,
153− 319C19451B4843EB005EFEE5 /* Resources */,
154− );
155− buildRules = (
156− );
157− dependencies = (
158− 319C19491B4843EB005EFEE5 /* PBXTargetDependency */,
159− );
160− name = GMStepperExampleTests;
161− productName = GMStepperExampleTests;
162− productReference = 319C19471B4843EB005EFEE5 /* GMStepperExampleTests.xctest */;
163− productType = "com.apple.product-type.bundle.unit-test";
164− };
165158/* End PBXNativeTarget section */
166159
167160/* Begin PBXProject section */
168161 319C192A1B4843EB005EFEE5 /* Project object */ = {
169162 isa = PBXProject;
170163 attributes = {
171− LastUpgradeCheck = 0640;
164+ LastSwiftMigration = 0700;
165+ LastSwiftUpdateCheck = 0800;
166+ LastUpgradeCheck = 0900;
172167 ORGANIZATIONNAME = "Gunay Mert Karadogan";
173168 TargetAttributes = {
174− 319C19311B4843EB005EFEE5 = {
175− CreatedOnToolsVersion = 6.4;
169+ 3116C0D71DA4DD360015AC69 = {
170+ CreatedOnToolsVersion = 8.0;
171+ LastSwiftMigration = 0900;
172+ ProvisioningStyle = Automatic;
173+ TestTargetID = 319C19311B4843EB005EFEE5;
176174 };
177− 319C19461B4843EB005EFEE5 = {
175+ 319C19311B4843EB005EFEE5 = {
178176 CreatedOnToolsVersion = 6.4;
179− TestTargetID = 319C19311B4843EB005EFEE5;
177+ DevelopmentTeam = 289M6XEDV4;
178+ LastSwiftMigration = 0900;
180179 };
181180 };
182181 };
@@ -194,57 +193,58 @@
194193 projectRoot = "";
195194 targets = (
196195 319C19311B4843EB005EFEE5 /* GMStepperExample */,
197− 319C19461B4843EB005EFEE5 /* GMStepperExampleTests */,
196+ 3116C0D71DA4DD360015AC69 /* GMStepperExampleTests */,
198197 );
199198 };
200199/* End PBXProject section */
201200
202201/* Begin PBXResourcesBuildPhase section */
203− 319C19301B4843EB005EFEE5 /* Resources */ = {
202+ 3116C0D61DA4DD360015AC69 /* Resources */ = {
204203 isa = PBXResourcesBuildPhase;
205204 buildActionMask = 2147483647;
206205 files = (
207− 319C193D1B4843EB005EFEE5 /* Main.storyboard in Resources */,
208− 319C19421B4843EB005EFEE5 /* LaunchScreen.xib in Resources */,
209− 319C193F1B4843EB005EFEE5 /* Images.xcassets in Resources */,
210206 );
211207 runOnlyForDeploymentPostprocessing = 0;
212208 };
213− 319C19451B4843EB005EFEE5 /* Resources */ = {
209+ 319C19301B4843EB005EFEE5 /* Resources */ = {
214210 isa = PBXResourcesBuildPhase;
215211 buildActionMask = 2147483647;
216212 files = (
213+ 319C193D1B4843EB005EFEE5 /* Main.storyboard in Resources */,
214+ 319C19421B4843EB005EFEE5 /* LaunchScreen.xib in Resources */,
215+ 319C193F1B4843EB005EFEE5 /* Images.xcassets in Resources */,
217216 );
218217 runOnlyForDeploymentPostprocessing = 0;
219218 };
220219/* End PBXResourcesBuildPhase section */
221220
222221/* Begin PBXSourcesBuildPhase section */
223− 319C192E1B4843EB005EFEE5 /* Sources */ = {
222+ 3116C0D41DA4DD360015AC69 /* Sources */ = {
224223 isa = PBXSourcesBuildPhase;
225224 buildActionMask = 2147483647;
226225 files = (
227− 319C193A1B4843EB005EFEE5 /* ViewController.swift in Sources */,
228− 319C195C1B48445E005EFEE5 /* GMStepper.swift in Sources */,
229− 319C19381B4843EB005EFEE5 /* AppDelegate.swift in Sources */,
226+ 3116C0DB1DA4DD360015AC69 /* GMStepperExampleTests.swift in Sources */,
227+ 3198AC771DA4DEE500311FB3 /* GMStepper.swift in Sources */,
230228 );
231229 runOnlyForDeploymentPostprocessing = 0;
232230 };
233− 319C19431B4843EB005EFEE5 /* Sources */ = {
231+ 319C192E1B4843EB005EFEE5 /* Sources */ = {
234232 isa = PBXSourcesBuildPhase;
235233 buildActionMask = 2147483647;
236234 files = (
237− 319C194E1B4843EB005EFEE5 /* GMStepperExampleTests.swift in Sources */,
235+ 319C193A1B4843EB005EFEE5 /* ViewController.swift in Sources */,
236+ 319C195C1B48445E005EFEE5 /* GMStepper.swift in Sources */,
237+ 319C19381B4843EB005EFEE5 /* AppDelegate.swift in Sources */,
238238 );
239239 runOnlyForDeploymentPostprocessing = 0;
240240 };
241241/* End PBXSourcesBuildPhase section */
242242
243243/* Begin PBXTargetDependency section */
244− 319C19491B4843EB005EFEE5 /* PBXTargetDependency */ = {
244+ 3116C0DE1DA4DD360015AC69 /* PBXTargetDependency */ = {
245245 isa = PBXTargetDependency;
246246 target = 319C19311B4843EB005EFEE5 /* GMStepperExample */;
247− targetProxy = 319C19481B4843EB005EFEE5 /* PBXContainerItemProxy */;
247+ targetProxy = 3116C0DD1DA4DD360015AC69 /* PBXContainerItemProxy */;
248248 };
249249/* End PBXTargetDependency section */
250250
@@ -268,6 +268,44 @@
268268/* End PBXVariantGroup section */
269269
270270/* 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+ };
271309 319C194F1B4843EB005EFEE5 /* Debug */ = {
272310 isa = XCBuildConfiguration;
273311 buildSettings = {
@@ -276,19 +314,28 @@
276314 CLANG_CXX_LIBRARY = "libc++";
277315 CLANG_ENABLE_MODULES = YES;
278316 CLANG_ENABLE_OBJC_ARC = YES;
317+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
279318 CLANG_WARN_BOOL_CONVERSION = YES;
319+ CLANG_WARN_COMMA = YES;
280320 CLANG_WARN_CONSTANT_CONVERSION = YES;
281321 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
282322 CLANG_WARN_EMPTY_BODY = YES;
283323 CLANG_WARN_ENUM_CONVERSION = YES;
324+ CLANG_WARN_INFINITE_RECURSION = YES;
284325 CLANG_WARN_INT_CONVERSION = YES;
326+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
327+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
285328 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;
286332 CLANG_WARN_UNREACHABLE_CODE = YES;
287333 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
288334 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
289335 COPY_PHASE_STRIP = NO;
290336 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
291337 ENABLE_STRICT_OBJC_MSGSEND = YES;
338+ ENABLE_TESTABILITY = YES;
292339 GCC_C_LANGUAGE_STANDARD = gnu99;
293340 GCC_DYNAMIC_NO_PIC = NO;
294341 GCC_NO_COMMON_BLOCKS = YES;
@@ -320,13 +367,21 @@
320367 CLANG_CXX_LIBRARY = "libc++";
321368 CLANG_ENABLE_MODULES = YES;
322369 CLANG_ENABLE_OBJC_ARC = YES;
370+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
323371 CLANG_WARN_BOOL_CONVERSION = YES;
372+ CLANG_WARN_COMMA = YES;
324373 CLANG_WARN_CONSTANT_CONVERSION = YES;
325374 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
326375 CLANG_WARN_EMPTY_BODY = YES;
327376 CLANG_WARN_ENUM_CONVERSION = YES;
377+ CLANG_WARN_INFINITE_RECURSION = YES;
328378 CLANG_WARN_INT_CONVERSION = YES;
379+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
380+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
329381 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;
330385 CLANG_WARN_UNREACHABLE_CODE = YES;
331386 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
332387 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@@ -345,6 +400,7 @@
345400 IPHONEOS_DEPLOYMENT_TARGET = 8.4;
346401 MTL_ENABLE_DEBUG_INFO = NO;
347402 SDKROOT = iphoneos;
403+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
348404 VALIDATE_PRODUCT = YES;
349405 };
350406 name = Release;
@@ -353,9 +409,13 @@
353409 isa = XCBuildConfiguration;
354410 buildSettings = {
355411 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
412+ DEVELOPMENT_TEAM = 289M6XEDV4;
356413 INFOPLIST_FILE = GMStepperExample/Info.plist;
357414 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
415+ PRODUCT_BUNDLE_IDENTIFIER = "com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)";
358416 PRODUCT_NAME = "$(TARGET_NAME)";
417+ SWIFT_SWIFT3_OBJC_INFERENCE = Default;
418+ SWIFT_VERSION = 4.0;
359419 };
360420 name = Debug;
361421 };
@@ -363,72 +423,42 @@
363423 isa = XCBuildConfiguration;
364424 buildSettings = {
365425 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
426+ DEVELOPMENT_TEAM = 289M6XEDV4;
366427 INFOPLIST_FILE = GMStepperExample/Info.plist;
367428 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
429+ PRODUCT_BUNDLE_IDENTIFIER = "com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)";
368430 PRODUCT_NAME = "$(TARGET_NAME)";
369− };
370− name = Release;
371− };
372− 319C19551B4843EB005EFEE5 /* Debug */ = {
373− isa = XCBuildConfiguration;
374− buildSettings = {
375− BUNDLE_LOADER = "$(TEST_HOST)";
376− FRAMEWORK_SEARCH_PATHS = (
377− "$(SDKROOT)/Developer/Library/Frameworks",
378− "$(inherited)",
379− );
380− GCC_PREPROCESSOR_DEFINITIONS = (
381− "DEBUG=1",
382− "$(inherited)",
383− );
384− INFOPLIST_FILE = GMStepperExampleTests/Info.plist;
385− LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
386− PRODUCT_NAME = "$(TARGET_NAME)";
387− TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GMStepperExample.app/GMStepperExample";
388− };
389− name = Debug;
390− };
391− 319C19561B4843EB005EFEE5 /* Release */ = {
392− isa = XCBuildConfiguration;
393− buildSettings = {
394− BUNDLE_LOADER = "$(TEST_HOST)";
395− FRAMEWORK_SEARCH_PATHS = (
396− "$(SDKROOT)/Developer/Library/Frameworks",
397− "$(inherited)",
398− );
399− INFOPLIST_FILE = GMStepperExampleTests/Info.plist;
400− LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
401− PRODUCT_NAME = "$(TARGET_NAME)";
402− TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GMStepperExample.app/GMStepperExample";
431+ SWIFT_SWIFT3_OBJC_INFERENCE = Default;
432+ SWIFT_VERSION = 4.0;
403433 };
404434 name = Release;
405435 };
406436/* End XCBuildConfiguration section */
407437
408438/* Begin XCConfigurationList section */
409− 319C192D1B4843EB005EFEE5 /* Build configuration list for PBXProject "GMStepperExample" */ = {
439+ 3116C0DF1DA4DD360015AC69 /* Build configuration list for PBXNativeTarget "GMStepperExampleTests" */ = {
410440 isa = XCConfigurationList;
411441 buildConfigurations = (
412− 319C194F1B4843EB005EFEE5 /* Debug */,
413− 319C19501B4843EB005EFEE5 /* Release */,
442+ 3116C0E01DA4DD360015AC69 /* Debug */,
443+ 3116C0E11DA4DD360015AC69 /* Release */,
414444 );
415445 defaultConfigurationIsVisible = 0;
416446 defaultConfigurationName = Release;
417447 };
418− 319C19511B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExample" */ = {
448+ 319C192D1B4843EB005EFEE5 /* Build configuration list for PBXProject "GMStepperExample" */ = {
419449 isa = XCConfigurationList;
420450 buildConfigurations = (
421− 319C19521B4843EB005EFEE5 /* Debug */,
422− 319C19531B4843EB005EFEE5 /* Release */,
451+ 319C194F1B4843EB005EFEE5 /* Debug */,
452+ 319C19501B4843EB005EFEE5 /* Release */,
423453 );
424454 defaultConfigurationIsVisible = 0;
425455 defaultConfigurationName = Release;
426456 };
427− 319C19541B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExampleTests" */ = {
457+ 319C19511B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExample" */ = {
428458 isa = XCConfigurationList;
429459 buildConfigurations = (
430− 319C19551B4843EB005EFEE5 /* Debug */,
431− 319C19561B4843EB005EFEE5 /* Release */,
460+ 319C19521B4843EB005EFEE5 /* Debug */,
461+ 319C19531B4843EB005EFEE5 /* Release */,
432462 );
433463 defaultConfigurationIsVisible = 0;
434464 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+76−42
@@ -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,102 +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="1"/>
28+ <userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
29+ <real key="value" value="25"/>
30+ </userDefinedRuntimeAttribute>
31+ <userDefinedRuntimeAttribute type="number" keyPath="stepValue">
32+ <real key="value" value="5"/>
2933 </userDefinedRuntimeAttribute>
3034 </userDefinedRuntimeAttributes>
3135 </view>
3236 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="if7-U5-bAM" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
33− <rect key="frame" x="140" y="116" width="320" height="66"/>
34− <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"/>
3538 <constraints>
3639 <constraint firstAttribute="height" constant="66" id="iQi-6P-vt9"/>
3740 <constraint firstAttribute="width" constant="320" id="ji4-Pf-1fZ"/>
3841 </constraints>
3942 <userDefinedRuntimeAttributes>
40− <userDefinedRuntimeAttribute type="number" keyPath="minimumValue">
41− <integer key="value" value="7"/>
42− </userDefinedRuntimeAttribute>
43− <userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
44− <integer key="value" value="10"/>
45− </userDefinedRuntimeAttribute>
4643 <userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor">
47− <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"/>
4845 </userDefinedRuntimeAttribute>
4946 <userDefinedRuntimeAttribute type="color" keyPath="labelTextColor">
50− <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"/>
5148 </userDefinedRuntimeAttribute>
5249 <userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor">
53− <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"/>
5451 </userDefinedRuntimeAttribute>
5552 <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
56− <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"/>
5754 </userDefinedRuntimeAttribute>
5855 <userDefinedRuntimeAttribute type="color" keyPath="limitHitAnimationColor">
59− <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"/>
6060 </userDefinedRuntimeAttribute>
6161 </userDefinedRuntimeAttributes>
6262 </view>
6363 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="R9n-I0-fq7" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
64− <rect key="frame" x="200" y="226" width="200" height="66"/>
65− <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"/>
6665 <constraints>
6766 <constraint firstAttribute="width" constant="200" id="dVu-w0-mf0"/>
6867 <constraint firstAttribute="height" constant="66" id="ie8-vl-NAV"/>
6968 </constraints>
7069 <userDefinedRuntimeAttributes>
7170 <userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor">
72− <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"/>
7372 </userDefinedRuntimeAttribute>
7473 <userDefinedRuntimeAttribute type="color" keyPath="labelTextColor">
75− <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"/>
7675 </userDefinedRuntimeAttribute>
7776 <userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor">
78− <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"/>
7978 </userDefinedRuntimeAttribute>
8079 <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
81− <color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
82− </userDefinedRuntimeAttribute>
83− <userDefinedRuntimeAttribute type="number" keyPath="minimumValue">
84− <integer key="value" value="9"/>
85− </userDefinedRuntimeAttribute>
86− <userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
87− <integer key="value" value="13"/>
80+ <color key="value" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
8881 </userDefinedRuntimeAttribute>
8982 <userDefinedRuntimeAttribute type="color" keyPath="limitHitAnimationColor">
90− <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"/>
9184 </userDefinedRuntimeAttribute>
9285 </userDefinedRuntimeAttributes>
9386 </view>
9487 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="U8X-8Z-7Mo" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
95− <rect key="frame" x="24" y="336" width="552" height="44"/>
96− <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"/>
9789 <constraints>
9890 <constraint firstAttribute="height" constant="44" id="aF3-T7-dIP"/>
9991 <constraint firstAttribute="width" constant="200" id="vhP-7K-vOu"/>
10092 </constraints>
10193 <userDefinedRuntimeAttributes>
10294 <userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor">
103− <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"/>
10496 </userDefinedRuntimeAttribute>
10597 <userDefinedRuntimeAttribute type="color" keyPath="labelTextColor">
106− <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"/>
10799 </userDefinedRuntimeAttribute>
108100 <userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor">
109− <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"/>
110102 </userDefinedRuntimeAttribute>
111103 <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
112− <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"/>
113105 </userDefinedRuntimeAttribute>
114106 <userDefinedRuntimeAttribute type="number" keyPath="labelWidthWeight">
115107 <real key="value" value="0.69999999999999996"/>
@@ -117,10 +109,13 @@
117109 <userDefinedRuntimeAttribute type="string" keyPath="leftButtonText" value="😞"/>
118110 <userDefinedRuntimeAttribute type="string" keyPath="rightButtonText" value="☺️"/>
119111 <userDefinedRuntimeAttribute type="number" keyPath="minimumValue">
120− <integer key="value" value="5"/>
112+ <integer key="value" value="0"/>
121113 </userDefinedRuntimeAttribute>
122114 <userDefinedRuntimeAttribute type="color" keyPath="limitHitAnimationColor">
123− <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"/>
116+ </userDefinedRuntimeAttribute>
117+ <userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
118+ <integer key="value" value="100"/>
124119 </userDefinedRuntimeAttribute>
125120 </userDefinedRuntimeAttributes>
126121 <variation key="default">
@@ -129,8 +124,44 @@
129124 </mask>
130125 </variation>
131126 </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>
132163 </subviews>
133− <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"/>
134165 <constraints>
135166 <constraint firstItem="R9n-I0-fq7" firstAttribute="centerX" secondItem="U8X-8Z-7Mo" secondAttribute="centerX" id="3cD-WW-Qg4"/>
136167 <constraint firstItem="if7-U5-bAM" firstAttribute="top" secondItem="AU6-4C-B2F" secondAttribute="bottom" constant="44" id="662-W9-CUn"/>
@@ -140,7 +171,9 @@
140171 <constraint firstItem="if7-U5-bAM" firstAttribute="centerX" secondItem="R9n-I0-fq7" secondAttribute="centerX" id="K7g-rC-ehJ"/>
141172 <constraint firstItem="U8X-8Z-7Mo" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" constant="8" id="S1b-pD-UXA"/>
142173 <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"/>
143175 <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"/>
144177 <constraint firstAttribute="trailingMargin" secondItem="U8X-8Z-7Mo" secondAttribute="trailing" constant="8" id="ppd-nb-fL8"/>
145178 <constraint firstItem="AU6-4C-B2F" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" constant="8" symbolic="YES" id="zDK-gF-kms"/>
146179 </constraints>
@@ -151,6 +184,7 @@
151184 </viewController>
152185 <placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
153186 </objects>
187+ <point key="canvasLocation" x="274" y="363"/>
154188 </scene>
155189 </scenes>
156190</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+23−17
@@ -6,31 +6,37 @@
66// Copyright (c) 2015 Gunay Mert Karadogan. All rights reserved.
77//
88
9−import UIKit
109import XCTest
10+import UIKit
1111
1212class GMStepperExampleTests: XCTestCase {
13−
13+ var stepper: GMStepper!
14+
1415 override func setUp() {
1516 super.setUp()
16− // Put setup code here. This method is called before the invocation of each test method in the class.
17+ stepper = GMStepper()
1718 }
18−
19− override func tearDown() {
20− // Put teardown code here. This method is called after the invocation of each test method in the class.
21− super.tearDown()
19+
20+ func testDefaults() {
21+ XCTAssert(stepper.minimumValue == 0, "minimumValue defaults 0")
22+ XCTAssert(stepper.maximumValue == 100, "maximumValue defaults to 100")
23+ XCTAssert(stepper.value == 0, "value defaults to 0")
2224 }
23−
24− func testExample() {
25− // This is an example of a functional test case.
26− XCTAssert(true, "Pass")
25+
26+ func testMinimum_limitsValue() {
27+ stepper.minimumValue = 1
28+ stepper.value = 0
29+
30+ XCTAssert(stepper.value == stepper.minimumValue, "Pass")
2731 }
28−
29− func testPerformanceExample() {
30− // This is an example of a performance test case.
31− self.measureBlock() {
32− // Put the code you want to measure the time of here.
33− }
32+
33+ func testMaximum_limitsValue() {
34+ let stepper = GMStepper()
35+ stepper.maximumValue = 1
36+ stepper.value = 2
37+
38+ XCTAssert(stepper.value == stepper.maximumValue, "Pass")
3439 }
3540
41+
3642}
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+95−2
@@ -3,6 +3,99 @@
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.
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+
8+
9+## Screenshot
10+
11+![Screenshot_1.gif](https://raw.githubusercontent.com/gmertk/GMStepper/master/Screenshots/screenshot_1.gif)
12+
13+
14+## Usage
15+
16+GMStepper is available through [CocoaPods](http://cocoapods.org). To install
17+it, simply add the following lines to your Podfile:
18+
19+```ruby
20+use_frameworks!
21+pod "GMStepper"
22+```
23+
24+## Setup
25+
26+If 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+
28+## Properties
29+```swift
30+/// Current value of the stepper. Defaults to 0.
31+var value: Double = 0
32+
33+/// Minimum value. Must be less than maximumValue. Defaults to 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
41+
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
47+
48+/// Text on the left button. Be sure that it fits in the button. Defaults to "-".
49+var leftButtonText: String = "-"
50+
51+/// Text on the right button. Be sure that it fits in the button. Defaults to "+".
52+var rightButtonText: String = "+"
53+
54+/// Text color of the buttons. Defaults to white
55+var buttonsTextColor: UIColor = UIColor.whiteColor()
56+
57+/// Background color of the buttons. Defaults to dark blue.
58+var buttonsBackgroundColor: UIColor = UIColor(red:0.21, green:0.5, blue:0.74, alpha:1)
59+
60+/// Font of the buttons. Defaults to AvenirNext-Bold, 20.0 points in size.
61+var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)
62+
63+/// Text color of the middle label. Defaults to white.
64+var labelTextColor: UIColor = UIColor.whiteColor()
65+
66+/// Text color of the middle label. Defaults to lighter blue.
67+var labelBackgroundColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
68+
69+/// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
70+var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)
71+
72+/// Corner radius of the middle label's layer. Defaults to 0.0.
73+var labelCornerRadius: CGFloat = 0.0
74+
75+/// Corner radius of the stepper's layer. Defaults to 4.0.
76+var cornerRadius: CGFloat = 4.0
77+
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+
84+/// 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.
85+var labelWidthWeight: CGFloat = 0.5
86+
87+/// Color of the flashing animation on the buttons in case the value hit the limit.
88+var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
89+```
90+
91+
92+## Authors
93+
94+* [Brent Whitman](https://github.com/bwhtmn) - Maintainer
95+* [Günay Mert Karadoğan](https://github.com/gmertk) - Creator
96+
97+## License
98+
99+GMStepper is available under the MIT license. See the LICENSE file for more info.
100+
7101
8−![Screenshot 0](https://raw.githubusercontent.com/gmertk/GMStepper/master/Screenshots/screenshot_0.png)
Screenshots/screencast_0.gifdeleted

Binary file not shown.

Screenshots/screenshot_0.pngdeleted

Binary file not shown.

Screenshots/screenshot_1.gifnew file

Binary file not shown.