-
-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathUIView+Shake.swift
153 lines (129 loc) · 5.37 KB
/
UIView+Shake.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//
// UIView+Shake.swift
// SingleLineShakeAnimation
//
// Created by Håkon Bogen on 24/04/15.
// Copyright (c) 2015 haaakon. All rights reserved.
//
import UIKit
public enum ShakeDirection: Int {
case Horizontal
case Vertical
private func startPosition() -> ShakePosition {
switch self {
case .Horizontal:
return ShakePosition.Left
default:
return ShakePosition.Top
}
}
}
public enum Vibration {
case Light
case Medium
case Heavy
@available(iOS 10.0, *)
func hapticFeedback() -> UIImpactFeedbackGenerator {
switch self {
case .Heavy:
return UIImpactFeedbackGenerator(style: .heavy)
case .Medium:
return UIImpactFeedbackGenerator(style: .medium)
case .Light:
return UIImpactFeedbackGenerator(style: .light)
}
}
}
private struct DefaultValues {
static let numberOfTimes = 5
static let totalDuration: Float = 0.5
}
extension UIView {
/**
Shake a view back and forth for the number of times given in the duration specified.
If the total duration given is 1 second, and the number of shakes is 5, it will use 0.20 seconds per shake.
After it's done shaking, the completion handler is called, if specified.
- parameter direction: The direction to shake (horizontal or vertical motion)
- parameter numberOfTimes: The total number of times to shake back and forth, default value is 5
- parameter totalDuration: Total duration to do the shakes, default is 0.5 seconds
- parameter completion: Optional completion closure
*/
public func shake(direction: ShakeDirection, numberOfTimes: Int = DefaultValues.numberOfTimes, totalDuration: Float = DefaultValues.totalDuration, completion: (() -> Void)? = nil) -> UIView? {
if UIAccessibilityIsVoiceOverRunning() {
return self
} else {
let timePerShake = Double(totalDuration) / Double(numberOfTimes)
shake(forTimes: numberOfTimes, position: direction.startPosition(), durationPerShake: timePerShake, completion: completion)
return nil
}
}
public func postAccessabilityNotification(text text: String ) {
var hasRead = false
NSNotificationCenter.defaultCenter().addObserverForName(UIAccessibilityAnnouncementDidFinishNotification, object: nil, queue: nil) { (notification) -> Void in
if hasRead == false {
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, text)
hasRead = true
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIAccessibilityAnnouncementDidFinishNotification, object:nil)
}
}
// seems to be a bug with UIAccessability that does not allow to post a notification with text in the action when tapping a button
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.01 * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, " ")
}
}
@available(iOS 10.0, *)
public func withHapticVibration(_ intesity: Vibration) {
intesity.hapticFeedback().impactOccurred()
}
func didFinishReadingAccessabilityLabel() {
dispatch_async(dispatch_get_main_queue(), {
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, "hello world")
})
}
private func shake(forTimes forTimes: Int, position: ShakePosition, durationPerShake: NSTimeInterval, completion: (() -> Void)?) {
UIView.animateWithDuration(durationPerShake, animations: { () -> Void in
switch position.direction {
case .Horizontal:
self.layer.setAffineTransform( CGAffineTransformMakeTranslation(2 * position.value, 0))
break
case .Vertical:
self.layer.setAffineTransform( CGAffineTransformMakeTranslation(0, 2 * position.value))
break
}
}) { (complete) -> Void in
if (forTimes == 0) {
UIView.animateWithDuration(durationPerShake, animations: { () -> Void in
self.layer.setAffineTransform(CGAffineTransformIdentity)
}, completion: { (complete) -> Void in
completion?()
})
} else {
self.shake(forTimes: forTimes - 1, position: position.oppositePosition(), durationPerShake: durationPerShake, completion:completion)
}
}
}
}
private struct ShakePosition {
let value: CGFloat
let direction: ShakeDirection
init(value: CGFloat, direction: ShakeDirection) {
self.value = value
self.direction = direction
}
func oppositePosition() -> ShakePosition {
return ShakePosition(value: (self.value * -1), direction: direction)
}
static var Left: ShakePosition {
return ShakePosition(value: 1, direction: .Horizontal)
}
static var Right: ShakePosition {
return ShakePosition(value: -1, direction: .Horizontal)
}
static var Top: ShakePosition {
return ShakePosition(value: 1, direction: .Vertical)
}
static var Bottom: ShakePosition {
return ShakePosition(value: -1, direction: .Vertical)
}
}