@@ -11,79 +11,59 @@ import UIKit
1111@IBDesignable public class GMStepper: UIControl {
1212
1313 /// Current value of the stepper. Defaults to 0.
14− @objc @IBInspectable public var value: Double = 0 {
14+ public var value = 0 {
1515 didSet {
1616 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 {
30− label.text = String(stringInterpolationSegment: value)
31− }
17+ label.text = String(value)
3218
3319 if oldValue != value {
34− sendActions(for: .valueChanged)
20+ sendActionsForControlEvents(.ValueChanged)
3521 }
3622 }
3723 }
3824
3925 /// Minimum value. Must be less than maximumValue. Defaults to 0.
40− @objc @IBInspectable public var minimumValue: Double = 0 {
26+ @IBInspectable public var minimumValue: Int = 0 {
4127 didSet {
42− value = min(maximumValue, max(minimumValue, value))
28+ value = 0
4329 }
4430 }
4531
46− /// Maximum value. Must be more than minimumValue. Defaults to 100.
47− @objc @IBInspectable public var maximumValue: Double = 100 {
32+ /// Maximum value. Must be more than minimumValue. Defaults to 10.
33+ @IBInspectable public var maximumValue: Int = 10 {
4834 didSet {
49− value = min(maximumValue, max(minimumValue, value))
35+ value = 0
5036 }
5137 }
5238
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
39+ /// The same as UIStepper's autorepeat. If true, holding on the buttons or the pan gesture repeatedly alters value. Defaults to true.
40+ @IBInspectable public var autorepeat: Bool = true
6141
62− /// Text on the left button. Be sure that it fits in the button. Defaults to "−".
63− @objc @IBInspectable public var leftButtonText: String = "−" {
42+ /// Text on the left button. Be sure that it fits in the button. Defaults to "-".
43+ @IBInspectable public var leftButtonText: String = "-" {
6444 didSet {
65− leftButton.setTitle(leftButtonText, for: .normal)
45+ leftButton.setTitle(leftButtonText, forState: .Normal)
6646 }
6747 }
6848
6949 /// Text on the right button. Be sure that it fits in the button. Defaults to "+".
70− @objc @IBInspectable public var rightButtonText: String = "+" {
50+ @IBInspectable public var rightButtonText: String = "+" {
7151 didSet {
72− rightButton.setTitle(rightButtonText, for: .normal)
52+ rightButton.setTitle(rightButtonText, forState: .Normal)
7353 }
7454 }
7555
7656 /// Text color of the buttons. Defaults to white.
77− @objc @IBInspectable public var buttonsTextColor: UIColor = UIColor.white {
57+ @IBInspectable public var buttonsTextColor: UIColor = UIColor.whiteColor() {
7858 didSet {
7959 for button in [leftButton, rightButton] {
80− button.setTitleColor(buttonsTextColor, for: .normal)
60+ button.setTitleColor(buttonsTextColor, forState: .Normal)
8161 }
8262 }
8363 }
8464
8565 /// Background color of the buttons. Defaults to dark blue.
86− @objc @IBInspectable public var buttonsBackgroundColor: UIColor = UIColor(red:0.21, green:0.5, blue:0.74, alpha:1) {
66+ @IBInspectable public var buttonsBackgroundColor: UIColor = UIColor(red:0.21, green:0.5, blue:0.74, alpha:1) {
8767 didSet {
8868 for button in [leftButton, rightButton] {
8969 button.backgroundColor = buttonsBackgroundColor
@@ -93,7 +73,7 @@ import UIKit
9373 }
9474
9575 /// Font of the buttons. Defaults to AvenirNext-Bold, 20.0 points in size.
96− @objc public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
76+ public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! {
9777 didSet {
9878 for button in [leftButton, rightButton] {
9979 button.titleLabel?.font = buttonsFont
@@ -102,174 +82,70 @@ import UIKit
10282 }
10383
10484 /// Text color of the middle label. Defaults to white.
105− @objc @IBInspectable public var labelTextColor: UIColor = UIColor.white {
85+ @IBInspectable public var labelTextColor: UIColor = UIColor.whiteColor() {
10686 didSet {
10787 label.textColor = labelTextColor
10888 }
10989 }
11090
11191 /// Text color of the middle label. Defaults to lighter blue.
112− @objc @IBInspectable public var labelBackgroundColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1) {
92+ @IBInspectable public var labelBackgroundColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1) {
11393 didSet {
11494 label.backgroundColor = labelBackgroundColor
11595 }
11696 }
11797
11898 /// Font of the middle label. Defaults to AvenirNext-Bold, 25.0 points in size.
119− @objc public var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)! {
99+ public var labelFont = UIFont(name: "AvenirNext-Bold", size: 25.0)! {
120100 didSet {
121101 label.font = labelFont
122102 }
123103 }
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− }
131104
132105 /// Corner radius of the stepper's layer. Defaults to 4.0.
133− @objc @IBInspectable public var cornerRadius: CGFloat = 4.0 {
106+ @IBInspectable public var cornerRadius: CGFloat = 4.0 {
134107 didSet {
135108 layer.cornerRadius = cornerRadius
136109 clipsToBounds = true
137110 }
138111 }
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− }
155112
156113 /// 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.
157− @objc @IBInspectable public var labelWidthWeight: CGFloat = 0.5 {
114+ @IBInspectable public var labelWidthWeight: CGFloat = 0.5 {
158115 didSet {
159116 labelWidthWeight = min(1, max(0, labelWidthWeight))
160117 setNeedsLayout()
161118 }
162119 }
163120
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−
167121 /**
168122 Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
169123 */
170− let labelSlideLength: CGFloat = 5
124+ private let labelSlideLength: CGFloat = 5
171125
172126 /// Duration of the sliding animation
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− }()
127+ private let labelSlideDuration = NSTimeInterval(0.1)
190128
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− }()
129+ /// Duration of the flashing animation when the value hits the limit.
130+ private let limitHitAnimationDuration = NSTimeInterval(0.1)
203131
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− }()
132+ /// Color of the flashing animation on the buttons in case the value hit the limit.
133+ @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
223134
224− var labelOriginalCenter: CGPoint!
225− var labelMaximumCenterX: CGFloat!
226− var labelMinimumCenterX: CGFloat!
135+ private let leftButton = UIButton()
136+ private let rightButton = UIButton()
137+ private let label = UILabel()
227138
228− enum LabelPanState {
229− case Stable, HitRightEdge, HitLeftEdge
230− }
231− var panState = LabelPanState.Stable
139+ private var labelOriginalCenter: CGPoint!
140+ private var labelMaximumCenterX: CGFloat!
141+ private var labelMinimumCenterX: CGFloat!
232142
233− enum StepperState {
234− case Stable, ShouldIncrease, ShouldDecrease
235− }
236− var stepperState = StepperState.Stable {
237− didSet {
238− if stepperState != .Stable {
239− updateValue()
240− if autorepeat {
241− scheduleTimer()
242− }
243− }
244− }
245− }
246−
247−
248− @objc public var items : [String] = [] {
249− didSet {
250− let isInteger = floor(value) == value
251−
252− //
253− // If we have items, we will display them as steps
254− //
255−
256− if isInteger && stepValue == 1.0 && items.count > 0 {
257−
258− var value = Int(self.value)
259−
260− if value >= items.count {
261− value = items.count - 1
262− self.value = Double(value)
263− }
264− else {
265− label.text = items[value]
266− }
267− }
268− }
143+ private enum LabelPanState {
144+ case Stale, HitRightEdge, HitLeftEdge
269145 }
146+ private var panState = LabelPanState.Stale
270147
271− /// Timer used for autorepeat option
272− var timer: Timer?
148+ private var timer: NSTimer?
273149
274150 /** When UIStepper reaches its top speed, it alters the value with a time interval of ~0.05 sec.
275151 The user pressing and holding on the stepper repeatedly:
@@ -277,11 +153,11 @@ import UIKit
277153 - For the next 1.5 sec, it changes the value every 0.1 sec.
278154 - Then, every 0.05 sec.
279155 */
280− let timerInterval = TimeInterval(0.05)
156+ private let timerInterval = NSTimeInterval(0.05)
281157
282158 /// 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 {
159+ private var timerFireCount = 0
160+ private var timerFireCountModulo: Int {
285161 if timerFireCount > 80 {
286162 return 1 // 0.05 sec * 1 = 0.05 sec
287163 } else if timerFireCount > 50 {
@@ -291,27 +167,61 @@ import UIKit
291167 }
292168 }
293169
294− @objc required public init?(coder aDecoder: NSCoder) {
170+ lazy var printTimerGaps: () -> () = {
171+ var prevTime: CFAbsoluteTime?
172+
173+ return { _ in
174+ var now = CFAbsoluteTimeGetCurrent()
175+ if let prevTime = prevTime {
176+ println(now - prevTime)
177+ }
178+ prevTime = now
179+ }
180+ }()
181+
182+ required public init(coder aDecoder: NSCoder) {
295183 super.init(coder: aDecoder)
296184 setup()
297185 }
298186
299− @objc public override init(frame: CGRect) {
187+ public override init(frame: CGRect) {
300188 super.init(frame: frame)
301189 setup()
302190 }
303191
304− func setup() {
192+ private func setup() {
193+ leftButton.setTitle(leftButtonText, forState: .Normal)
194+ leftButton.setTitleColor(buttonsTextColor, forState: .Normal)
195+ leftButton.backgroundColor = buttonsBackgroundColor
196+ leftButton.titleLabel?.font = buttonsFont
197+ leftButton.addTarget(self, action: "leftButtonTouchDown:", forControlEvents: .TouchDown)
198+ leftButton.addTarget(self, action: "leftButtonTouchUp:", forControlEvents: UIControlEvents.TouchUpInside)
199+ leftButton.addTarget(self, action: "leftButtonTouchUp:", forControlEvents: UIControlEvents.TouchUpOutside)
305200 addSubview(leftButton)
201+
202+ rightButton.setTitle(rightButtonText, forState: .Normal)
203+ rightButton.setTitleColor(buttonsTextColor, forState: .Normal)
204+ rightButton.backgroundColor = buttonsBackgroundColor
205+ rightButton.titleLabel?.font = buttonsFont
206+ rightButton.addTarget(self, action: "rightButtonTapped:", forControlEvents: .TouchDown)
207+ rightButton.addTarget(self, action: "rightButtonTouchUp:", forControlEvents: UIControlEvents.TouchUpInside)
208+ rightButton.addTarget(self, action: "rightButtonTouchUp:", forControlEvents: UIControlEvents.TouchUpOutside)
306209 addSubview(rightButton)
210+
211+ label.textAlignment = .Center
212+ label.text = String(value)
213+ label.textColor = labelTextColor
214+ label.backgroundColor = labelBackgroundColor
215+ label.font = labelFont
307216 addSubview(label)
308217
309218 backgroundColor = buttonsBackgroundColor
310219 layer.cornerRadius = cornerRadius
311220 clipsToBounds = true
312− labelOriginalCenter = label.center
313221
314− NotificationCenter.default.addObserver(self, selector: #selector(GMStepper.reset), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
222+ let panRecognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
223+ label.userInteractionEnabled = true
224+ label.addGestureRecognizer(panRecognizer)
315225 }
316226
317227 public override func layoutSubviews() {
@@ -327,46 +237,16 @@ import UIKit
327237 labelOriginalCenter = label.center
328238 }
329239
330− func updateValue() {
331− if stepperState == .ShouldIncrease {
332− value += stepValue
333− } else if stepperState == .ShouldDecrease {
334− value -= stepValue
335− }
336− }
337−
338− deinit {
339− resetTimer()
340− NotificationCenter.default.removeObserver(self)
341− }
342240
343− /// Useful closure for logging the timer interval. You can call this in the timer handler to test the autorepeat option. Not used in the current implementation.
344−// lazy var printTimerGaps: () -> () = {
345−// var prevTime: CFAbsoluteTime?
346−//
347−// return { _ in
348−// var now = CFAbsoluteTimeGetCurrent()
349−// if let prevTime = prevTime {
350−// print(now - prevTime)
351−// }
352−// prevTime = now
353−// }
354−// }()
355−}
356241
357−// MARK: Pan Gesture
358−extension GMStepper {
359− @objc func handlePan(gesture: UIPanGestureRecognizer) {
242+ @objc private func handlePan(gesture: UIPanGestureRecognizer) {
360243 switch gesture.state {
361− case .began:
362− leftButton.isEnabled = false
363− rightButton.isEnabled = false
364− case .changed:
365− let translation = gesture.translation(in: label)
366− gesture.setTranslation(CGPoint.zero, in: label)
244+ case .Changed:
245+ var translation = gesture.translationInView(label)
246+ gesture.setTranslation(CGPointZero, inView: label)
367247
368− let slidingRight = gesture.velocity(in: label).x > 0
369− let slidingLeft = gesture.velocity(in: label).x < 0
248+ var slidingRight = gesture.velocityInView(label).x > 0
249+ var slidingLeft = gesture.velocityInView(label).x < 0
370250
371251 // Move the label with pan
372252 if slidingRight {
@@ -377,45 +257,44 @@ extension GMStepper {
377257
378258 // When the label hits the edges, increase/decrease value and change button backgrounds
379259 if label.center.x == labelMaximumCenterX {
380− // If not hit the right edge before, increase the value and start the timer. If already hit the edge, do nothing. Timer will handle it.
260+
261+ // Increase only once for each pan gesture
381262 if panState != .HitRightEdge {
382− stepperState = .ShouldIncrease
263+ value += 1
264+ scheduleTimerWithUserInfo(rightButton)
383265 panState = .HitRightEdge
384266 }
385−
386− animateLimitHitIfNeeded()
267+
268+ if value == maximumValue {
269+ animateLimitHitForButton(rightButton)
270+ }
387271 } else if label.center.x == labelMinimumCenterX {
272+
388273 if panState != .HitLeftEdge {
389− stepperState = .ShouldDecrease
274+ value -= 1
275+ scheduleTimerWithUserInfo(leftButton)
390276 panState = .HitLeftEdge
391277 }
392278
393− animateLimitHitIfNeeded()
279+ if value == minimumValue {
280+ animateLimitHitForButton(leftButton)
281+ }
394282 } else {
395− panState = .Stable
396− stepperState = .Stable
397− resetTimer()
398−
399− self.rightButton.backgroundColor = self.buttonsBackgroundColor
400− self.leftButton.backgroundColor = self.buttonsBackgroundColor
283+ reset()
401284 }
402− case .ended, .cancelled, .failed:
285+
286+ case .Ended:
403287 reset()
404288 default:
405289 break
406290 }
407291 }
408292
409− @objc func reset() {
410− panState = .Stable
411− stepperState = .Stable
293+ private func reset() {
294+ panState = .Stale
412295 resetTimer()
413296
414− leftButton.isEnabled = true
415− rightButton.isEnabled = true
416− label.isUserInteractionEnabled = true
417−
418− UIView.animate(withDuration: self.labelSlideDuration, animations: {
297+ UIView.animateWithDuration(self.labelSlideDuration, animations: {
419298 self.label.center = self.labelOriginalCenter
420299 self.rightButton.backgroundColor = self.buttonsBackgroundColor
421300 self.leftButton.backgroundColor = self.buttonsBackgroundColor
@@ -425,88 +304,90 @@ extension GMStepper {
425304
426305// MARK: Button Events
427306extension GMStepper {
428− @objc func leftButtonTouchDown(button: UIButton) {
429− rightButton.isEnabled = false
430− label.isUserInteractionEnabled = false
431− resetTimer()
432−
307+ @objc private func leftButtonTouchDown(button: UIButton) {
433308 if value == minimumValue {
434− animateLimitHitIfNeeded()
309+ animateLimitHitForButton(leftButton)
435310 } else {
436− stepperState = .ShouldDecrease
311+ value -= 1
312+ if autorepeat {
313+ scheduleTimerWithUserInfo(leftButton)
314+ }
437315 animateSlideLeft()
438316 }
439−
440317 }
441318
442− @objc func rightButtonTouchDown(button: UIButton) {
443− leftButton.isEnabled = false
444− label.isUserInteractionEnabled = false
445− resetTimer()
319+ @objc private func leftButtonTouchUp(button: UIButton) {
320+ reset()
321+ stopTimer()
322+ }
446323
324+ @objc private func rightButtonTapped(button: UIButton) {
447325 if value == maximumValue {
448− animateLimitHitIfNeeded()
326+ animateLimitHitForButton(rightButton)
449327 } else {
450− stepperState = .ShouldIncrease
328+ value += 1
329+ if autorepeat {
330+ scheduleTimerWithUserInfo(rightButton)
331+ }
451332 animateSlideRight()
452333 }
453334 }
454335
455− @objc func buttonTouchUp(button: UIButton) {
336+ @objc private func rightButtonTouchUp(button: UIButton) {
456337 reset()
338+ stopTimer()
457339 }
458340}
459341
460342// MARK: Animations
461343extension GMStepper {
462344
463− func animateSlideLeft() {
464− UIView.animate(withDuration: labelSlideDuration) {
345+ private func animateSlideLeft() {
346+ UIView.animateWithDuration(labelSlideDuration) {
465347 self.label.center.x -= self.labelSlideLength
466348 }
467349 }
468350
469− func animateSlideRight() {
470− UIView.animate(withDuration: labelSlideDuration) {
351+ private func animateSlideRight() {
352+ UIView.animateWithDuration(labelSlideDuration) {
471353 self.label.center.x += self.labelSlideLength
472354 }
473355 }
474356
475− func animateToOriginalPosition() {
357+ private func animateToOriginalPosition() {
476358 if self.label.center != self.labelOriginalCenter {
477− UIView.animate(withDuration: labelSlideDuration) {
359+ UIView.animateWithDuration(labelSlideDuration) {
478360 self.label.center = self.labelOriginalCenter
479361 }
480362 }
481363 }
482364
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) {
365+ private func animateLimitHitForButton(button: UIButton){
366+ UIView.animateWithDuration(limitHitAnimationDuration) {
493367 button.backgroundColor = self.limitHitAnimationColor
494368 }
495369 }
370+
496371}
497372
498373// MARK: Timer
499374extension GMStepper {
500− @objc func handleTimerFire(timer: Timer) {
375+ func handleTimerFire(timer: NSTimer) {
501376 timerFireCount += 1
502−
503377 if timerFireCount % timerFireCountModulo == 0 {
504− updateValue()
378+
379+ if let button = timer.userInfo as? UIButton {
380+ if button == leftButton {
381+ value -= 1
382+ } else {
383+ value += 1
384+ }
385+ }
505386 }
506387 }
507388
508− func scheduleTimer() {
509− timer = Timer.scheduledTimer(timeInterval: timerInterval, target: self, selector: #selector(GMStepper.handleTimerFire), userInfo: nil, repeats: true)
389+ func scheduleTimerWithUserInfo(userInfo: AnyObject?) {
390+ timer = NSTimer.scheduledTimerWithTimeInterval(timerInterval, target: self, selector: "handleTimerFire:", userInfo: userInfo, repeats: true)
510391 }
511392
512393 func resetTimer() {
@@ -518,4 +399,4 @@ extension GMStepper {
518399 }
519400
520401
521−}
402+}