on master help sign in
.gitignore+2
since splitsnapshots
unifiedsplit
@@ -31,3 +31,5 @@ DerivedData
31# Carthage/Checkouts
31# Carthage/Checkouts
32
32
33Carthage/Build
33Carthage/Build
34+
35+.DS_Store
.swift-versionnew file+1
@@ -0,0 +1 @@
1+4.0
GMStepper.podspec+2−2
@@ -1,9 +1,9 @@
1Pod::Spec.new do |s|
1Pod::Spec.new do |s|
2 s.name = "GMStepper"
2 s.name = "GMStepper"
3− s.version = "0.2.0"
3+ s.version = "2.1"
4 s.summary = "A stepper with a sliding label in the middle."
4 s.summary = "A stepper with a sliding label in the middle."
5 s.homepage = "https://github.com/gmertk/GMStepper"
5 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"
7 s.license = 'MIT'
7 s.license = 'MIT'
8 s.author = { "Gunay Mert Karadogan" => "mertkaradogan@gmail.com" }
8 s.author = { "Gunay Mert Karadogan" => "mertkaradogan@gmail.com" }
9 s.source = { :git => "https://github.com/gmertk/GMStepper.git", :tag => s.version.to_s }
9 s.source = { :git => "https://github.com/gmertk/GMStepper.git", :tag => s.version.to_s }
GMStepper/GMStepper.swift+342−126
@@ -11,56 +11,79 @@ import UIKit
11@IBDesignable public class GMStepper: UIControl {
11@IBDesignable public class GMStepper: UIControl {
12
12
13 /// Current value of the stepper. Defaults to 0.
13 /// Current value of the stepper. Defaults to 0.
14− public var value = 0 {
14+ @objc @IBInspectable public var value: Double = 0 {
15 didSet {
15 didSet {
16 value = min(maximumValue, max(minimumValue, value))
16 value = min(maximumValue, max(minimumValue, 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 {
17− label.text = String(value)
30+ label.text = String(stringInterpolationSegment: value)
31+ }
18
32
19 if oldValue != value {
33 if oldValue != value {
20− sendActionsForControlEvents(.ValueChanged)
34+ sendActions(for: .valueChanged)
21 }
35 }
22 }
36 }
23 }
37 }
24
38
25 /// Minimum value. Must be less than maximumValue. Defaults to 0.
39 /// 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 {
27 didSet {
41 didSet {
28− value = 0
42+ value = min(maximumValue, max(minimumValue, value))
29 }
43 }
30 }
44 }
31
45
32− /// Maximum value. Must be more than minimumValue. Defaults to 10.
46+ /// Maximum value. Must be more than minimumValue. Defaults to 100.
33− @IBInspectable public var maximumValue: Int = 10 {
47+ @objc @IBInspectable public var maximumValue: Double = 100 {
34 didSet {
48 didSet {
35− value = 0
49+ value = min(maximumValue, max(minimumValue, value))
36 }
50 }
37 }
51 }
38
52
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+
39− /// Text on the left button. Be sure that it fits in the button. Defaults to "-".
62+ /// Text on the left button. Be sure that it fits in the button. Defaults to "−".
40− @IBInspectable public var leftButtonText: String = "-" {
63+ @objc @IBInspectable public var leftButtonText: String = "−" {
41 didSet {
64 didSet {
42− leftButton.setTitle(leftButtonText, forState: .Normal)
65+ leftButton.setTitle(leftButtonText, for: .normal)
43 }
66 }
44 }
67 }
45
68
46 /// Text on the right button. Be sure that it fits in the button. Defaults to "+".
69 /// Text on the right button. Be sure that it fits in the button. Defaults to "+".
47− @IBInspectable public var rightButtonText: String = "+" {
70+ @objc @IBInspectable public var rightButtonText: String = "+" {
48 didSet {
71 didSet {
49− rightButton.setTitle(rightButtonText, forState: .Normal)
72+ rightButton.setTitle(rightButtonText, for: .normal)
50 }
73 }
51 }
74 }
52
75
53 /// Text color of the buttons. Defaults to white.
76 /// 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 {
55 didSet {
78 didSet {
56 for button in [leftButton, rightButton] {
79 for button in [leftButton, rightButton] {
57− button.setTitleColor(buttonsTextColor, forState: .Normal)
80+ button.setTitleColor(buttonsTextColor, for: .normal)
58 }
81 }
59 }
82 }
60 }
83 }
61
84
62 /// Background color of the buttons. Defaults to dark blue.
85 /// 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) {
64 didSet {
87 didSet {
65 for button in [leftButton, rightButton] {
88 for button in [leftButton, rightButton] {
66 button.backgroundColor = buttonsBackgroundColor
89 button.backgroundColor = buttonsBackgroundColor
@@ -70,7 +93,7 @@ import UIKit
70 }
93 }
71
94
72 /// Font of the buttons. Defaults to AvenirNext-Bold, 20.0 points in size.
95 /// Font of the buttons. Defaults to AvenirNext-Bold, 20.0 points in size.
73− public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
96+ @objc public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
74 didSet {
97 didSet {
75 for button in [leftButton, rightButton] {
98 for button in [leftButton, rightButton] {
76 button.titleLabel?.font = buttonsFont
99 button.titleLabel?.font = buttonsFont
@@ -79,108 +102,216 @@ import UIKit
79 }
102 }
80
103
81 /// Text color of the middle label. Defaults to white.
104 /// 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 {
83 didSet {
106 didSet {
84 label.textColor = labelTextColor
107 label.textColor = labelTextColor
85 }
108 }
86 }
109 }
87
110
88 /// Text color of the middle label. Defaults to lighter blue.
111 /// 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) {
90 didSet {
113 didSet {
91 label.backgroundColor = labelBackgroundColor
114 label.backgroundColor = labelBackgroundColor
92 }
115 }
93 }
116 }
94
117
95 /// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
118 /// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
96− public var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)! {
119+ @objc public var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)! {
97 didSet {
120 didSet {
98 label.font = labelFont
121 label.font = labelFont
99 }
122 }
100 }
123 }
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+ }
101
131
102 /// Corner radius of the stepper's layer. Defaults to 4.0.
132 /// 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 {
104 didSet {
134 didSet {
105 layer.cornerRadius = cornerRadius
135 layer.cornerRadius = cornerRadius
106 clipsToBounds = true
136 clipsToBounds = true
107 }
137 }
108 }
138 }
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+ }
109
155
110 /// Percentage of the middle label's width. Must be between 0 and 1. Defaults to 0.5. Be sure that it is wide enough to show the value.
156 /// Percentage of the middle label's width. Must be between 0 and 1. Defaults to 0.5. Be sure that it is wide enough to show the value.
111− @IBInspectable public var labelWidthWeight: CGFloat = 0.5 {
157+ @objc @IBInspectable public var labelWidthWeight: CGFloat = 0.5 {
112 didSet {
158 didSet {
113 labelWidthWeight = min(1, max(0, labelWidthWeight))
159 labelWidthWeight = min(1, max(0, labelWidthWeight))
114 setNeedsLayout()
160 setNeedsLayout()
115 }
161 }
116 }
162 }
117
163
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+
118 /**
167 /**
119 Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
168 Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
120 */
169 */
121− private let labelSlideLength: CGFloat = 5
170+ let labelSlideLength: CGFloat = 5
122
171
123 /// Duration of the sliding animation
172 /// 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+ }()
125
223
126− /// Duration of the flashing animation when the value hits the limit.
224+ var labelOriginalCenter: CGPoint!
127− private let limitHitAnimationDuration: NSTimeInterval = 0.1
225+ var labelMaximumCenterX: CGFloat!
226+ var labelMinimumCenterX: CGFloat!
128
227
129− /// Color of the flashing animation on the buttons in case the value hit the limit.
228+ enum LabelPanState {
130− @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
229+ case Stable, HitRightEdge, HitLeftEdge
230+ }
231+ var panState = LabelPanState.Stable
131
232
132− private let leftButton = UIButton()
233+ enum StepperState {
133− private let rightButton = UIButton()
234+ case Stable, ShouldIncrease, ShouldDecrease
134− private let label = UILabel()
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+ }
135
270
136− private var labelOriginalCenter: CGPoint!
271+ /// Timer used for autorepeat option
137− private var labelMaximumCenterX: CGFloat!
272+ var timer: Timer?
138− private var labelMinimumCenterX: CGFloat!
139
273
140− private enum LabelPanState {
274+ /** When UIStepper reaches its top speed, it alters the value with a time interval of ~0.05 sec.
141− case Stale, HitRightEdge, HitLeftEdge
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+ }
142 }
292 }
143− private var panState = LabelPanState.Stale
144
293
145− required public init(coder aDecoder: NSCoder) {
294+ @objc required public init?(coder aDecoder: NSCoder) {
146 super.init(coder: aDecoder)
295 super.init(coder: aDecoder)
147 setup()
296 setup()
148 }
297 }
149
298
150− public override init(frame: CGRect) {
299+ @objc public override init(frame: CGRect) {
151 super.init(frame: frame)
300 super.init(frame: frame)
152 setup()
301 setup()
153 }
302 }
154
303
155− private func setup() {
304+ func setup() {
156− leftButton.setTitle(leftButtonText, forState: .Normal)
157− leftButton.setTitleColor(buttonsTextColor, forState: .Normal)
158− leftButton.backgroundColor = buttonsBackgroundColor
159− leftButton.titleLabel?.font = buttonsFont
160− leftButton.addTarget(self, action: "leftButtonTapped:", forControlEvents: .TouchDown)
161 addSubview(leftButton)
305 addSubview(leftButton)
162−
163− rightButton.setTitle(rightButtonText, forState: .Normal)
164− rightButton.setTitleColor(buttonsTextColor, forState: .Normal)
165− rightButton.backgroundColor = buttonsBackgroundColor
166− rightButton.titleLabel?.font = buttonsFont
167− rightButton.addTarget(self, action: "rightButtonTapped:", forControlEvents: .TouchDown)
168 addSubview(rightButton)
306 addSubview(rightButton)
169−
170− label.textAlignment = .Center
171− label.text = String(value)
172− label.textColor = labelTextColor
173− label.backgroundColor = labelBackgroundColor
174− label.font = labelFont
175 addSubview(label)
307 addSubview(label)
176
308
177 backgroundColor = buttonsBackgroundColor
309 backgroundColor = buttonsBackgroundColor
178 layer.cornerRadius = cornerRadius
310 layer.cornerRadius = cornerRadius
179 clipsToBounds = true
311 clipsToBounds = true
312+ labelOriginalCenter = label.center
180
313
181− let panRecognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
314+ NotificationCenter.default.addObserver(self, selector: #selector(GMStepper.reset), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
182− label.userInteractionEnabled = true
183− label.addGestureRecognizer(panRecognizer)
184 }
315 }
185
316
186 public override func layoutSubviews() {
317 public override func layoutSubviews() {
@@ -196,60 +327,46 @@ import UIKit
196 labelOriginalCenter = label.center
327 labelOriginalCenter = label.center
197 }
328 }
198
329
199− @objc private func leftButtonTapped(button: UIButton) {
330+ func updateValue() {
200− if value == minimumValue {
331+ if stepperState == .ShouldIncrease {
201− animateLimitHitForButton(leftButton)
332+ value += stepValue
202− } else {
333+ } else if stepperState == .ShouldDecrease {
203− value -= 1
334+ value -= stepValue
204− animateSlideLeft()
205− }
335+ }
206 }
336 }
207
337
208− @objc private func rightButtonTapped(button: UIButton) {
338+ deinit {
209− if value == maximumValue {
339+ resetTimer()
210− animateLimitHitForButton(rightButton)
340+ NotificationCenter.default.removeObserver(self)
211− } else {
212− value += 1
213− animateSlideRight()
214− }
215 }
341 }
216
342
217− private func animateSlideLeft() {
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.
218− UIView.animateWithDuration(labelSlideDuration, animations: {
344+// lazy var printTimerGaps: () -> () = {
219− self.label.center.x -= self.labelSlideLength
345+// var prevTime: CFAbsoluteTime?
220− }, completion: { _ in
346+//
221− UIView.animateWithDuration(self.labelSlideDuration, animations: {
347+// return { _ in
222− self.label.center.x += self.labelSlideLength
348+// var now = CFAbsoluteTimeGetCurrent()
223− })
349+// if let prevTime = prevTime {
224− })
350+// print(now - prevTime)
225− }
351+// }
226−
352+// prevTime = now
227− private func animateSlideRight() {
228− UIView.animateWithDuration(labelSlideDuration, animations: {
229− self.label.center.x += self.labelSlideLength
230− }, completion: { _ in
231− UIView.animateWithDuration(self.labelSlideDuration, animations: {
232− self.label.center.x -= self.labelSlideLength
233− })
234− })
235− }
236−
237− private func animateLimitHitForButton(button: UIButton){
238− UIView.animateWithDuration(limitHitAnimationDuration, animations: {
239− button.backgroundColor = self.limitHitAnimationColor
240− }, completion: { _ in
241− button.backgroundColor = self.buttonsBackgroundColor
242− })
353+// }
354+// }()
243− }
355+}
244
356
357+// MARK: Pan Gesture
358+extension GMStepper {
245− @objc private func handlePan(gesture: UIPanGestureRecognizer) {
359+ @objc func handlePan(gesture: UIPanGestureRecognizer) {
246 switch gesture.state {
360 switch gesture.state {
247− case .Changed:
361+ case .began:
362+ leftButton.isEnabled = false
363+ rightButton.isEnabled = false
364+ case .changed:
248− var translation = gesture.translationInView(label)
365+ let translation = gesture.translation(in: label)
249− gesture.setTranslation(CGPointZero, inView: label)
366+ gesture.setTranslation(CGPoint.zero, in: label)
250
367
251− var slidingRight = gesture.velocityInView(label).x > 0
368+ let slidingRight = gesture.velocity(in: label).x > 0
252− var slidingLeft = gesture.velocityInView(label).x < 0
369+ let slidingLeft = gesture.velocity(in: label).x < 0
253
370
254 // Move the label with pan
371 // Move the label with pan
255 if slidingRight {
372 if slidingRight {
@@ -260,46 +377,145 @@ import UIKit
260
377
261 // When the label hits the edges, increase/decrease value and change button backgrounds
378 // When the label hits the edges, increase/decrease value and change button backgrounds
262 if label.center.x == labelMaximumCenterX {
379 if label.center.x == labelMaximumCenterX {
263−
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.
264− // Increase only once for each pan gesture
265 if panState != .HitRightEdge {
381 if panState != .HitRightEdge {
266− value += 1
382+ stepperState = .ShouldIncrease
267 panState = .HitRightEdge
383 panState = .HitRightEdge
268 }
384 }
269−
385+
270− if value == maximumValue {
386+ animateLimitHitIfNeeded()
271− rightButton.backgroundColor = self.limitHitAnimationColor
272− }
273 } else if label.center.x == labelMinimumCenterX {
387 } else if label.center.x == labelMinimumCenterX {
274−
275 if panState != .HitLeftEdge {
388 if panState != .HitLeftEdge {
276− value -= 1
389+ stepperState = .ShouldDecrease
277 panState = .HitLeftEdge
390 panState = .HitLeftEdge
278 }
391 }
279
392
280− if value == minimumValue {
393+ animateLimitHitIfNeeded()
281− leftButton.backgroundColor = self.limitHitAnimationColor
282− }
283 } else {
394 } else {
284− leftButton.backgroundColor = buttonsBackgroundColor
395+ panState = .Stable
285− rightButton.backgroundColor = buttonsBackgroundColor
396+ stepperState = .Stable
286− }
397+ resetTimer()
287
398
288− case .Ended:
399+ self.rightButton.backgroundColor = self.buttonsBackgroundColor
400+ self.leftButton.backgroundColor = self.buttonsBackgroundColor
401+ }
402+ case .ended, .cancelled, .failed:
289 reset()
403 reset()
290 default:
404 default:
291 break
405 break
292 }
406 }
293 }
407 }
294
408
295− private func reset() {
409+ @objc func reset() {
296− panState = .Stale
410+ panState = .Stable
411+ stepperState = .Stable
412+ resetTimer()
297
413
414+ leftButton.isEnabled = true
415+ rightButton.isEnabled = true
416+ label.isUserInteractionEnabled = true
417+
298− UIView.animateWithDuration(self.labelSlideDuration, animations: {
418+ UIView.animate(withDuration: self.labelSlideDuration, animations: {
299 self.label.center = self.labelOriginalCenter
419 self.label.center = self.labelOriginalCenter
300 self.rightButton.backgroundColor = self.buttonsBackgroundColor
420 self.rightButton.backgroundColor = self.buttonsBackgroundColor
301 self.leftButton.backgroundColor = self.buttonsBackgroundColor
421 self.leftButton.backgroundColor = self.buttonsBackgroundColor
302 })
422 })
303 }
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+
433+ if value == minimumValue {
434+ animateLimitHitIfNeeded()
435+ } else {
436+ stepperState = .ShouldDecrease
437+ animateSlideLeft()
438+ }
439+
440+ }
441+
442+ @objc func rightButtonTouchDown(button: UIButton) {
443+ leftButton.isEnabled = false
444+ label.isUserInteractionEnabled = false
445+ resetTimer()
446+
447+ if value == maximumValue {
448+ animateLimitHitIfNeeded()
449+ } else {
450+ stepperState = .ShouldIncrease
451+ animateSlideRight()
452+ }
453+ }
454+
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) {
465+ self.label.center.x -= self.labelSlideLength
466+ }
467+ }
468+
469+ func animateSlideRight() {
470+ UIView.animate(withDuration: labelSlideDuration) {
471+ self.label.center.x += self.labelSlideLength
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+ }
489+ }
490+
491+ func animateLimitHitForButton(button: UIButton){
492+ UIView.animate(withDuration: limitHitAnimationDuration) {
493+ button.backgroundColor = self.limitHitAnimationColor
494+ }
495+ }
496+}
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+
304
520
305}
521}
GMStepperExample/GMStepperExample.xcodeproj/project.pbxproj+136−109
@@ -7,18 +7,18 @@
7 objects = {
7 objects = {
8
8
9/* Begin PBXBuildFile section */
9/* Begin PBXBuildFile section */
10+ 3116C0DB1DA4DD360015AC69 /* GMStepperExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3116C0DA1DA4DD360015AC69 /* GMStepperExampleTests.swift */; };
10− 3169DD3E1B496C33009791D1 /* GMStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C195B1B48445E005EFEE5 /* GMStepper.swift */; };
11+ 3198AC771DA4DEE500311FB3 /* GMStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C195B1B48445E005EFEE5 /* GMStepper.swift */; };
11 319C19381B4843EB005EFEE5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C19371B4843EB005EFEE5 /* AppDelegate.swift */; };
12 319C19381B4843EB005EFEE5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C19371B4843EB005EFEE5 /* AppDelegate.swift */; };
12 319C193A1B4843EB005EFEE5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C19391B4843EB005EFEE5 /* ViewController.swift */; };
13 319C193A1B4843EB005EFEE5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C19391B4843EB005EFEE5 /* ViewController.swift */; };
13 319C193D1B4843EB005EFEE5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 319C193B1B4843EB005EFEE5 /* Main.storyboard */; };
14 319C193D1B4843EB005EFEE5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 319C193B1B4843EB005EFEE5 /* Main.storyboard */; };
14 319C193F1B4843EB005EFEE5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 319C193E1B4843EB005EFEE5 /* Images.xcassets */; };
15 319C193F1B4843EB005EFEE5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 319C193E1B4843EB005EFEE5 /* Images.xcassets */; };
15 319C19421B4843EB005EFEE5 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 319C19401B4843EB005EFEE5 /* LaunchScreen.xib */; };
16 319C19421B4843EB005EFEE5 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 319C19401B4843EB005EFEE5 /* LaunchScreen.xib */; };
16− 319C194E1B4843EB005EFEE5 /* GMStepperExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C194D1B4843EB005EFEE5 /* GMStepperExampleTests.swift */; };
17 319C195C1B48445E005EFEE5 /* GMStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C195B1B48445E005EFEE5 /* GMStepper.swift */; };
17 319C195C1B48445E005EFEE5 /* GMStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319C195B1B48445E005EFEE5 /* GMStepper.swift */; };
18/* End PBXBuildFile section */
18/* End PBXBuildFile section */
19
19
20/* Begin PBXContainerItemProxy section */
20/* Begin PBXContainerItemProxy section */
21− 319C19481B4843EB005EFEE5 /* PBXContainerItemProxy */ = {
21+ 3116C0DD1DA4DD360015AC69 /* PBXContainerItemProxy */ = {
22 isa = PBXContainerItemProxy;
22 isa = PBXContainerItemProxy;
23 containerPortal = 319C192A1B4843EB005EFEE5 /* Project object */;
23 containerPortal = 319C192A1B4843EB005EFEE5 /* Project object */;
24 proxyType = 1;
24 proxyType = 1;
@@ -28,6 +28,9 @@
28/* End PBXContainerItemProxy section */
28/* End PBXContainerItemProxy section */
29
29
30/* Begin PBXFileReference section */
30/* 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>"; };
31 319C19321B4843EB005EFEE5 /* GMStepperExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GMStepperExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
34 319C19321B4843EB005EFEE5 /* GMStepperExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GMStepperExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
32 319C19361B4843EB005EFEE5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
35 319C19361B4843EB005EFEE5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
33 319C19371B4843EB005EFEE5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
36 319C19371B4843EB005EFEE5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -35,21 +38,18 @@
35 319C193C1B4843EB005EFEE5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
38 319C193C1B4843EB005EFEE5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
36 319C193E1B4843EB005EFEE5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
39 319C193E1B4843EB005EFEE5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
37 319C19411B4843EB005EFEE5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
40 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>"; };
41 319C195B1B48445E005EFEE5 /* GMStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMStepper.swift; sourceTree = "<group>"; };
41 319C195B1B48445E005EFEE5 /* GMStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMStepper.swift; sourceTree = "<group>"; };
42/* End PBXFileReference section */
42/* End PBXFileReference section */
43
43
44/* Begin PBXFrameworksBuildPhase section */
44/* Begin PBXFrameworksBuildPhase section */
45− 319C192F1B4843EB005EFEE5 /* Frameworks */ = {
45+ 3116C0D51DA4DD360015AC69 /* Frameworks */ = {
46 isa = PBXFrameworksBuildPhase;
46 isa = PBXFrameworksBuildPhase;
47 buildActionMask = 2147483647;
47 buildActionMask = 2147483647;
48 files = (
48 files = (
49 );
49 );
50 runOnlyForDeploymentPostprocessing = 0;
50 runOnlyForDeploymentPostprocessing = 0;
51 };
51 };
52− 319C19441B4843EB005EFEE5 /* Frameworks */ = {
52+ 319C192F1B4843EB005EFEE5 /* Frameworks */ = {
53 isa = PBXFrameworksBuildPhase;
53 isa = PBXFrameworksBuildPhase;
54 buildActionMask = 2147483647;
54 buildActionMask = 2147483647;
55 files = (
55 files = (
@@ -59,12 +59,21 @@
59/* End PBXFrameworksBuildPhase section */
59/* End PBXFrameworksBuildPhase section */
60
60
61/* Begin PBXGroup section */
61/* 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+ };
62 319C19291B4843EB005EFEE5 = {
71 319C19291B4843EB005EFEE5 = {
63 isa = PBXGroup;
72 isa = PBXGroup;
64 children = (
73 children = (
65 319C195A1B48445E005EFEE5 /* GMStepper */,
74 319C195A1B48445E005EFEE5 /* GMStepper */,
66 319C19341B4843EB005EFEE5 /* GMStepperExample */,
75 319C19341B4843EB005EFEE5 /* GMStepperExample */,
67− 319C194A1B4843EB005EFEE5 /* GMStepperExampleTests */,
76+ 3116C0D91DA4DD360015AC69 /* GMStepperExampleTests */,
68 319C19331B4843EB005EFEE5 /* Products */,
77 319C19331B4843EB005EFEE5 /* Products */,
69 );
78 );
70 sourceTree = "<group>";
79 sourceTree = "<group>";
@@ -73,7 +82,7 @@
73 isa = PBXGroup;
82 isa = PBXGroup;
74 children = (
83 children = (
75 319C19321B4843EB005EFEE5 /* GMStepperExample.app */,
84 319C19321B4843EB005EFEE5 /* GMStepperExample.app */,
76− 319C19471B4843EB005EFEE5 /* GMStepperExampleTests.xctest */,
85+ 3116C0D81DA4DD360015AC69 /* GMStepperExampleTests.xctest */,
77 );
86 );
78 name = Products;
87 name = Products;
79 sourceTree = "<group>";
88 sourceTree = "<group>";
@@ -99,23 +108,6 @@
99 name = "Supporting Files";
108 name = "Supporting Files";
100 sourceTree = "<group>";
109 sourceTree = "<group>";
101 };
110 };
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− };
119 319C195A1B48445E005EFEE5 /* GMStepper */ = {
111 319C195A1B48445E005EFEE5 /* GMStepper */ = {
120 isa = PBXGroup;
112 isa = PBXGroup;
121 children = (
113 children = (
@@ -128,6 +120,24 @@
128/* End PBXGroup section */
120/* End PBXGroup section */
129
121
130/* Begin PBXNativeTarget section */
122/* 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+ };
131 319C19311B4843EB005EFEE5 /* GMStepperExample */ = {
141 319C19311B4843EB005EFEE5 /* GMStepperExample */ = {
132 isa = PBXNativeTarget;
142 isa = PBXNativeTarget;
133 buildConfigurationList = 319C19511B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExample" */;
143 buildConfigurationList = 319C19511B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExample" */;
@@ -145,40 +155,27 @@
145 productReference = 319C19321B4843EB005EFEE5 /* GMStepperExample.app */;
155 productReference = 319C19321B4843EB005EFEE5 /* GMStepperExample.app */;
146 productType = "com.apple.product-type.application";
156 productType = "com.apple.product-type.application";
147 };
157 };
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− };
166/* End PBXNativeTarget section */
158/* End PBXNativeTarget section */
167
159
168/* Begin PBXProject section */
160/* Begin PBXProject section */
169 319C192A1B4843EB005EFEE5 /* Project object */ = {
161 319C192A1B4843EB005EFEE5 /* Project object */ = {
170 isa = PBXProject;
162 isa = PBXProject;
171 attributes = {
163 attributes = {
164+ LastSwiftMigration = 0700;
172− LastSwiftUpdateCheck = 0700;
165+ LastSwiftUpdateCheck = 0800;
173− LastUpgradeCheck = 0640;
166+ LastUpgradeCheck = 0900;
174 ORGANIZATIONNAME = "Gunay Mert Karadogan";
167 ORGANIZATIONNAME = "Gunay Mert Karadogan";
175 TargetAttributes = {
168 TargetAttributes = {
176− 319C19311B4843EB005EFEE5 = {
169+ 3116C0D71DA4DD360015AC69 = {
177− CreatedOnToolsVersion = 6.4;
170+ CreatedOnToolsVersion = 8.0;
171+ LastSwiftMigration = 0900;
172+ ProvisioningStyle = Automatic;
173+ TestTargetID = 319C19311B4843EB005EFEE5;
178 };
174 };
179− 319C19461B4843EB005EFEE5 = {
175+ 319C19311B4843EB005EFEE5 = {
180 CreatedOnToolsVersion = 6.4;
176 CreatedOnToolsVersion = 6.4;
181− TestTargetID = 319C19311B4843EB005EFEE5;
177+ DevelopmentTeam = 289M6XEDV4;
178+ LastSwiftMigration = 0900;
182 };
179 };
183 };
180 };
184 };
181 };
@@ -196,58 +193,58 @@
196 projectRoot = "";
193 projectRoot = "";
197 targets = (
194 targets = (
198 319C19311B4843EB005EFEE5 /* GMStepperExample */,
195 319C19311B4843EB005EFEE5 /* GMStepperExample */,
199− 319C19461B4843EB005EFEE5 /* GMStepperExampleTests */,
196+ 3116C0D71DA4DD360015AC69 /* GMStepperExampleTests */,
200 );
197 );
201 };
198 };
202/* End PBXProject section */
199/* End PBXProject section */
203
200
204/* Begin PBXResourcesBuildPhase section */
201/* Begin PBXResourcesBuildPhase section */
205− 319C19301B4843EB005EFEE5 /* Resources */ = {
202+ 3116C0D61DA4DD360015AC69 /* Resources */ = {
206 isa = PBXResourcesBuildPhase;
203 isa = PBXResourcesBuildPhase;
207 buildActionMask = 2147483647;
204 buildActionMask = 2147483647;
208 files = (
205 files = (
209− 319C193D1B4843EB005EFEE5 /* Main.storyboard in Resources */,
210− 319C19421B4843EB005EFEE5 /* LaunchScreen.xib in Resources */,
211− 319C193F1B4843EB005EFEE5 /* Images.xcassets in Resources */,
212 );
206 );
213 runOnlyForDeploymentPostprocessing = 0;
207 runOnlyForDeploymentPostprocessing = 0;
214 };
208 };
215− 319C19451B4843EB005EFEE5 /* Resources */ = {
209+ 319C19301B4843EB005EFEE5 /* Resources */ = {
216 isa = PBXResourcesBuildPhase;
210 isa = PBXResourcesBuildPhase;
217 buildActionMask = 2147483647;
211 buildActionMask = 2147483647;
218 files = (
212 files = (
213+ 319C193D1B4843EB005EFEE5 /* Main.storyboard in Resources */,
214+ 319C19421B4843EB005EFEE5 /* LaunchScreen.xib in Resources */,
215+ 319C193F1B4843EB005EFEE5 /* Images.xcassets in Resources */,
219 );
216 );
220 runOnlyForDeploymentPostprocessing = 0;
217 runOnlyForDeploymentPostprocessing = 0;
221 };
218 };
222/* End PBXResourcesBuildPhase section */
219/* End PBXResourcesBuildPhase section */
223
220
224/* Begin PBXSourcesBuildPhase section */
221/* Begin PBXSourcesBuildPhase section */
225− 319C192E1B4843EB005EFEE5 /* Sources */ = {
222+ 3116C0D41DA4DD360015AC69 /* Sources */ = {
226 isa = PBXSourcesBuildPhase;
223 isa = PBXSourcesBuildPhase;
227 buildActionMask = 2147483647;
224 buildActionMask = 2147483647;
228 files = (
225 files = (
229− 319C193A1B4843EB005EFEE5 /* ViewController.swift in Sources */,
226+ 3116C0DB1DA4DD360015AC69 /* GMStepperExampleTests.swift in Sources */,
230− 319C195C1B48445E005EFEE5 /* GMStepper.swift in Sources */,
227+ 3198AC771DA4DEE500311FB3 /* GMStepper.swift in Sources */,
231− 319C19381B4843EB005EFEE5 /* AppDelegate.swift in Sources */,
232 );
228 );
233 runOnlyForDeploymentPostprocessing = 0;
229 runOnlyForDeploymentPostprocessing = 0;
234 };
230 };
235− 319C19431B4843EB005EFEE5 /* Sources */ = {
231+ 319C192E1B4843EB005EFEE5 /* Sources */ = {
236 isa = PBXSourcesBuildPhase;
232 isa = PBXSourcesBuildPhase;
237 buildActionMask = 2147483647;
233 buildActionMask = 2147483647;
238 files = (
234 files = (
239− 319C194E1B4843EB005EFEE5 /* GMStepperExampleTests.swift in Sources */,
235+ 319C193A1B4843EB005EFEE5 /* ViewController.swift in Sources */,
240− 3169DD3E1B496C33009791D1 /* GMStepper.swift in Sources */,
236+ 319C195C1B48445E005EFEE5 /* GMStepper.swift in Sources */,
237+ 319C19381B4843EB005EFEE5 /* AppDelegate.swift in Sources */,
241 );
238 );
242 runOnlyForDeploymentPostprocessing = 0;
239 runOnlyForDeploymentPostprocessing = 0;
243 };
240 };
244/* End PBXSourcesBuildPhase section */
241/* End PBXSourcesBuildPhase section */
245
242
246/* Begin PBXTargetDependency section */
243/* Begin PBXTargetDependency section */
247− 319C19491B4843EB005EFEE5 /* PBXTargetDependency */ = {
244+ 3116C0DE1DA4DD360015AC69 /* PBXTargetDependency */ = {
248 isa = PBXTargetDependency;
245 isa = PBXTargetDependency;
249 target = 319C19311B4843EB005EFEE5 /* GMStepperExample */;
246 target = 319C19311B4843EB005EFEE5 /* GMStepperExample */;
250− targetProxy = 319C19481B4843EB005EFEE5 /* PBXContainerItemProxy */;
247+ targetProxy = 3116C0DD1DA4DD360015AC69 /* PBXContainerItemProxy */;
251 };
248 };
252/* End PBXTargetDependency section */
249/* End PBXTargetDependency section */
253
250
@@ -271,6 +268,44 @@
271/* End PBXVariantGroup section */
268/* End PBXVariantGroup section */
272
269
273/* Begin XCBuildConfiguration section */
270/* 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+ };
274 319C194F1B4843EB005EFEE5 /* Debug */ = {
309 319C194F1B4843EB005EFEE5 /* Debug */ = {
275 isa = XCBuildConfiguration;
310 isa = XCBuildConfiguration;
276 buildSettings = {
311 buildSettings = {
@@ -279,19 +314,28 @@
279 CLANG_CXX_LIBRARY = "libc++";
314 CLANG_CXX_LIBRARY = "libc++";
280 CLANG_ENABLE_MODULES = YES;
315 CLANG_ENABLE_MODULES = YES;
281 CLANG_ENABLE_OBJC_ARC = YES;
316 CLANG_ENABLE_OBJC_ARC = YES;
317+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
282 CLANG_WARN_BOOL_CONVERSION = YES;
318 CLANG_WARN_BOOL_CONVERSION = YES;
319+ CLANG_WARN_COMMA = YES;
283 CLANG_WARN_CONSTANT_CONVERSION = YES;
320 CLANG_WARN_CONSTANT_CONVERSION = YES;
284 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
321 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
285 CLANG_WARN_EMPTY_BODY = YES;
322 CLANG_WARN_EMPTY_BODY = YES;
286 CLANG_WARN_ENUM_CONVERSION = YES;
323 CLANG_WARN_ENUM_CONVERSION = YES;
324+ CLANG_WARN_INFINITE_RECURSION = YES;
287 CLANG_WARN_INT_CONVERSION = YES;
325 CLANG_WARN_INT_CONVERSION = YES;
326+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
327+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
288 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
328 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;
289 CLANG_WARN_UNREACHABLE_CODE = YES;
332 CLANG_WARN_UNREACHABLE_CODE = YES;
290 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
333 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
291 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
334 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
292 COPY_PHASE_STRIP = NO;
335 COPY_PHASE_STRIP = NO;
293 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
336 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
294 ENABLE_STRICT_OBJC_MSGSEND = YES;
337 ENABLE_STRICT_OBJC_MSGSEND = YES;
338+ ENABLE_TESTABILITY = YES;
295 GCC_C_LANGUAGE_STANDARD = gnu99;
339 GCC_C_LANGUAGE_STANDARD = gnu99;
296 GCC_DYNAMIC_NO_PIC = NO;
340 GCC_DYNAMIC_NO_PIC = NO;
297 GCC_NO_COMMON_BLOCKS = YES;
341 GCC_NO_COMMON_BLOCKS = YES;
@@ -323,13 +367,21 @@
323 CLANG_CXX_LIBRARY = "libc++";
367 CLANG_CXX_LIBRARY = "libc++";
324 CLANG_ENABLE_MODULES = YES;
368 CLANG_ENABLE_MODULES = YES;
325 CLANG_ENABLE_OBJC_ARC = YES;
369 CLANG_ENABLE_OBJC_ARC = YES;
370+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
326 CLANG_WARN_BOOL_CONVERSION = YES;
371 CLANG_WARN_BOOL_CONVERSION = YES;
372+ CLANG_WARN_COMMA = YES;
327 CLANG_WARN_CONSTANT_CONVERSION = YES;
373 CLANG_WARN_CONSTANT_CONVERSION = YES;
328 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
374 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
329 CLANG_WARN_EMPTY_BODY = YES;
375 CLANG_WARN_EMPTY_BODY = YES;
330 CLANG_WARN_ENUM_CONVERSION = YES;
376 CLANG_WARN_ENUM_CONVERSION = YES;
377+ CLANG_WARN_INFINITE_RECURSION = YES;
331 CLANG_WARN_INT_CONVERSION = YES;
378 CLANG_WARN_INT_CONVERSION = YES;
379+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
380+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
332 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
381 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;
333 CLANG_WARN_UNREACHABLE_CODE = YES;
385 CLANG_WARN_UNREACHABLE_CODE = YES;
334 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
386 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
335 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
387 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@@ -348,6 +400,7 @@
348 IPHONEOS_DEPLOYMENT_TARGET = 8.4;
400 IPHONEOS_DEPLOYMENT_TARGET = 8.4;
349 MTL_ENABLE_DEBUG_INFO = NO;
401 MTL_ENABLE_DEBUG_INFO = NO;
350 SDKROOT = iphoneos;
402 SDKROOT = iphoneos;
403+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
351 VALIDATE_PRODUCT = YES;
404 VALIDATE_PRODUCT = YES;
352 };
405 };
353 name = Release;
406 name = Release;
@@ -356,9 +409,13 @@
356 isa = XCBuildConfiguration;
409 isa = XCBuildConfiguration;
357 buildSettings = {
410 buildSettings = {
358 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
411 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
412+ DEVELOPMENT_TEAM = 289M6XEDV4;
359 INFOPLIST_FILE = GMStepperExample/Info.plist;
413 INFOPLIST_FILE = GMStepperExample/Info.plist;
360 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
414 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
415+ PRODUCT_BUNDLE_IDENTIFIER = "com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)";
361 PRODUCT_NAME = "$(TARGET_NAME)";
416 PRODUCT_NAME = "$(TARGET_NAME)";
417+ SWIFT_SWIFT3_OBJC_INFERENCE = Default;
418+ SWIFT_VERSION = 4.0;
362 };
419 };
363 name = Debug;
420 name = Debug;
364 };
421 };
@@ -366,72 +423,42 @@
366 isa = XCBuildConfiguration;
423 isa = XCBuildConfiguration;
367 buildSettings = {
424 buildSettings = {
368 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
425 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
426+ DEVELOPMENT_TEAM = 289M6XEDV4;
369 INFOPLIST_FILE = GMStepperExample/Info.plist;
427 INFOPLIST_FILE = GMStepperExample/Info.plist;
370 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
428 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
429+ PRODUCT_BUNDLE_IDENTIFIER = "com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)";
371 PRODUCT_NAME = "$(TARGET_NAME)";
430 PRODUCT_NAME = "$(TARGET_NAME)";
372− };
431+ SWIFT_SWIFT3_OBJC_INFERENCE = Default;
373− name = Release;
432+ SWIFT_VERSION = 4.0;
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";
406 };
433 };
407 name = Release;
434 name = Release;
408 };
435 };
409/* End XCBuildConfiguration section */
436/* End XCBuildConfiguration section */
410
437
411/* Begin XCConfigurationList section */
438/* Begin XCConfigurationList section */
412− 319C192D1B4843EB005EFEE5 /* Build configuration list for PBXProject "GMStepperExample" */ = {
439+ 3116C0DF1DA4DD360015AC69 /* Build configuration list for PBXNativeTarget "GMStepperExampleTests" */ = {
413 isa = XCConfigurationList;
440 isa = XCConfigurationList;
414 buildConfigurations = (
441 buildConfigurations = (
415− 319C194F1B4843EB005EFEE5 /* Debug */,
442+ 3116C0E01DA4DD360015AC69 /* Debug */,
416− 319C19501B4843EB005EFEE5 /* Release */,
443+ 3116C0E11DA4DD360015AC69 /* Release */,
417 );
444 );
418 defaultConfigurationIsVisible = 0;
445 defaultConfigurationIsVisible = 0;
419 defaultConfigurationName = Release;
446 defaultConfigurationName = Release;
420 };
447 };
421− 319C19511B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExample" */ = {
448+ 319C192D1B4843EB005EFEE5 /* Build configuration list for PBXProject "GMStepperExample" */ = {
422 isa = XCConfigurationList;
449 isa = XCConfigurationList;
423 buildConfigurations = (
450 buildConfigurations = (
424− 319C19521B4843EB005EFEE5 /* Debug */,
451+ 319C194F1B4843EB005EFEE5 /* Debug */,
425− 319C19531B4843EB005EFEE5 /* Release */,
452+ 319C19501B4843EB005EFEE5 /* Release */,
426 );
453 );
427 defaultConfigurationIsVisible = 0;
454 defaultConfigurationIsVisible = 0;
428 defaultConfigurationName = Release;
455 defaultConfigurationName = Release;
429 };
456 };
430− 319C19541B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExampleTests" */ = {
457+ 319C19511B4843EB005EFEE5 /* Build configuration list for PBXNativeTarget "GMStepperExample" */ = {
431 isa = XCConfigurationList;
458 isa = XCConfigurationList;
432 buildConfigurations = (
459 buildConfigurations = (
433− 319C19551B4843EB005EFEE5 /* Debug */,
460+ 319C19521B4843EB005EFEE5 /* Debug */,
434− 319C19561B4843EB005EFEE5 /* Release */,
461+ 319C19531B4843EB005EFEE5 /* Release */,
435 );
462 );
436 defaultConfigurationIsVisible = 0;
463 defaultConfigurationIsVisible = 0;
437 defaultConfigurationName = Release;
464 defaultConfigurationName = Release;
GMStepperExample/GMStepperExample/AppDelegate.swift+10−10
@@ -14,33 +14,33 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
14 var window: UIWindow?
14 var window: UIWindow?
15
15
16
16
17− func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
17+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 // Override point for customization after application launch.
18 // Override point for customization after application launch.
19 return true
19 return true
20 }
20 }
21
21
22− func applicationWillResignActive(application: UIApplication) {
22+ func applicationWillResignActive(_ application: UIApplication) {
23 // 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.
23 // 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.
25 }
25 }
26
26
27− func applicationDidEnterBackground(application: UIApplication) {
27+ func applicationDidEnterBackground(_ application: UIApplication) {
28 // 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.
28 // 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.
29 // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
29 // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 }
30 }
31
31
32− func applicationWillEnterForeground(application: UIApplication) {
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.
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.
34 }
34 }
35
35
36− func applicationDidBecomeActive(application: UIApplication) {
36+ func applicationDidBecomeActive(_ application: UIApplication) {
37 // 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.
37 // 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.
38 }
38 }
39
39
40− func applicationWillTerminate(application: UIApplication) {
40+ func applicationWillTerminate(_ application: UIApplication) {
41 // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
41 // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 }
42 }
43−
43+
44−
44+
45}
45}
46
46
GMStepperExample/GMStepperExample/Base.lproj/LaunchScreen.xib+6−2
@@ -1,7 +1,8 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1<?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">
3 <dependencies>
3 <dependencies>
4+ <deployment identifier="iOS"/>
4− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
5+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
5 <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
6 <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
6 </dependencies>
7 </dependencies>
7 <objects>
8 <objects>
@@ -13,17 +14,20 @@
13 <subviews>
14 <subviews>
14 <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">
15 <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">
15 <rect key="frame" x="20" y="439" width="441" height="21"/>
16 <rect key="frame" x="20" y="439" width="441" height="21"/>
17+ <animations/>
16 <fontDescription key="fontDescription" type="system" pointSize="17"/>
18 <fontDescription key="fontDescription" type="system" pointSize="17"/>
17 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
19 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
18 <nil key="highlightedColor"/>
20 <nil key="highlightedColor"/>
19 </label>
21 </label>
20 <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">
22 <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">
21 <rect key="frame" x="20" y="140" width="441" height="43"/>
23 <rect key="frame" x="20" y="140" width="441" height="43"/>
24+ <animations/>
22 <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
25 <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
23 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
26 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
24 <nil key="highlightedColor"/>
27 <nil key="highlightedColor"/>
25 </label>
28 </label>
26 </subviews>
29 </subviews>
30+ <animations/>
27 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
31 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
28 <constraints>
32 <constraints>
29 <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
33 <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 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1<?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">
3 <dependencies>
3 <dependencies>
4+ <deployment identifier="iOS"/>
4− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
5+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
5 <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
6 <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
7+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
6 </dependencies>
8 </dependencies>
7 <scenes>
9 <scenes>
8 <!--View Controller-->
10 <!--View Controller-->
@@ -14,102 +16,92 @@
14 <viewControllerLayoutGuide type="bottom" id="2fi-mo-0CV"/>
16 <viewControllerLayoutGuide type="bottom" id="2fi-mo-0CV"/>
15 </layoutGuides>
17 </layoutGuides>
16 <view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
18 <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"/>
18 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
20 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
19 <subviews>
21 <subviews>
20 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AU6-4C-B2F" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
22 <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"/>
23 <constraints>
24 <constraints>
24 <constraint firstAttribute="height" constant="44" id="6mC-hP-wFD"/>
25 <constraint firstAttribute="height" constant="44" id="6mC-hP-wFD"/>
25 </constraints>
26 </constraints>
26 <userDefinedRuntimeAttributes>
27 <userDefinedRuntimeAttributes>
27− <userDefinedRuntimeAttribute type="number" keyPath="minimumValue">
28+ <userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
28− <integer key="value" value="1"/>
29+ <real key="value" value="25"/>
30+ </userDefinedRuntimeAttribute>
31+ <userDefinedRuntimeAttribute type="number" keyPath="stepValue">
32+ <real key="value" value="5"/>
29 </userDefinedRuntimeAttribute>
33 </userDefinedRuntimeAttribute>
30 </userDefinedRuntimeAttributes>
34 </userDefinedRuntimeAttributes>
31 </view>
35 </view>
32 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="if7-U5-bAM" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
36 <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"/>
35 <constraints>
38 <constraints>
36 <constraint firstAttribute="height" constant="66" id="iQi-6P-vt9"/>
39 <constraint firstAttribute="height" constant="66" id="iQi-6P-vt9"/>
37 <constraint firstAttribute="width" constant="320" id="ji4-Pf-1fZ"/>
40 <constraint firstAttribute="width" constant="320" id="ji4-Pf-1fZ"/>
38 </constraints>
41 </constraints>
39 <userDefinedRuntimeAttributes>
42 <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>
46 <userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor">
43 <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"/>
48 </userDefinedRuntimeAttribute>
45 </userDefinedRuntimeAttribute>
49 <userDefinedRuntimeAttribute type="color" keyPath="labelTextColor">
46 <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"/>
51 </userDefinedRuntimeAttribute>
48 </userDefinedRuntimeAttribute>
52 <userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor">
49 <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"/>
54 </userDefinedRuntimeAttribute>
51 </userDefinedRuntimeAttribute>
55 <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
52 <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"/>
57 </userDefinedRuntimeAttribute>
54 </userDefinedRuntimeAttribute>
58 <userDefinedRuntimeAttribute type="color" keyPath="limitHitAnimationColor">
55 <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"/>
60 </userDefinedRuntimeAttribute>
60 </userDefinedRuntimeAttribute>
61 </userDefinedRuntimeAttributes>
61 </userDefinedRuntimeAttributes>
62 </view>
62 </view>
63 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="R9n-I0-fq7" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
63 <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"/>
66 <constraints>
65 <constraints>
67 <constraint firstAttribute="width" constant="200" id="dVu-w0-mf0"/>
66 <constraint firstAttribute="width" constant="200" id="dVu-w0-mf0"/>
68 <constraint firstAttribute="height" constant="66" id="ie8-vl-NAV"/>
67 <constraint firstAttribute="height" constant="66" id="ie8-vl-NAV"/>
69 </constraints>
68 </constraints>
70 <userDefinedRuntimeAttributes>
69 <userDefinedRuntimeAttributes>
71 <userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor">
70 <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"/>
73 </userDefinedRuntimeAttribute>
72 </userDefinedRuntimeAttribute>
74 <userDefinedRuntimeAttribute type="color" keyPath="labelTextColor">
73 <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"/>
76 </userDefinedRuntimeAttribute>
75 </userDefinedRuntimeAttribute>
77 <userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor">
76 <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"/>
79 </userDefinedRuntimeAttribute>
78 </userDefinedRuntimeAttribute>
80 <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
79 <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
81− <color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
80+ <color key="value" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
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"/>
88 </userDefinedRuntimeAttribute>
81 </userDefinedRuntimeAttribute>
89 <userDefinedRuntimeAttribute type="color" keyPath="limitHitAnimationColor">
82 <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"/>
91 </userDefinedRuntimeAttribute>
84 </userDefinedRuntimeAttribute>
92 </userDefinedRuntimeAttributes>
85 </userDefinedRuntimeAttributes>
93 </view>
86 </view>
94 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="U8X-8Z-7Mo" customClass="GMStepper" customModule="GMStepperExample" customModuleProvider="target">
87 <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"/>
97 <constraints>
89 <constraints>
98 <constraint firstAttribute="height" constant="44" id="aF3-T7-dIP"/>
90 <constraint firstAttribute="height" constant="44" id="aF3-T7-dIP"/>
99 <constraint firstAttribute="width" constant="200" id="vhP-7K-vOu"/>
91 <constraint firstAttribute="width" constant="200" id="vhP-7K-vOu"/>
100 </constraints>
92 </constraints>
101 <userDefinedRuntimeAttributes>
93 <userDefinedRuntimeAttributes>
102 <userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor">
94 <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"/>
104 </userDefinedRuntimeAttribute>
96 </userDefinedRuntimeAttribute>
105 <userDefinedRuntimeAttribute type="color" keyPath="labelTextColor">
97 <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"/>
107 </userDefinedRuntimeAttribute>
99 </userDefinedRuntimeAttribute>
108 <userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor">
100 <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"/>
110 </userDefinedRuntimeAttribute>
102 </userDefinedRuntimeAttribute>
111 <userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor">
103 <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"/>
113 </userDefinedRuntimeAttribute>
105 </userDefinedRuntimeAttribute>
114 <userDefinedRuntimeAttribute type="number" keyPath="labelWidthWeight">
106 <userDefinedRuntimeAttribute type="number" keyPath="labelWidthWeight">
115 <real key="value" value="0.69999999999999996"/>
107 <real key="value" value="0.69999999999999996"/>
@@ -117,10 +109,13 @@
117 <userDefinedRuntimeAttribute type="string" keyPath="leftButtonText" value="😞"/>
109 <userDefinedRuntimeAttribute type="string" keyPath="leftButtonText" value="😞"/>
118 <userDefinedRuntimeAttribute type="string" keyPath="rightButtonText" value="☺️"/>
110 <userDefinedRuntimeAttribute type="string" keyPath="rightButtonText" value="☺️"/>
119 <userDefinedRuntimeAttribute type="number" keyPath="minimumValue">
111 <userDefinedRuntimeAttribute type="number" keyPath="minimumValue">
120− <integer key="value" value="5"/>
112+ <integer key="value" value="0"/>
121 </userDefinedRuntimeAttribute>
113 </userDefinedRuntimeAttribute>
122 <userDefinedRuntimeAttribute type="color" keyPath="limitHitAnimationColor">
114 <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"/>
124 </userDefinedRuntimeAttribute>
119 </userDefinedRuntimeAttribute>
125 </userDefinedRuntimeAttributes>
120 </userDefinedRuntimeAttributes>
126 <variation key="default">
121 <variation key="default">
@@ -129,8 +124,44 @@
129 </mask>
124 </mask>
130 </variation>
125 </variation>
131 </view>
126 </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>
132 </subviews>
163 </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"/>
134 <constraints>
165 <constraints>
135 <constraint firstItem="R9n-I0-fq7" firstAttribute="centerX" secondItem="U8X-8Z-7Mo" secondAttribute="centerX" id="3cD-WW-Qg4"/>
166 <constraint firstItem="R9n-I0-fq7" firstAttribute="centerX" secondItem="U8X-8Z-7Mo" secondAttribute="centerX" id="3cD-WW-Qg4"/>
136 <constraint firstItem="if7-U5-bAM" firstAttribute="top" secondItem="AU6-4C-B2F" secondAttribute="bottom" constant="44" id="662-W9-CUn"/>
167 <constraint firstItem="if7-U5-bAM" firstAttribute="top" secondItem="AU6-4C-B2F" secondAttribute="bottom" constant="44" id="662-W9-CUn"/>
@@ -140,7 +171,9 @@
140 <constraint firstItem="if7-U5-bAM" firstAttribute="centerX" secondItem="R9n-I0-fq7" secondAttribute="centerX" id="K7g-rC-ehJ"/>
171 <constraint firstItem="if7-U5-bAM" firstAttribute="centerX" secondItem="R9n-I0-fq7" secondAttribute="centerX" id="K7g-rC-ehJ"/>
141 <constraint firstItem="U8X-8Z-7Mo" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" constant="8" id="S1b-pD-UXA"/>
172 <constraint firstItem="U8X-8Z-7Mo" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" constant="8" id="S1b-pD-UXA"/>
142 <constraint firstItem="U8X-8Z-7Mo" firstAttribute="top" secondItem="R9n-I0-fq7" secondAttribute="bottom" constant="44" id="fBW-Xx-pa0"/>
173 <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"/>
143 <constraint firstItem="AU6-4C-B2F" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" constant="8" id="iIM-b2-jzV"/>
175 <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"/>
144 <constraint firstAttribute="trailingMargin" secondItem="U8X-8Z-7Mo" secondAttribute="trailing" constant="8" id="ppd-nb-fL8"/>
177 <constraint firstAttribute="trailingMargin" secondItem="U8X-8Z-7Mo" secondAttribute="trailing" constant="8" id="ppd-nb-fL8"/>
145 <constraint firstItem="AU6-4C-B2F" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" constant="8" symbolic="YES" id="zDK-gF-kms"/>
178 <constraint firstItem="AU6-4C-B2F" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" constant="8" symbolic="YES" id="zDK-gF-kms"/>
146 </constraints>
179 </constraints>
@@ -151,6 +184,7 @@
151 </viewController>
184 </viewController>
152 <placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
185 <placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
153 </objects>
186 </objects>
187+ <point key="canvasLocation" x="274" y="363"/>
154 </scene>
188 </scene>
155 </scenes>
189 </scenes>
156</document>
190</document>
GMStepperExample/GMStepperExample/Info.plist+1−1
@@ -7,7 +7,7 @@
7 <key>CFBundleExecutable</key>
7 <key>CFBundleExecutable</key>
8 <string>$(EXECUTABLE_NAME)</string>
8 <string>$(EXECUTABLE_NAME)</string>
9 <key>CFBundleIdentifier</key>
9 <key>CFBundleIdentifier</key>
10− <string>com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)</string>
10+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11 <key>CFBundleInfoDictionaryVersion</key>
11 <key>CFBundleInfoDictionaryVersion</key>
12 <string>6.0</string>
12 <string>6.0</string>
13 <key>CFBundleName</key>
13 <key>CFBundleName</key>
GMStepperExample/GMStepperExample/ViewController.swift+3−3
@@ -13,10 +13,10 @@ class ViewController: UIViewController {
13 @IBOutlet weak var stepper: GMStepper!
13 @IBOutlet weak var stepper: GMStepper!
14 override func viewDidLoad() {
14 override func viewDidLoad() {
15 super.viewDidLoad()
15 super.viewDidLoad()
16− stepper.addTarget(self, action: "stepperValueChanged:", forControlEvents: .ValueChanged)
16+ stepper.addTarget(self, action: #selector(ViewController.stepperValueChanged), for: .valueChanged)
17 }
17 }
18
18
19− func stepperValueChanged(stepper: GMStepper) {
19+ @objc func stepperValueChanged(stepper: GMStepper) {
20− println(stepper.value)
20+ print(stepper.value, terminator: "")
21 }
21 }
22}
22}
GMStepperExample/GMStepperExampleTests/GMStepperExampleTests.swift+4−4
@@ -6,8 +6,8 @@
6// Copyright (c) 2015 Gunay Mert Karadogan. All rights reserved.
6// Copyright (c) 2015 Gunay Mert Karadogan. All rights reserved.
7//
7//
8
8
9−import UIKit
10import XCTest
9import XCTest
10+import UIKit
11
11
12class GMStepperExampleTests: XCTestCase {
12class GMStepperExampleTests: XCTestCase {
13 var stepper: GMStepper!
13 var stepper: GMStepper!
@@ -19,7 +19,7 @@ class GMStepperExampleTests: XCTestCase {
19
19
20 func testDefaults() {
20 func testDefaults() {
21 XCTAssert(stepper.minimumValue == 0, "minimumValue defaults 0")
21 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")
23 XCTAssert(stepper.value == 0, "value defaults to 0")
23 XCTAssert(stepper.value == 0, "value defaults to 0")
24 }
24 }
25
25
@@ -37,6 +37,6 @@ class GMStepperExampleTests: XCTestCase {
37
37
38 XCTAssert(stepper.value == stepper.maximumValue, "Pass")
38 XCTAssert(stepper.value == stepper.maximumValue, "Pass")
39 }
39 }
40−
40+
41−
41+
42}
42}
GMStepperExample/GMStepperExampleTests/Info.plist+1−3
@@ -7,7 +7,7 @@
7 <key>CFBundleExecutable</key>
7 <key>CFBundleExecutable</key>
8 <string>$(EXECUTABLE_NAME)</string>
8 <string>$(EXECUTABLE_NAME)</string>
9 <key>CFBundleIdentifier</key>
9 <key>CFBundleIdentifier</key>
10− <string>com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)</string>
10+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11 <key>CFBundleInfoDictionaryVersion</key>
11 <key>CFBundleInfoDictionaryVersion</key>
12 <string>6.0</string>
12 <string>6.0</string>
13 <key>CFBundleName</key>
13 <key>CFBundleName</key>
@@ -16,8 +16,6 @@
16 <string>BNDL</string>
16 <string>BNDL</string>
17 <key>CFBundleShortVersionString</key>
17 <key>CFBundleShortVersionString</key>
18 <string>1.0</string>
18 <string>1.0</string>
19− <key>CFBundleSignature</key>
20− <string>????</string>
21 <key>CFBundleVersion</key>
19 <key>CFBundleVersion</key>
22 <string>1</string>
20 <string>1</string>
23</dict>
21</dict>
README.md+30−10
@@ -3,11 +3,12 @@
3[![License](https://img.shields.io/cocoapods/l/GMStepper.svg?style=flat)](http://cocoapods.org/pods/GMStepper)
3[![License](https://img.shields.io/cocoapods/l/GMStepper.svg?style=flat)](http://cocoapods.org/pods/GMStepper)
4[![Platform](https://img.shields.io/cocoapods/p/GMStepper.svg?style=flat)](http://cocoapods.org/pods/GMStepper)
4[![Platform](https://img.shields.io/cocoapods/p/GMStepper.svg?style=flat)](http://cocoapods.org/pods/GMStepper)
5
5
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+
7
8
8## Screenshot
9## Screenshot
9
10
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)
11
12
12
13
13## Usage
14## Usage
@@ -19,7 +20,7 @@ it, simply add the following lines to your Podfile:
19use_frameworks!
20use_frameworks!
20pod "GMStepper"
21pod "GMStepper"
21```
22```
22−
23+
23## Setup
24## Setup
24
25
25If 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.
26If 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
27## Properties
28## Properties
28```swift
29```swift
29/// Current value of the stepper. Defaults to 0.
30/// Current value of the stepper. Defaults to 0.
30−var value: Int = 0
31+var value: Double = 0
31
32
32/// Minimum value. Must be less than maximumValue. Defaults to 0.
33/// 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
34
41
35−/// Maximum value. Must be more than minimumValue. Defaults to 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.
36−var maximumValue: Int = 10
43+var autorepeat: Bool = true
44+
45+/// If the value is integer, it is shown without floating point.
46+var showIntegerIfDoubleIsInteger: Bool = true
37
47
38/// Text on the left button. Be sure that it fits in the button. Defaults to "-".
48/// Text on the left button. Be sure that it fits in the button. Defaults to "-".
39var leftButtonText: String = "-"
49var leftButtonText: String = "-"
@@ -59,20 +69,30 @@ var labelBackgroundColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alph
59/// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
69/// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
60var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)
70var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)
61
71
72+/// Corner radius of the middle label's layer. Defaults to 0.0.
73+var labelCornerRadius: CGFloat = 0.0
74+
62/// Corner radius of the stepper's layer. Defaults to 4.0.
75/// Corner radius of the stepper's layer. Defaults to 4.0.
63var cornerRadius: CGFloat = 4.0
76var cornerRadius: CGFloat = 4.0
64
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+
65/// 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.
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.
66−var labelWidthWeight: CGFloat = 0.5
85+var labelWidthWeight: CGFloat = 0.5
67
86
68/// Color of the flashing animation on the buttons in case the value hit the limit.
87/// Color of the flashing animation on the buttons in case the value hit the limit.
69var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
88var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
70```
89```
71
90
72
91
73−## Author
92+## Authors
74
93
94+* [Brent Whitman](https://github.com/bwhtmn) - Maintainer
75−Günay Mert Karadoğan, mertkaradogan@gmail.com
95+* [Günay Mert Karadoğan](https://github.com/gmertk) - Creator
76
96
77## License
97## License
78
98
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.