├── .gitignore
├── .swift-version
├── Actions.podspec
├── CHANGELOG.md
├── LICENSE
├── README.md
├── actions.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ ├── actions.xcscheme
│ │ └── actions_app.xcscheme
└── xcuserdata
│ └── Manu.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── actions
├── Info.plist
├── actions.h
└── actions
│ ├── Action.swift
│ ├── NotificationCenter+Actions.swift
│ ├── Throttle.swift
│ ├── Timer+Actions.swift
│ ├── UIBarButtonItem+Actions.swift
│ ├── UIControl+Actions.swift
│ ├── UIControl+Throttle.swift
│ ├── UIGestureRecognizer+Actions.swift
│ └── UIView+Actions.swift
└── actions_app
├── AppDelegate.swift
├── Assets.xcassets
└── AppIcon.appiconset
│ └── Contents.json
├── Base.lproj
├── LaunchScreen.storyboard
└── Main.storyboard
├── Info.plist
├── data
└── Data.swift
└── scenes
├── BaseViewController.swift
├── HomeViewController.swift
├── NotificationCenterViewController.swift
├── TimerViewController.swift
├── UIBarbuttonItemViewController.swift
├── UIControlViewController.swift
├── UIGestureRecognizerViewController.swift
└── UIViewViewController.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | actions.xcodeproj/xcuserdata
2 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 4.2
2 |
--------------------------------------------------------------------------------
/Actions.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 |
3 | spec.name = "Actions"
4 | spec.version = "3.0.1"
5 | spec.summary = "An easy way to add swift closures to UIView, UIControl and more"
6 | spec.description = <<-DESC
7 | Actions provides a set of extensions to add closures to `UIView` and `UIControl` instances. Also brings some methods to `UIBarButtonItem`, `UIGestureRecognizer`, `Timer` and `NotificationCenter`, that allow using them with a closure instead of a pair of target/action.
8 | DESC
9 | spec.homepage = "https://github.com/ManueGE/Actions/"
10 | spec.license = "MIT"
11 |
12 |
13 | spec.author = "Manuel García-Estañ"
14 | spec.social_media_url = "http://twitter.com/ManueGE"
15 |
16 | spec.platform = :ios, "8.0"
17 | spec.source = { :git => "https://github.com/ManueGE/Actions.git", :tag => "#{spec.version}" }
18 |
19 | spec.requires_arc = true
20 | spec.framework = "Foundation"
21 |
22 | spec.source_files = "actions/actions/*.{swift}"
23 | spec.pod_target_xcconfig = { 'SWIFT_VERSION' => '4.2' }
24 |
25 | end
26 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ### 3.0.0 (25 September 2018)
4 | - Swift 4.2 & Xcode 10
5 |
6 | ### 2.2.1 (3 April 2017)
7 | - Avoid retainc cycle in parametized actions. ([PR #5](https://github.com/ManueGE/Actions/pull/5))
8 |
9 | ### 2.2.0 (27 October 2016)
10 | - New method to stop observing notifications in `NotificationServer`:
11 |
12 | `stopObserver(_ obse3 April 2017rver: NSObject, name: NSNotification.Name? = nil, object: AnyObject? = nil)`
13 |
14 | This allows you stop observing a notification without storing the `Action` returned when an observation starts.
15 |
16 | - New method to remove actions for a given gesture recognizer in `UIControl`:
17 |
18 | `removeActions(for events: UIControlEvents)`
19 |
20 | This allows you cancel an action without storing the `Action` returned when it is added.
21 |
22 | - Deprecated `UIControl` `remove(action:, forControlEvents)` in favor of `remove(_:for:)`.
23 |
24 |
25 | ### 2.1.1 (20 October 2016)
26 | - Support for Carthage.
27 |
28 | ### 2.1.0 (11 October 2016)
29 | - Add `throttle` extension for `UIControl`.
30 |
31 | ### 2.0.1 (15 September 2016)
32 | - Replace `(Void)` with `()` in closures definition.
33 |
34 | ### 2.0.0 (11 September 2016)
35 | - **Swift 3** support.
36 |
37 | ### 1.3.0 (10 July 2016)
38 | - `addObserver(to:object:action:)` has been deprecated in favor of `observe(name:object:action:)`.
39 | - `NSNotificationCenter` observations can be stopped by calling `stopObserving(action)`.
40 | - New `NSNotificationCenter` method (`add(observer:name:object:action:`) to add actions binded to the lifetime of a given object.
41 |
42 | ### 1.2.0 (6 July 2016)
43 | - Add support to `NSNotificationCenter`.
44 |
45 | ### 1.1.0 (4 July 2016)
46 | - Add support to `NSTimer`.
47 |
48 | ### 1.0.0 (1 July 2016)
49 | - Initial version with support to `UIView`, `UIControl`, `UIGestureRecognizer` and `UIBarButtonItem`.
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Manuel García-Estañ
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Actions
2 | [](https://github.com/Carthage/Carthage)
3 |
4 | **Actions** provides a set of extensions to add closures to `UIView` and `UIControl` instances. Also brings some methods to `UIBarButtonItem`, `UIGestureRecognizer`, `Timer` and `NotificationCenter`, that allow using them with a closure instead of a pair of target/action.
5 |
6 | With **Actions**, you will easily add actions this way:
7 |
8 | ````swift
9 | // UIView
10 | let imageView = UIImageView()
11 | imageView.add(gesture: .swipe(.Left)) {
12 | print("Image swipped")
13 | }
14 |
15 | // UIControl
16 | let button = UIButton()
17 | button.add(event: .touchUpInside) {
18 | print("Button tapped")
19 | }
20 |
21 | let textField = UITextField()
22 | textField.throttle(.editingChanged, interval: 0.5) { (textField: UITextField) in
23 | print("Text changed to: \(textField.text)")
24 | }
25 |
26 | // UIGestureRecognizer
27 | let gestureRecognizer = UIRotationGestureRecognizer {
28 | print("Gesture triggered")
29 | }
30 |
31 | // UIBarButtonItem
32 | let barButtonItem = UIBarButtonItem(title: "Title") {
33 | print("Bar button item tapped")
34 | }
35 |
36 | // Timer (formally NSTimer)
37 | Timer.scheduledTimer(timeInterval: 5) {
38 | print("timer fired")
39 | }
40 |
41 | // NotificationCenter (formally NSNotificationCenter)
42 | NotificationCenter.default.add(observer: self, name: "NotificationName") {
43 | print("Notification received")
44 | }
45 | ````
46 |
47 | Keep reading to know how!
48 |
49 | ## Installation
50 |
51 | ### Cocoapods
52 | Add the following to your `Podfile`:
53 |
54 | ````
55 | pod 'Actions'
56 | ````
57 |
58 | Then run `$ pod install`.
59 |
60 | And finally, in the classes where you need **Actions**:
61 |
62 | ````
63 | import Actions
64 | ````
65 |
66 | If you don’t have CocoaPods installed or integrated into your project, you can learn how to do so [here](http://cocoapods.org).
67 |
68 | ### Carthage
69 | Add the following to your `Cartfile`:
70 |
71 | ```
72 | github “ManueGE/Actions”
73 | ```
74 |
75 | Then run `$ carthage update`.
76 |
77 | If you need more info about Carthage you will find it [here](https://github.com/Carthage/Carthage]).
78 |
79 | ## Usage
80 |
81 | ## Supported classes
82 | - [UIView](#UIView)
83 | - [UIControl](#UIControl)
84 | - [UIGestureRecognizer](#UIGestureRecognizer)
85 | - [UIBarButtonItem](#UIBarButtonItem)
86 | - [Timer](#Timer)
87 | - [NotificationCenter](#NotificationCenter)
88 |
89 |
90 | ### UIView [☝️](#usage)
91 |
92 | You can make your `UIViews` to respond to simple touches. The allowed gestures are members of the enum `Gestures` and their values are:
93 |
94 | - `tap`: One finger tap, accept one `Int` parameter that indicate the number of required touches.
95 | - `swipe`: One finger swipe, accept one `UISwipeGestureRecognizer.Direction` parameter that indicate the direction of the swip.
96 | - `multiTap`: Tap with any number of fingers, accept two `Int` parameter that indicate the number of required fingers and touches.
97 | - `multiSwipe`: Swipe with any number of fingers; accept one `UISwipeGestureRecognizer.Direction` parameter that indicate the direction of the swip and an `Int` parameter that indicate the number of required fingers and touches.
98 |
99 | To add one of this gestures to a `UIView` you can do:
100 |
101 | ````swift
102 | let view = UIView()
103 |
104 | // Not any gesture argument means .tap(1):
105 | view.addAction {
106 | print("view tapped")
107 | }
108 |
109 | // You can also make the closure have one argument (the own view):
110 | view.addAction { (view: UIView) in
111 | print("view \(view) tapped")
112 | }
113 |
114 | // Add 3 tap gesture
115 | view.add(gesture: .tap(3)) {
116 | print("View tapped 3 times")
117 | }
118 |
119 | // Add a multi swipe gesture with the view as closure argument
120 | view.add(gesture: .multiSwipe(direction: .Left, fingers: 2)) { (view: UIView) in
121 | print("View \(view) swipped left with 2 fingers")
122 | }
123 | ````
124 |
125 | All the add action methods returns the UIGestureRecognizer added to the view, in case you need it.
126 |
127 |
128 | ### UIControl [☝️](#usage)
129 |
130 | Assign actions to your `UIControl` events.
131 |
132 | You can add three types of closures:
133 |
134 | - Without any argument
135 | - With one argument, it will be the control itself.
136 | - With two arguments, the first one will be the control itself, the second one will be the `UIEvent?`.
137 |
138 | You can add actions:
139 |
140 | - To a single `UIControl.Event`, using the method `add(event: UIControl.Event, action: () -> Void)`
141 | - To multple control events at the same time: `add(events: [UIControl.Event], action: ()) -> Void)`
142 |
143 | Here there are some examples:
144 |
145 | ````swift
146 | // Closure without arguments and single event
147 | button.add(event: .touchUpInside) {
148 | print("button tapped")
149 | }
150 |
151 | // Closure with one argument and multiple events
152 | textField.add(events: [.editingChanged, .editingDidEnd]) { (textField: UITextField) in
153 | print("Text did change: \(textField.text)")
154 | }
155 |
156 | // Closure with two arguments
157 | button.add(event: .touchUpInside) { (sender, event) in
158 | print("Sender: \(sender), Event: \(event)")
159 | }
160 | ````
161 |
162 | Actions added using this methods can be removed using the method:
163 |
164 | `func removeActions(for events: UIControl.Event)`
165 |
166 | > **Note**: Just the actions added using the `Actions` method will be removed!.
167 |
168 |
169 | #### Throttle
170 | Actions allows `UIControl` schedulling actions to be called after a specific time interval, and prevent it of being called more than once in that interval. In other words, if the action is scheduled again before the time interval expires, it cancels the previous call (if any) preventing the action to be called twice.
171 |
172 | A typical example is a `UITextField` which will trigger a search every time the user enter some text. If this search implies a http request, it can lead to overload the server if you make a request every time the user enter a character. With `throttle` you can wait a few milliseconds to check if the user is still typing and just send the action if the user hasn't entered any character in a time interval.
173 |
174 | You can use throttle this way:
175 |
176 | ````swift
177 | textField.throttle(.editingChanged, interval: 0.5) { [unowned self] (textField: UITextField) in
178 | self.performSearch(with: textField.text)
179 | }
180 | ````
181 |
182 | Other than the `UIControl` extension, the `Throttle` class can be used standalone to add your custom throttles all over your app.
183 |
184 |
185 | ### UIGestureRecognizer [☝️](#usage)
186 |
187 | Create `UIGestureRecognizer` with a closure instead of a pair of target/action:
188 |
189 | ````swift
190 | // without argument
191 | let recognizer = UIRotationGestureRecognizer {
192 | print("Gesture triggered")
193 | }
194 |
195 | // with argument
196 | let recognizer = UIRotationGestureRecognizer { (recognizer: UIRotationGestureRecognizer) in
197 | print("Gesture \(recognizer) triggered")
198 | }
199 | ````
200 |
201 |
202 | ### UIBarButtonItem [☝️](#usage)
203 |
204 | Create `UIBarButtonItem` with a closure instead of a pair of target/action. You can create bar button items from its title, image or using a system type:
205 |
206 | ````swift
207 | let imageTitle = UIBarButtonItem(image: UIImage(named: "image")!) {
208 | print("image item pressed")
209 | }
210 |
211 | let titleItem = UIBarButtonItem(title: "Title") {
212 | print("title item pressed")
213 | }
214 |
215 | let systemItem = UIBarButtonItem(barButtonSystemItem: .action) {
216 | print("system item pressed")
217 | }
218 | ````
219 |
220 | All these methods has some additional, optional arguments. They also can be used with closures that takes the `UIBarButtonItem` as an argument, for instance:
221 |
222 | ````swift
223 | let imageTitle = UIBarButtonItem(image: UIImage(named: "image")!) { (item: UIBarButtonItem) in
224 | print("image item \(item) pressed")
225 | }
226 | ````
227 |
228 |
229 | ### Timer [☝️](#usage)
230 |
231 | Create a `Timer` with a closure instead of a pair of target/action. You can create timers in three different ways:
232 |
233 | ````swift
234 | // Scheduele a timer
235 | Timer.scheduledTimer(timeInterval: 5) {
236 | print("timer fired")
237 | }
238 |
239 | // create a timer with a fire date
240 | let timer = Timer(fire: date, interval: 0.5, repeats: true) {
241 | print("timer fired")
242 | }
243 |
244 | // create a timer with a time interval
245 | let timer = Timer(timeInterval: 0.5) {
246 | print("timer fired")
247 | }
248 | ````
249 |
250 |
251 | All these methods has some additional, optional arguments as `repeats` and `userInfo`. They also can be used with closures that takes the `Timer` as an argument, for example:
252 |
253 | ````swift
254 | let timer = Timer(fire: date, interval: 0.5, repeats: true) { (timer: Timer) in
255 | print("timer fired \(timer)")
256 | }
257 | ````
258 |
259 |
260 | ### NotificationCenter [☝️](#usage)
261 |
262 | Add an observer to a `NotificationCenter` with a closure instead of a pair of observer/selector. You can do it in two ways, observations that would live until they are stopped manually or notifications that are bind to the lifetime of an object:
263 |
264 | ````swift
265 | let center = NotificationCenter.default
266 |
267 | // This observation will live forever until it is stopped manually
268 | let action = center.observe(notificationName) {
269 | print("Notification received")
270 | }
271 |
272 | // Stop observing the notification
273 | center.stopObserving(action: action)
274 | ````
275 |
276 | ````swift
277 | // This observation will live until the observer is deallocated
278 | center.add(observer: self, name: notificationName) { [unowned self] in
279 | print("observe notification from \(self)")
280 | }
281 |
282 | // It can be stopped manually by doing:
283 | center.stopObserver(self) // name and object are optional paramteres for this method
284 | ````
285 |
286 | These methods has some additional, optional arguments `object`: the object whose notifications the observer wants to receive.
287 |
288 |
289 | ---
290 |
291 |
292 | ## Contact
293 |
294 | [Manuel García-Estañ Martínez](http://github.com/ManueGE)
295 | [@manueGE](https://twitter.com/ManueGE)
296 |
297 | ## License
298 |
299 | Actions is available under the [MIT license](LICENSE.md).
300 |
--------------------------------------------------------------------------------
/actions.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 32281F031DC8FEAC004BF074 /* UIGestureRecognizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32281F021DC8FEAC004BF074 /* UIGestureRecognizerViewController.swift */; };
11 | 3252023E1CFEDFC400CB20CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3252023D1CFEDFC400CB20CF /* AppDelegate.swift */; };
12 | 325202431CFEDFC400CB20CF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 325202411CFEDFC400CB20CF /* Main.storyboard */; };
13 | 325202451CFEDFC400CB20CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 325202441CFEDFC400CB20CF /* Assets.xcassets */; };
14 | 325202481CFEDFC400CB20CF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 325202461CFEDFC400CB20CF /* LaunchScreen.storyboard */; };
15 | 3253D6CA1DB90313006A94C2 /* actions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3253D6C81DB90312006A94C2 /* actions.h */; settings = {ATTRIBUTES = (Public, ); }; };
16 | 3253D6CD1DB90313006A94C2 /* Actions.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3253D6C61DB90312006A94C2 /* Actions.framework */; };
17 | 3253D6CE1DB90313006A94C2 /* Actions.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3253D6C61DB90312006A94C2 /* Actions.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
18 | 3253D6DE1DB90342006A94C2 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3253D6D41DB90342006A94C2 /* Action.swift */; };
19 | 3253D6E01DB90342006A94C2 /* NotificationCenter+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3253D6D51DB90342006A94C2 /* NotificationCenter+Actions.swift */; };
20 | 3253D6E21DB90342006A94C2 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3253D6D61DB90342006A94C2 /* Throttle.swift */; };
21 | 3253D6E41DB90342006A94C2 /* Timer+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3253D6D71DB90342006A94C2 /* Timer+Actions.swift */; };
22 | 3253D6E61DB90342006A94C2 /* UIBarButtonItem+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3253D6D81DB90342006A94C2 /* UIBarButtonItem+Actions.swift */; };
23 | 3253D6E81DB90342006A94C2 /* UIControl+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3253D6D91DB90342006A94C2 /* UIControl+Actions.swift */; };
24 | 3253D6EA1DB90342006A94C2 /* UIControl+Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3253D6DA1DB90342006A94C2 /* UIControl+Throttle.swift */; };
25 | 3253D6EC1DB90342006A94C2 /* UIGestureRecognizer+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3253D6DB1DB90342006A94C2 /* UIGestureRecognizer+Actions.swift */; };
26 | 3253D6EE1DB90342006A94C2 /* UIView+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3253D6DC1DB90342006A94C2 /* UIView+Actions.swift */; };
27 | 328C89D51DC8A7A2005AD08D /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328C89D41DC8A7A2005AD08D /* Data.swift */; };
28 | 328C89D71DC8A94D005AD08D /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328C89D61DC8A94D005AD08D /* BaseViewController.swift */; };
29 | 328C89DB1DC8A98F005AD08D /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328C89DA1DC8A98F005AD08D /* HomeViewController.swift */; };
30 | 328C89DD1DC8AC24005AD08D /* UIViewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328C89DC1DC8AC24005AD08D /* UIViewViewController.swift */; };
31 | 328C89DF1DC8B065005AD08D /* UIBarbuttonItemViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328C89DE1DC8B065005AD08D /* UIBarbuttonItemViewController.swift */; };
32 | 328C89E11DC8B1ED005AD08D /* TimerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328C89E01DC8B1ED005AD08D /* TimerViewController.swift */; };
33 | 328C89E31DC8B333005AD08D /* NotificationCenterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328C89E21DC8B333005AD08D /* NotificationCenterViewController.swift */; };
34 | 328C89E51DC8BB34005AD08D /* UIControlViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328C89E41DC8BB34005AD08D /* UIControlViewController.swift */; };
35 | /* End PBXBuildFile section */
36 |
37 | /* Begin PBXContainerItemProxy section */
38 | 3253D6CB1DB90313006A94C2 /* PBXContainerItemProxy */ = {
39 | isa = PBXContainerItemProxy;
40 | containerPortal = 325202321CFEDFC400CB20CF /* Project object */;
41 | proxyType = 1;
42 | remoteGlobalIDString = 3253D6C51DB90312006A94C2;
43 | remoteInfo = actions;
44 | };
45 | /* End PBXContainerItemProxy section */
46 |
47 | /* Begin PBXCopyFilesBuildPhase section */
48 | 3253D6D21DB90313006A94C2 /* Embed Frameworks */ = {
49 | isa = PBXCopyFilesBuildPhase;
50 | buildActionMask = 2147483647;
51 | dstPath = "";
52 | dstSubfolderSpec = 10;
53 | files = (
54 | 3253D6CE1DB90313006A94C2 /* Actions.framework in Embed Frameworks */,
55 | );
56 | name = "Embed Frameworks";
57 | runOnlyForDeploymentPostprocessing = 0;
58 | };
59 | /* End PBXCopyFilesBuildPhase section */
60 |
61 | /* Begin PBXFileReference section */
62 | 32281F021DC8FEAC004BF074 /* UIGestureRecognizerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizerViewController.swift; sourceTree = ""; };
63 | 3252023A1CFEDFC400CB20CF /* actions_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = actions_app.app; sourceTree = BUILT_PRODUCTS_DIR; };
64 | 3252023D1CFEDFC400CB20CF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
65 | 325202421CFEDFC400CB20CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
66 | 325202441CFEDFC400CB20CF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
67 | 325202471CFEDFC400CB20CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
68 | 325202491CFEDFC400CB20CF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
69 | 3253D6C61DB90312006A94C2 /* Actions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Actions.framework; sourceTree = BUILT_PRODUCTS_DIR; };
70 | 3253D6C81DB90312006A94C2 /* actions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = actions.h; sourceTree = ""; };
71 | 3253D6C91DB90312006A94C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
72 | 3253D6D41DB90342006A94C2 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; };
73 | 3253D6D51DB90342006A94C2 /* NotificationCenter+Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NotificationCenter+Actions.swift"; sourceTree = ""; };
74 | 3253D6D61DB90342006A94C2 /* Throttle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Throttle.swift; sourceTree = ""; };
75 | 3253D6D71DB90342006A94C2 /* Timer+Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Timer+Actions.swift"; sourceTree = ""; };
76 | 3253D6D81DB90342006A94C2 /* UIBarButtonItem+Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Actions.swift"; sourceTree = ""; };
77 | 3253D6D91DB90342006A94C2 /* UIControl+Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+Actions.swift"; sourceTree = ""; };
78 | 3253D6DA1DB90342006A94C2 /* UIControl+Throttle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+Throttle.swift"; sourceTree = ""; };
79 | 3253D6DB1DB90342006A94C2 /* UIGestureRecognizer+Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Actions.swift"; sourceTree = ""; };
80 | 3253D6DC1DB90342006A94C2 /* UIView+Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Actions.swift"; sourceTree = ""; };
81 | 328C89D41DC8A7A2005AD08D /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; };
82 | 328C89D61DC8A94D005AD08D /* BaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; };
83 | 328C89DA1DC8A98F005AD08D /* HomeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; };
84 | 328C89DC1DC8AC24005AD08D /* UIViewViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewViewController.swift; sourceTree = ""; };
85 | 328C89DE1DC8B065005AD08D /* UIBarbuttonItemViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarbuttonItemViewController.swift; sourceTree = ""; };
86 | 328C89E01DC8B1ED005AD08D /* TimerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimerViewController.swift; sourceTree = ""; };
87 | 328C89E21DC8B333005AD08D /* NotificationCenterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationCenterViewController.swift; sourceTree = ""; };
88 | 328C89E41DC8BB34005AD08D /* UIControlViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlViewController.swift; sourceTree = ""; };
89 | /* End PBXFileReference section */
90 |
91 | /* Begin PBXFrameworksBuildPhase section */
92 | 325202371CFEDFC400CB20CF /* Frameworks */ = {
93 | isa = PBXFrameworksBuildPhase;
94 | buildActionMask = 2147483647;
95 | files = (
96 | 3253D6CD1DB90313006A94C2 /* Actions.framework in Frameworks */,
97 | );
98 | runOnlyForDeploymentPostprocessing = 0;
99 | };
100 | 3253D6C21DB90312006A94C2 /* Frameworks */ = {
101 | isa = PBXFrameworksBuildPhase;
102 | buildActionMask = 2147483647;
103 | files = (
104 | );
105 | runOnlyForDeploymentPostprocessing = 0;
106 | };
107 | /* End PBXFrameworksBuildPhase section */
108 |
109 | /* Begin PBXGroup section */
110 | 325202311CFEDFC400CB20CF = {
111 | isa = PBXGroup;
112 | children = (
113 | 3252023C1CFEDFC400CB20CF /* actions_app */,
114 | 3253D6C71DB90312006A94C2 /* actions */,
115 | 3252023B1CFEDFC400CB20CF /* Products */,
116 | );
117 | sourceTree = "";
118 | };
119 | 3252023B1CFEDFC400CB20CF /* Products */ = {
120 | isa = PBXGroup;
121 | children = (
122 | 3252023A1CFEDFC400CB20CF /* actions_app.app */,
123 | 3253D6C61DB90312006A94C2 /* Actions.framework */,
124 | );
125 | name = Products;
126 | sourceTree = "";
127 | };
128 | 3252023C1CFEDFC400CB20CF /* actions_app */ = {
129 | isa = PBXGroup;
130 | children = (
131 | 3252023D1CFEDFC400CB20CF /* AppDelegate.swift */,
132 | 328C89D21DC8A783005AD08D /* data */,
133 | 328C89D31DC8A783005AD08D /* scenes */,
134 | 325202411CFEDFC400CB20CF /* Main.storyboard */,
135 | 325202441CFEDFC400CB20CF /* Assets.xcassets */,
136 | 325202461CFEDFC400CB20CF /* LaunchScreen.storyboard */,
137 | 325202491CFEDFC400CB20CF /* Info.plist */,
138 | );
139 | path = actions_app;
140 | sourceTree = "";
141 | };
142 | 3253D6C71DB90312006A94C2 /* actions */ = {
143 | isa = PBXGroup;
144 | children = (
145 | 3253D6D31DB90342006A94C2 /* actions */,
146 | 3253D6C81DB90312006A94C2 /* actions.h */,
147 | 3253D6C91DB90312006A94C2 /* Info.plist */,
148 | );
149 | path = actions;
150 | sourceTree = "";
151 | };
152 | 3253D6D31DB90342006A94C2 /* actions */ = {
153 | isa = PBXGroup;
154 | children = (
155 | 3253D6D41DB90342006A94C2 /* Action.swift */,
156 | 3253D6D51DB90342006A94C2 /* NotificationCenter+Actions.swift */,
157 | 3253D6D61DB90342006A94C2 /* Throttle.swift */,
158 | 3253D6D71DB90342006A94C2 /* Timer+Actions.swift */,
159 | 3253D6D81DB90342006A94C2 /* UIBarButtonItem+Actions.swift */,
160 | 3253D6D91DB90342006A94C2 /* UIControl+Actions.swift */,
161 | 3253D6DA1DB90342006A94C2 /* UIControl+Throttle.swift */,
162 | 3253D6DB1DB90342006A94C2 /* UIGestureRecognizer+Actions.swift */,
163 | 3253D6DC1DB90342006A94C2 /* UIView+Actions.swift */,
164 | );
165 | path = actions;
166 | sourceTree = "";
167 | };
168 | 328C89D21DC8A783005AD08D /* data */ = {
169 | isa = PBXGroup;
170 | children = (
171 | 328C89D41DC8A7A2005AD08D /* Data.swift */,
172 | );
173 | path = data;
174 | sourceTree = "";
175 | };
176 | 328C89D31DC8A783005AD08D /* scenes */ = {
177 | isa = PBXGroup;
178 | children = (
179 | 328C89DA1DC8A98F005AD08D /* HomeViewController.swift */,
180 | 328C89D61DC8A94D005AD08D /* BaseViewController.swift */,
181 | 328C89DC1DC8AC24005AD08D /* UIViewViewController.swift */,
182 | 328C89DE1DC8B065005AD08D /* UIBarbuttonItemViewController.swift */,
183 | 328C89E01DC8B1ED005AD08D /* TimerViewController.swift */,
184 | 328C89E21DC8B333005AD08D /* NotificationCenterViewController.swift */,
185 | 328C89E41DC8BB34005AD08D /* UIControlViewController.swift */,
186 | 32281F021DC8FEAC004BF074 /* UIGestureRecognizerViewController.swift */,
187 | );
188 | path = scenes;
189 | sourceTree = "";
190 | };
191 | /* End PBXGroup section */
192 |
193 | /* Begin PBXHeadersBuildPhase section */
194 | 3253D6C31DB90312006A94C2 /* Headers */ = {
195 | isa = PBXHeadersBuildPhase;
196 | buildActionMask = 2147483647;
197 | files = (
198 | 3253D6CA1DB90313006A94C2 /* actions.h in Headers */,
199 | );
200 | runOnlyForDeploymentPostprocessing = 0;
201 | };
202 | /* End PBXHeadersBuildPhase section */
203 |
204 | /* Begin PBXNativeTarget section */
205 | 325202391CFEDFC400CB20CF /* actions_app */ = {
206 | isa = PBXNativeTarget;
207 | buildConfigurationList = 3252024C1CFEDFC400CB20CF /* Build configuration list for PBXNativeTarget "actions_app" */;
208 | buildPhases = (
209 | 325202361CFEDFC400CB20CF /* Sources */,
210 | 325202371CFEDFC400CB20CF /* Frameworks */,
211 | 325202381CFEDFC400CB20CF /* Resources */,
212 | 3253D6D21DB90313006A94C2 /* Embed Frameworks */,
213 | );
214 | buildRules = (
215 | );
216 | dependencies = (
217 | 3253D6CC1DB90313006A94C2 /* PBXTargetDependency */,
218 | );
219 | name = actions_app;
220 | productName = actions;
221 | productReference = 3252023A1CFEDFC400CB20CF /* actions_app.app */;
222 | productType = "com.apple.product-type.application";
223 | };
224 | 3253D6C51DB90312006A94C2 /* Actions */ = {
225 | isa = PBXNativeTarget;
226 | buildConfigurationList = 3253D6CF1DB90313006A94C2 /* Build configuration list for PBXNativeTarget "Actions" */;
227 | buildPhases = (
228 | 3253D6C11DB90312006A94C2 /* Sources */,
229 | 3253D6C21DB90312006A94C2 /* Frameworks */,
230 | 3253D6C31DB90312006A94C2 /* Headers */,
231 | 3253D6C41DB90312006A94C2 /* Resources */,
232 | );
233 | buildRules = (
234 | );
235 | dependencies = (
236 | );
237 | name = Actions;
238 | productName = actions;
239 | productReference = 3253D6C61DB90312006A94C2 /* Actions.framework */;
240 | productType = "com.apple.product-type.framework";
241 | };
242 | /* End PBXNativeTarget section */
243 |
244 | /* Begin PBXProject section */
245 | 325202321CFEDFC400CB20CF /* Project object */ = {
246 | isa = PBXProject;
247 | attributes = {
248 | LastSwiftUpdateCheck = 0730;
249 | LastUpgradeCheck = 1000;
250 | ORGANIZATIONNAME = manuege;
251 | TargetAttributes = {
252 | 325202391CFEDFC400CB20CF = {
253 | CreatedOnToolsVersion = 7.3.1;
254 | DevelopmentTeam = CF864KN2XE;
255 | LastSwiftMigration = 0800;
256 | };
257 | 3253D6C51DB90312006A94C2 = {
258 | CreatedOnToolsVersion = 8.0;
259 | LastSwiftMigration = 1000;
260 | ProvisioningStyle = Automatic;
261 | };
262 | };
263 | };
264 | buildConfigurationList = 325202351CFEDFC400CB20CF /* Build configuration list for PBXProject "actions" */;
265 | compatibilityVersion = "Xcode 3.2";
266 | developmentRegion = English;
267 | hasScannedForEncodings = 0;
268 | knownRegions = (
269 | en,
270 | Base,
271 | );
272 | mainGroup = 325202311CFEDFC400CB20CF;
273 | productRefGroup = 3252023B1CFEDFC400CB20CF /* Products */;
274 | projectDirPath = "";
275 | projectRoot = "";
276 | targets = (
277 | 325202391CFEDFC400CB20CF /* actions_app */,
278 | 3253D6C51DB90312006A94C2 /* Actions */,
279 | );
280 | };
281 | /* End PBXProject section */
282 |
283 | /* Begin PBXResourcesBuildPhase section */
284 | 325202381CFEDFC400CB20CF /* Resources */ = {
285 | isa = PBXResourcesBuildPhase;
286 | buildActionMask = 2147483647;
287 | files = (
288 | 325202481CFEDFC400CB20CF /* LaunchScreen.storyboard in Resources */,
289 | 325202451CFEDFC400CB20CF /* Assets.xcassets in Resources */,
290 | 325202431CFEDFC400CB20CF /* Main.storyboard in Resources */,
291 | );
292 | runOnlyForDeploymentPostprocessing = 0;
293 | };
294 | 3253D6C41DB90312006A94C2 /* Resources */ = {
295 | isa = PBXResourcesBuildPhase;
296 | buildActionMask = 2147483647;
297 | files = (
298 | );
299 | runOnlyForDeploymentPostprocessing = 0;
300 | };
301 | /* End PBXResourcesBuildPhase section */
302 |
303 | /* Begin PBXSourcesBuildPhase section */
304 | 325202361CFEDFC400CB20CF /* Sources */ = {
305 | isa = PBXSourcesBuildPhase;
306 | buildActionMask = 2147483647;
307 | files = (
308 | 328C89D71DC8A94D005AD08D /* BaseViewController.swift in Sources */,
309 | 328C89E51DC8BB34005AD08D /* UIControlViewController.swift in Sources */,
310 | 328C89E11DC8B1ED005AD08D /* TimerViewController.swift in Sources */,
311 | 328C89DD1DC8AC24005AD08D /* UIViewViewController.swift in Sources */,
312 | 328C89DF1DC8B065005AD08D /* UIBarbuttonItemViewController.swift in Sources */,
313 | 3252023E1CFEDFC400CB20CF /* AppDelegate.swift in Sources */,
314 | 328C89D51DC8A7A2005AD08D /* Data.swift in Sources */,
315 | 328C89E31DC8B333005AD08D /* NotificationCenterViewController.swift in Sources */,
316 | 32281F031DC8FEAC004BF074 /* UIGestureRecognizerViewController.swift in Sources */,
317 | 328C89DB1DC8A98F005AD08D /* HomeViewController.swift in Sources */,
318 | );
319 | runOnlyForDeploymentPostprocessing = 0;
320 | };
321 | 3253D6C11DB90312006A94C2 /* Sources */ = {
322 | isa = PBXSourcesBuildPhase;
323 | buildActionMask = 2147483647;
324 | files = (
325 | 3253D6E01DB90342006A94C2 /* NotificationCenter+Actions.swift in Sources */,
326 | 3253D6E61DB90342006A94C2 /* UIBarButtonItem+Actions.swift in Sources */,
327 | 3253D6E21DB90342006A94C2 /* Throttle.swift in Sources */,
328 | 3253D6EA1DB90342006A94C2 /* UIControl+Throttle.swift in Sources */,
329 | 3253D6E81DB90342006A94C2 /* UIControl+Actions.swift in Sources */,
330 | 3253D6DE1DB90342006A94C2 /* Action.swift in Sources */,
331 | 3253D6E41DB90342006A94C2 /* Timer+Actions.swift in Sources */,
332 | 3253D6EC1DB90342006A94C2 /* UIGestureRecognizer+Actions.swift in Sources */,
333 | 3253D6EE1DB90342006A94C2 /* UIView+Actions.swift in Sources */,
334 | );
335 | runOnlyForDeploymentPostprocessing = 0;
336 | };
337 | /* End PBXSourcesBuildPhase section */
338 |
339 | /* Begin PBXTargetDependency section */
340 | 3253D6CC1DB90313006A94C2 /* PBXTargetDependency */ = {
341 | isa = PBXTargetDependency;
342 | target = 3253D6C51DB90312006A94C2 /* Actions */;
343 | targetProxy = 3253D6CB1DB90313006A94C2 /* PBXContainerItemProxy */;
344 | };
345 | /* End PBXTargetDependency section */
346 |
347 | /* Begin PBXVariantGroup section */
348 | 325202411CFEDFC400CB20CF /* Main.storyboard */ = {
349 | isa = PBXVariantGroup;
350 | children = (
351 | 325202421CFEDFC400CB20CF /* Base */,
352 | );
353 | name = Main.storyboard;
354 | sourceTree = "";
355 | };
356 | 325202461CFEDFC400CB20CF /* LaunchScreen.storyboard */ = {
357 | isa = PBXVariantGroup;
358 | children = (
359 | 325202471CFEDFC400CB20CF /* Base */,
360 | );
361 | name = LaunchScreen.storyboard;
362 | sourceTree = "";
363 | };
364 | /* End PBXVariantGroup section */
365 |
366 | /* Begin XCBuildConfiguration section */
367 | 3252024A1CFEDFC400CB20CF /* Debug */ = {
368 | isa = XCBuildConfiguration;
369 | buildSettings = {
370 | ALWAYS_SEARCH_USER_PATHS = NO;
371 | CLANG_ANALYZER_NONNULL = YES;
372 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
373 | CLANG_CXX_LIBRARY = "libc++";
374 | CLANG_ENABLE_MODULES = YES;
375 | CLANG_ENABLE_OBJC_ARC = YES;
376 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
377 | CLANG_WARN_BOOL_CONVERSION = YES;
378 | CLANG_WARN_COMMA = YES;
379 | CLANG_WARN_CONSTANT_CONVERSION = YES;
380 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
381 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
382 | CLANG_WARN_EMPTY_BODY = YES;
383 | CLANG_WARN_ENUM_CONVERSION = YES;
384 | CLANG_WARN_INFINITE_RECURSION = YES;
385 | CLANG_WARN_INT_CONVERSION = YES;
386 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
387 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
388 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
389 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
390 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
391 | CLANG_WARN_STRICT_PROTOTYPES = YES;
392 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
393 | CLANG_WARN_UNREACHABLE_CODE = YES;
394 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
395 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
396 | COPY_PHASE_STRIP = NO;
397 | DEBUG_INFORMATION_FORMAT = dwarf;
398 | ENABLE_STRICT_OBJC_MSGSEND = YES;
399 | ENABLE_TESTABILITY = YES;
400 | GCC_C_LANGUAGE_STANDARD = gnu99;
401 | GCC_DYNAMIC_NO_PIC = NO;
402 | GCC_NO_COMMON_BLOCKS = YES;
403 | GCC_OPTIMIZATION_LEVEL = 0;
404 | GCC_PREPROCESSOR_DEFINITIONS = (
405 | "DEBUG=1",
406 | "$(inherited)",
407 | );
408 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
409 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
410 | GCC_WARN_UNDECLARED_SELECTOR = YES;
411 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
412 | GCC_WARN_UNUSED_FUNCTION = YES;
413 | GCC_WARN_UNUSED_VARIABLE = YES;
414 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
415 | MTL_ENABLE_DEBUG_INFO = YES;
416 | ONLY_ACTIVE_ARCH = YES;
417 | SDKROOT = iphoneos;
418 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
419 | SWIFT_VERSION = 3.0;
420 | };
421 | name = Debug;
422 | };
423 | 3252024B1CFEDFC400CB20CF /* Release */ = {
424 | isa = XCBuildConfiguration;
425 | buildSettings = {
426 | ALWAYS_SEARCH_USER_PATHS = NO;
427 | CLANG_ANALYZER_NONNULL = YES;
428 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
429 | CLANG_CXX_LIBRARY = "libc++";
430 | CLANG_ENABLE_MODULES = YES;
431 | CLANG_ENABLE_OBJC_ARC = YES;
432 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
433 | CLANG_WARN_BOOL_CONVERSION = YES;
434 | CLANG_WARN_COMMA = YES;
435 | CLANG_WARN_CONSTANT_CONVERSION = YES;
436 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
437 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
438 | CLANG_WARN_EMPTY_BODY = YES;
439 | CLANG_WARN_ENUM_CONVERSION = YES;
440 | CLANG_WARN_INFINITE_RECURSION = YES;
441 | CLANG_WARN_INT_CONVERSION = YES;
442 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
443 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
444 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
445 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
446 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
447 | CLANG_WARN_STRICT_PROTOTYPES = YES;
448 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
449 | CLANG_WARN_UNREACHABLE_CODE = YES;
450 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
451 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
452 | COPY_PHASE_STRIP = NO;
453 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
454 | ENABLE_NS_ASSERTIONS = NO;
455 | ENABLE_STRICT_OBJC_MSGSEND = YES;
456 | GCC_C_LANGUAGE_STANDARD = gnu99;
457 | GCC_NO_COMMON_BLOCKS = YES;
458 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
459 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
460 | GCC_WARN_UNDECLARED_SELECTOR = YES;
461 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
462 | GCC_WARN_UNUSED_FUNCTION = YES;
463 | GCC_WARN_UNUSED_VARIABLE = YES;
464 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
465 | MTL_ENABLE_DEBUG_INFO = NO;
466 | SDKROOT = iphoneos;
467 | SWIFT_VERSION = 3.0;
468 | VALIDATE_PRODUCT = YES;
469 | };
470 | name = Release;
471 | };
472 | 3252024D1CFEDFC400CB20CF /* Debug */ = {
473 | isa = XCBuildConfiguration;
474 | buildSettings = {
475 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
476 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
477 | DEVELOPMENT_TEAM = CF864KN2XE;
478 | INFOPLIST_FILE = "$(SRCROOT)/actions_app/Info.plist";
479 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
480 | PRODUCT_BUNDLE_IDENTIFIER = com.manuege.actionsapp;
481 | PRODUCT_NAME = "$(TARGET_NAME)";
482 | SWIFT_VERSION = 3.0;
483 | };
484 | name = Debug;
485 | };
486 | 3252024E1CFEDFC400CB20CF /* Release */ = {
487 | isa = XCBuildConfiguration;
488 | buildSettings = {
489 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
490 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
491 | DEVELOPMENT_TEAM = CF864KN2XE;
492 | INFOPLIST_FILE = "$(SRCROOT)/actions_app/Info.plist";
493 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
494 | PRODUCT_BUNDLE_IDENTIFIER = com.manuege.actionsapp;
495 | PRODUCT_NAME = "$(TARGET_NAME)";
496 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
497 | SWIFT_VERSION = 3.0;
498 | };
499 | name = Release;
500 | };
501 | 3253D6D01DB90313006A94C2 /* Debug */ = {
502 | isa = XCBuildConfiguration;
503 | buildSettings = {
504 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
505 | CLANG_WARN_INFINITE_RECURSION = YES;
506 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
507 | CODE_SIGN_IDENTITY = "";
508 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
509 | CURRENT_PROJECT_VERSION = 1;
510 | DEFINES_MODULE = YES;
511 | DEVELOPMENT_TEAM = "";
512 | DYLIB_COMPATIBILITY_VERSION = 1;
513 | DYLIB_CURRENT_VERSION = 1;
514 | DYLIB_INSTALL_NAME_BASE = "@rpath";
515 | INFOPLIST_FILE = actions/Info.plist;
516 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
517 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
518 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
519 | PRODUCT_BUNDLE_IDENTIFIER = com.manuege.actions;
520 | PRODUCT_NAME = "$(TARGET_NAME)";
521 | SKIP_INSTALL = YES;
522 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
523 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
524 | SWIFT_VERSION = 4.2;
525 | TARGETED_DEVICE_FAMILY = "1,2";
526 | VERSIONING_SYSTEM = "apple-generic";
527 | VERSION_INFO_PREFIX = "";
528 | };
529 | name = Debug;
530 | };
531 | 3253D6D11DB90313006A94C2 /* Release */ = {
532 | isa = XCBuildConfiguration;
533 | buildSettings = {
534 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
535 | CLANG_WARN_INFINITE_RECURSION = YES;
536 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
537 | CODE_SIGN_IDENTITY = "";
538 | CURRENT_PROJECT_VERSION = 1;
539 | DEFINES_MODULE = YES;
540 | DEVELOPMENT_TEAM = "";
541 | DYLIB_COMPATIBILITY_VERSION = 1;
542 | DYLIB_CURRENT_VERSION = 1;
543 | DYLIB_INSTALL_NAME_BASE = "@rpath";
544 | INFOPLIST_FILE = actions/Info.plist;
545 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
546 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
547 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
548 | PRODUCT_BUNDLE_IDENTIFIER = com.manuege.actions;
549 | PRODUCT_NAME = "$(TARGET_NAME)";
550 | SKIP_INSTALL = YES;
551 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
552 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
553 | SWIFT_VERSION = 4.2;
554 | TARGETED_DEVICE_FAMILY = "1,2";
555 | VERSIONING_SYSTEM = "apple-generic";
556 | VERSION_INFO_PREFIX = "";
557 | };
558 | name = Release;
559 | };
560 | /* End XCBuildConfiguration section */
561 |
562 | /* Begin XCConfigurationList section */
563 | 325202351CFEDFC400CB20CF /* Build configuration list for PBXProject "actions" */ = {
564 | isa = XCConfigurationList;
565 | buildConfigurations = (
566 | 3252024A1CFEDFC400CB20CF /* Debug */,
567 | 3252024B1CFEDFC400CB20CF /* Release */,
568 | );
569 | defaultConfigurationIsVisible = 0;
570 | defaultConfigurationName = Release;
571 | };
572 | 3252024C1CFEDFC400CB20CF /* Build configuration list for PBXNativeTarget "actions_app" */ = {
573 | isa = XCConfigurationList;
574 | buildConfigurations = (
575 | 3252024D1CFEDFC400CB20CF /* Debug */,
576 | 3252024E1CFEDFC400CB20CF /* Release */,
577 | );
578 | defaultConfigurationIsVisible = 0;
579 | defaultConfigurationName = Release;
580 | };
581 | 3253D6CF1DB90313006A94C2 /* Build configuration list for PBXNativeTarget "Actions" */ = {
582 | isa = XCConfigurationList;
583 | buildConfigurations = (
584 | 3253D6D01DB90313006A94C2 /* Debug */,
585 | 3253D6D11DB90313006A94C2 /* Release */,
586 | );
587 | defaultConfigurationIsVisible = 0;
588 | defaultConfigurationName = Release;
589 | };
590 | /* End XCConfigurationList section */
591 | };
592 | rootObject = 325202321CFEDFC400CB20CF /* Project object */;
593 | }
594 |
--------------------------------------------------------------------------------
/actions.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/actions.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/actions.xcodeproj/xcshareddata/xcschemes/actions.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/actions.xcodeproj/xcshareddata/xcschemes/actions_app.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/actions.xcodeproj/xcuserdata/Manu.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | actions.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 | actions_app.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 |
18 | SuppressBuildableAutocreation
19 |
20 | 325202391CFEDFC400CB20CF
21 |
22 | primary
23 |
24 |
25 | 3253D6C51DB90312006A94C2
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/actions/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/actions/actions.h:
--------------------------------------------------------------------------------
1 | //
2 | // actions.h
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 20/10/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for actions.
12 | FOUNDATION_EXPORT double actionsVersionNumber;
13 |
14 | //! Project version string for actions.
15 | FOUNDATION_EXPORT const unsigned char actionsVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/actions/actions/Action.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Action.swift
3 | // actions
4 | //
5 | // Created by Manu on 1/6/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ObjectiveC
11 |
12 | /**
13 | Protocol used to convert Swift closures into ObjC selectors
14 | */
15 | @objc public protocol Action {
16 | // The key used to store the `Action`. Must be unique.
17 | var key: String { get }
18 |
19 | // The selector provided by the action
20 | var selector: Selector { get }
21 | }
22 |
23 | // Action that takes zero parameters
24 | class VoidAction: Action {
25 |
26 | @objc let key = ProcessInfo.processInfo.globallyUniqueString
27 | @objc let selector: Selector = #selector(perform)
28 |
29 | var action: (() -> Void)!
30 |
31 | init(action: @escaping () -> Void) {
32 | self.action = action
33 | }
34 |
35 | @objc func perform() {
36 | action()
37 | }
38 | }
39 |
40 | // Action which takes one single parameter
41 | class ParametizedAction: Action {
42 |
43 | @objc let key = ProcessInfo.processInfo.globallyUniqueString
44 | @objc let selector: Selector = #selector(perform)
45 |
46 | let action: ((T) -> Void)!
47 |
48 | init(action: @escaping (T) -> Void) {
49 | self.action = action
50 | }
51 |
52 | @objc func perform(parameter: AnyObject) {
53 | action(parameter as! T)
54 | }
55 | }
56 |
57 |
58 | // MARK: Actionable
59 | /*!
60 | Actionable is a protocol used to store `Action` instances. Its only purpose is avoid them to be deallocated.
61 | */
62 | protocol Actionable: class {
63 | var actions: [String: Action]! { get }
64 | }
65 |
66 | private var actionsKey: UInt8 = 0
67 | extension Actionable {
68 | fileprivate(set) var actions: [String: Action]! {
69 | get {
70 | var actions = objc_getAssociatedObject(self, &actionsKey) as? [String: Action]
71 |
72 | if actions == nil {
73 | actions = [:]
74 | self.actions = actions
75 | }
76 |
77 | return actions
78 | }
79 | set(newValue) {
80 | objc_setAssociatedObject(self, &actionsKey, newValue, .OBJC_ASSOCIATION_RETAIN)
81 | }
82 | }
83 | }
84 |
85 | func retainAction(_ action: Action, _ object: NSObject) {
86 | object.actions[action.key] = action
87 | }
88 |
89 | func releaseAction(_ action: Action, _ object: NSObject) {
90 | object.actions[action.key] = nil
91 | }
92 |
93 | extension NSObject: Actionable {}
94 |
--------------------------------------------------------------------------------
/actions/actions/NotificationCenter+Actions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NotificationCenter+Actions.swift
3 | // actions
4 | //
5 | // Created by Manu on 6/7/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | private protocol NotificationCenterAction: Action {
12 | var notificationName: NSNotification.Name? { get }
13 | var notificationObject: AnyObject? { get }
14 | }
15 |
16 | private class NotificationCenterVoidAction: VoidAction, NotificationCenterAction {
17 | fileprivate let notificationName: NSNotification.Name?
18 | fileprivate let notificationObject: AnyObject?
19 | fileprivate init(name: NSNotification.Name?, object: AnyObject?, action: @escaping () -> Void) {
20 | self.notificationName = name
21 | self.notificationObject = object
22 | super.init(action: action)
23 | }
24 | }
25 |
26 | private class NotificationCenterParametizedAction: ParametizedAction, NotificationCenterAction {
27 | fileprivate let notificationName: NSNotification.Name?
28 | fileprivate let notificationObject: AnyObject?
29 | fileprivate init(name: NSNotification.Name?, object: AnyObject?, action: @escaping (NSNotification) -> Void) {
30 | self.notificationName = name
31 | self.notificationObject = object;
32 | super.init(action: action)
33 | }
34 | }
35 |
36 | /// Observe notifications with closures instead of a pair of observer/selector
37 | extension NotificationCenter {
38 |
39 | // MARK: Add observers
40 |
41 | /**
42 | Adds an entry to the receiver’s dispatch table with a closure and optional criteria: notification name and sender.
43 | The observation lives until it is manually stopped,, so be sure to invoke `stopObserving(_)` when the observation is not longer needed
44 | - parameter name: The name of the notification for which to register the observer; that is, only notifications with this name are delivered to the observer.
45 | If you pass nil, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer.
46 | - parameter object: The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer. Default is `nil`.
47 | - parameter action: The closure which will be called when a notification with the criteria is sent
48 | - returns: The action that has been added to the receiver. You can catch this value to stop observing it by calling `stopObserving(_)`.
49 | */
50 | @discardableResult
51 | public func observe(_ name: NSNotification.Name?, object: AnyObject? = nil, action: @escaping () -> Void) -> Action {
52 | let action = NotificationCenterVoidAction(name: name, object: object, action: action)
53 | addObserver(action, selector: action.selector, name: name, object: object)
54 | retainAction(action, self)
55 | return action
56 | }
57 |
58 | /**
59 | Adds an entry to the receiver’s dispatch table with a closure and optional criteria: notification name and sender.
60 | The observation lives until it is manually stopped, so be sure to invoke `stopObserving(_)` when the observation is not longer needed
61 | - parameter name: The name of the notification for which to register the observer; that is, only notifications with this name are delivered to the observer.
62 | If you pass nil, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer.
63 | - parameter object: The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer. Default is `nil`.
64 | - parameter action: The closure which will be called when a notification with the criteria is sent
65 | - returns: The action that has been added to the receiver. You can catch this value to stop observing it by calling `stopObserving(_)`.
66 | */
67 | @discardableResult
68 | @nonobjc
69 | public func observe(_ name: NSNotification.Name?, object: AnyObject? = nil, action: @escaping (NSNotification) -> Void) -> Action {
70 | let action = NotificationCenterParametizedAction(name: name, object: object, action: action)
71 | addObserver(action, selector: action.selector, name: name, object: object)
72 | retainAction(action, self)
73 | return action
74 | }
75 |
76 | /**
77 | Adds an entry to the receiver’s dispatch table with a closure and optional criteria: notification name and sender.
78 | The observation lives while the `observer` is not deallocated. In case you need stop the observation before the òbserver` is deallocated, you can do it by invoking `stopObserving(_)`.
79 | - note: Due to internal implementation, the defaul method `removeObserver` not take any effect on obervations registered using this method.
80 | - parameter observer: Object registering as an observer. This value must not be nil.
81 | If you pass nil, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer.
82 | - parameter name: The name of the notification for which to register the observer; that is, only notifications with this name are delivered to the observer.
83 | If you pass nil, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer.
84 |
85 | - parameter object: The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer. Default is `nil`.
86 | - parameter action: The closure which will be called when a notification with the criteria is sent
87 | - returns: The action that has been added to the receiver. You can catch this value to stop observing it by calling `stopObserving(_)`.
88 | */
89 | @discardableResult
90 | public func add(observer: NSObject, name: NSNotification.Name?, object: AnyObject? = nil, action: @escaping () -> Void) -> Action {
91 | let action = NotificationCenterVoidAction(name: name, object: object, action: action)
92 | addObserver(action, selector: action.selector, name: name, object: object)
93 | retainAction(action, observer)
94 | return action
95 | }
96 |
97 | /**
98 | Adds an entry to the receiver’s dispatch table with a closure and optional criteria: notification name and sender.
99 | The observation lives while the `observer` is not deallocated. In case you need stop the observation before the òbserver` is deallocated, you can do it by invoking `stopObserving(_)`.
100 | - note: Due to internal implementation, the defaul method `removeObserver` not take any effect on obervations registered using this method.
101 | - parameter observer: Object registering as an observer. This value must not be nil.
102 | If you pass nil, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer.
103 | - parameter name: The name of the notification for which to register the observer; that is, only notifications with this name are delivered to the observer.
104 | If you pass nil, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer.
105 |
106 | - parameter object: The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer. Default is `nil`.
107 | - parameter action: The closure which will be called when a notification with the criteria is sent
108 | - returns: The action that has been added to the receiver. You can catch this value to stop observing it by calling `stopObserving(_)`.
109 | */
110 | @discardableResult
111 | @nonobjc
112 | public func add(observer: NSObject, name: NSNotification.Name?, object: AnyObject? = nil, action: @escaping (NSNotification) -> Void) -> Action {
113 | let action = NotificationCenterParametizedAction(name: name, object: object, action: action)
114 | addObserver(action, selector: action.selector, name: name, object: object)
115 | retainAction(action, observer)
116 | return action
117 | }
118 |
119 | // MARK: Remove observers
120 | /**
121 | Stop observing the given action.
122 | - parameter action: The action which won't be observed anymore
123 | */
124 | public func stopObserving(action: Action) {
125 | NotificationCenter.default.removeObserver(action)
126 | releaseAction(action, self)
127 | }
128 |
129 | /**
130 | Removes all the entries specifying a given observer from the receiver’s dispatch table.
131 | Be sure to invoke this method (or stopObserver(_:name:object:)) before observer or any object specified in add(observer:name:action:) is deallocated.
132 | You should not use this method to remove all observers from an object that is going to be long-lived, because your code may not be the only code adding observers that involve the object.
133 | - parameter observer: Object unregistered as observer.
134 | - parameter name: The name of the notification for which to unregister the observer; if nil, notifications with any name will be stopped.
135 | - parameter object: The object whose notifications the observer wants to stop; if nil, notifications from any object will be stopped.
136 | */
137 | public func stopObserver(_ observer: NSObject, name: NSNotification.Name? = nil, object: AnyObject? = nil) {
138 | for (_, value) in observer.actions {
139 |
140 | guard let action = value as? NotificationCenterAction else {
141 | continue
142 | }
143 |
144 | var matches: Bool
145 |
146 | switch (name, object) {
147 | case (nil, nil):
148 | matches = true
149 | case let (.some(name), nil):
150 | matches = (name == action.notificationName)
151 | case let (nil, .some(object)):
152 | matches = (object === object)
153 | case let (.some(name), .some(object)):
154 | matches = (object === object) && (name == action.notificationName)
155 | }
156 |
157 | if matches {
158 | stopObserving(action: action)
159 | }
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/actions/actions/Throttle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Throttle.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 11/10/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Just an internal protocol to encapsulate all the Throttles
12 | internal protocol ThrottleType {
13 | func scheduleWith(value: Any)
14 | func cancel()
15 | func fireWith(value: Any)
16 | }
17 |
18 |
19 | /// Object that allows scehduele actions to be called after a specific time interval, and prevent it of being called more than once in that interval.
20 | /// If the action is scheduled again before the time interval expires, it cancels the previous call (if any) preventing the action to be called twice.
21 | /// It contains a generic parameter `Argument` that indicates the type of parameter of the action.
22 | public final class Throttle {
23 |
24 | /// The type of the action.
25 | public typealias ThrottleAction = (Argument) -> Void
26 |
27 | /// The time interval that the Throttle will wait before call the actions
28 | private let interval: TimeInterval
29 |
30 | /// The action which will be called after the time interval
31 | private let action: ThrottleAction
32 |
33 | private var timer: Timer?
34 |
35 |
36 | /// Creates a new instance with the given time interval and action
37 | ///
38 | /// - parameter interval: The time interval
39 | /// - parameter action: The action
40 | ///
41 | /// - returns: The new Throttle
42 | public init(interval: TimeInterval, action: @escaping ThrottleAction) {
43 | self.interval = interval
44 | self.action = action
45 | }
46 |
47 |
48 | /// Schedule a new call of the action.
49 | /// If there is a pending action, it will be cancelled.
50 | ///
51 | /// - parameter value: The argument that will be sent as argument to the action closure
52 | public func schedule(with value: Argument) {
53 | cancel()
54 | timer = Timer.scheduledTimer(timeInterval: interval,
55 | target: self,
56 | selector: #selector(Throttle.onTimer),
57 | userInfo: value,
58 | repeats: false)
59 | }
60 |
61 | /// Cancel the pending action, if any.
62 | public func cancel() {
63 | timer?.invalidate()
64 | timer = nil
65 | }
66 |
67 |
68 | /// Force the execution of the action, without waiting for the interval.
69 | /// If there is a pending action, it will be cancelled.
70 | ///
71 | /// - parameter value: The argument that will be sent as argument to the action closure
72 | public func fire(with value: Argument) {
73 | action(value)
74 | }
75 |
76 | @objc private func onTimer(_ timer: Timer) {
77 | fire(with: timer.userInfo as! Argument)
78 | }
79 | }
80 |
81 | extension Throttle: ThrottleType {
82 | func scheduleWith(value: Any) {
83 | guard let value = value as? Argument else {
84 | return
85 | }
86 |
87 | schedule(with: value)
88 | }
89 |
90 | func fireWith(value: Any) {
91 | guard let value = value as? Argument else {
92 | return
93 | }
94 |
95 | fire(with: value)
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/actions/actions/Timer+Actions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Timer+Actions.swift
3 | // actions
4 | //
5 | // Created by Manu on 4/7/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Extension that allow create and schedule `Timers` with closures instead of target/selector
12 | extension Timer {
13 |
14 | // MARK: Inits with fire date
15 |
16 | /**
17 | Initializes a new Timer object using the specified action.
18 | The receiver, initialized such that, when added to a run loop, it will fire at date and then, if repeats is true, every ti after that.
19 | - parameter fireDate: The time at which the timer should first fire.
20 | - parameter interval: For a repeating timer, this parameter contains the number of seconds between firings of the timer. If ti is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead.
21 | - parameter userInfo: Custom user info for the timer. The timer maintains a strong reference to this object until it (the timer) is invalidated. This parameter is nil by default.
22 | - parameter repeats: If true, the timer will repeatedly reschedule itself until invalidated. If false, the timer will be invalidated after it fires. Default is false
23 | - parameter action: The closure called on the timeout
24 | - returns: The receiver, initialized such that, when added to a run loop, it will fire at date and then, if repeats is YES, every ti after that.
25 | */
26 | public convenience init(fireAt fireDate: Date, interval: TimeInterval, userInfo: AnyObject? = nil, repeats: Bool = false, action: @escaping (T) -> Void) {
27 | let action = ParametizedAction(action: action)
28 | self.init(fireAt: fireDate, interval: 0, target: action, selector: action.selector, userInfo: userInfo, repeats: repeats)
29 | retainAction(action, self)
30 | }
31 |
32 | /**
33 | Initializes a new Timer object using the specified action.
34 | The receiver, initialized such that, when added to a run loop, it will fire at date and then, if repeats is true, every ti after that.
35 | - parameter fireAt: The time at which the timer should first fire.
36 | - parameter interval: For a repeating timer, this parameter contains the number of seconds between firings of the timer. If ti is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead.
37 | - parameter userInfo: Custom user info for the timer. The timer maintains a strong reference to this object until it (the timer) is invalidated. This parameter is nil by default.
38 | - parameter repeats: If true, the timer will repeatedly reschedule itself until invalidated. If false, the timer will be invalidated after it fires. Default is false
39 | - parameter action: The closure called on the timeout
40 | - returns: The receiver, initialized such that, when added to a run loop, it will fire at date and then, if repeats is YES, every ti after that.
41 | */
42 | public convenience init(fireAt fireDate: Date, interval: TimeInterval, userInfo: AnyObject? = nil, repeats: Bool = false, action: @escaping () -> Void) {
43 | let action = VoidAction(action: action)
44 | self.init(fireAt: fireDate, interval: 0, target: action, selector: action.selector, userInfo: userInfo, repeats: repeats)
45 | retainAction(action, self)
46 | }
47 |
48 | // MARK: Inits with time interval
49 | /**
50 | Initializes a new Timer object using the specified action.
51 | The receiver, initialized such that, when added to a run loop, it will fire at date and then, if repeats is true, every ti after that.
52 | - parameter timeInterval: The number of seconds between firings of the timer. If ti is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead.
53 | - parameter userInfo: Custom user info for the timer. The timer maintains a strong reference to this object until it (the timer) is invalidated. This parameter is nil by default.
54 | - parameter repeats: If true, the timer will repeatedly reschedule itself until invalidated. If false, the timer will be invalidated after it fires. Default is false
55 | - parameter action: The closure called on the timeout
56 | - returns: A new Timer object, configured according to the specified parameters.
57 | */
58 | public convenience init(timeInterval interval: TimeInterval, userInfo: AnyObject? = nil, repeats: Bool = false, action: @escaping (T) -> Void) {
59 | let action = ParametizedAction(action: action)
60 | self.init(timeInterval: interval, target: action, selector: action.selector, userInfo: userInfo, repeats: repeats)
61 | retainAction(action, self)
62 | }
63 |
64 | /**
65 | Initializes a new Timer object using the specified action.
66 | The receiver, initialized such that, when added to a run loop, it will fire at date and then, if repeats is true, every ti after that.
67 | - parameter timeInterval: The number of seconds between firings of the timer. If ti is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead.
68 | - parameter userInfo: Custom user info for the timer. The timer maintains a strong reference to this object until it (the timer) is invalidated. This parameter is nil by default.
69 | - parameter repeats: If true, the timer will repeatedly reschedule itself until invalidated. If false, the timer will be invalidated after it fires. Default is false
70 | - parameter action: The closure called on the timeout
71 | - returns: A new Timer object, configured according to the specified parameters.
72 | */
73 | public convenience init(timeInterval interval: TimeInterval, userInfo: AnyObject? = nil, repeats: Bool = false, action: @escaping () -> Void) {
74 | let action = VoidAction(action: action)
75 | self.init(timeInterval: interval, target: action, selector: action.selector, userInfo: userInfo, repeats: repeats)
76 | retainAction(action, self)
77 | }
78 |
79 | // MARK: Schedule with interval
80 | /**
81 | Creates and returns a new Timer object and schedules it on the current run loop in the default mode.
82 | - parameter timeInterval: The number of seconds between firings of the timer. If ti is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead.
83 | - parameter userInfo: Custom user info for the timer. The timer maintains a strong reference to this object until it (the timer) is invalidated. This parameter is nil by default.
84 | - parameter repeats: If true, the timer will repeatedly reschedule itself until invalidated. If false, the timer will be invalidated after it fires. Default is false
85 | - parameter action: The closure called on the timeout
86 | - returns: A new Timer object, configured according to the specified parameters.
87 | */
88 | @discardableResult
89 | public class func scheduledTimer(timeInterval: TimeInterval, userInfo: AnyObject? = nil, repeats: Bool = false, action: @escaping (T) -> Void) -> Timer {
90 | let action = ParametizedAction(action: action)
91 |
92 | let timer = self.scheduledTimer(timeInterval: timeInterval,
93 | target: action,
94 | selector: action.selector,
95 | userInfo: userInfo,
96 | repeats: repeats)
97 | retainAction(action, timer)
98 |
99 | return timer
100 | }
101 |
102 | /**
103 | Creates and returns a new Timer object and schedules it on the current run loop in the default mode.
104 | - parameter timeInterval: The number of seconds between firings of the timer. If ti is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead.
105 | - parameter userInfo: Custom user info for the timer. The timer maintains a strong reference to this object until it (the timer) is invalidated. This parameter is nil by default.
106 | - parameter repeats: If true, the timer will repeatedly reschedule itself until invalidated. If false, the timer will be invalidated after it fires. Default is false
107 | - parameter action: The closure called on the timeout
108 | - returns: A new Timer object, configured according to the specified parameters.
109 | */
110 | @discardableResult
111 | public class func scheduledTimer(timeInterval: TimeInterval, userInfo: AnyObject? = nil, repeats: Bool = false, action: @escaping () -> Void) -> Timer {
112 | let action = VoidAction(action: action)
113 |
114 | let timer = self.scheduledTimer(timeInterval: timeInterval,
115 | target: action,
116 | selector: action.selector,
117 | userInfo: userInfo,
118 | repeats: repeats)
119 | retainAction(action, timer)
120 |
121 | return timer
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/actions/actions/UIBarButtonItem+Actions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIBarButtonItem+Actions.swift
3 | // actions
4 | //
5 | // Created by Manu on 1/6/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 | /// Extension that provides methods to add actions to bar button items
13 | extension UIBarButtonItem {
14 |
15 | // MARK: Init with image
16 |
17 | /**
18 | Initializes a new item using the specified image and other properties.
19 |
20 | - parameter image: The images displayed on the bar are derived from this image. If this image is too large to fit on the bar, it is scaled to fit. Typically, the size of a toolbar and navigation bar image is 20 x 20 points. The alpha values in the source image are used to create the images—opaque values are ignored.
21 | - parameter landscapeImagePhone: The style of the item. One of the constants defined in UIBarButtonItemStyle. nil by default
22 | - parameter style: The style of the item. One of the constants defined in UIBarButtonItemStyle. (.Plain by default)
23 | - parameter action: The action to be called when the button is tapped
24 | - returns: Newly initialized item with the specified properties.
25 | */
26 | public convenience init(image: UIImage?, landscapeImagePhone: UIImage? = nil, style: UIBarButtonItem.Style = .plain, action: @escaping (T) -> Void) {
27 | let action = ParametizedAction(action: action)
28 | self.init(image: image, landscapeImagePhone: landscapeImagePhone, style: style, target: action, action: action.selector)
29 | retainAction(action, self)
30 | }
31 |
32 | /**
33 | Initializes a new item using the specified image and other properties.
34 |
35 | - parameter image: The images displayed on the bar are derived from this image. If this image is too large to fit on the bar, it is scaled to fit. Typically, the size of a toolbar and navigation bar image is 20 x 20 points. The alpha values in the source image are used to create the images—opaque values are ignored.
36 | - parameter landscapeImagePhone: The style of the item. One of the constants defined in UIBarButtonItemStyle. nil by default
37 | - parameter style: The style of the item. One of the constants defined in UIBarButtonItemStyle. (.Plain by default)
38 | - parameter action: The action to be called when the button is tapped
39 | - returns: Newly initialized item with the specified properties.
40 | */
41 | public convenience init(image: UIImage?, landscapeImagePhone: UIImage? = nil, style: UIBarButtonItem.Style = .plain, action: @escaping () -> Void) {
42 | let action = VoidAction(action: action)
43 | self.init(image: image, landscapeImagePhone: landscapeImagePhone, style: style, target: action, action: action.selector)
44 | retainAction(action, self)
45 | }
46 |
47 | // MARK: Init with title
48 |
49 | /**
50 | Initializes a new item using the specified title and other properties.
51 |
52 | - parameter title: The item’s title.
53 | - parameter style: The style of the item. One of the constants defined in UIBarButtonItemStyle. (.Plain by default)
54 | - parameter action: The action to be called when the button is tapped
55 | - returns: Newly initialized item with the specified properties.
56 | */
57 | public convenience init(title: String?, style: UIBarButtonItem.Style = .plain, action: @escaping (T) -> Void) {
58 | let action = ParametizedAction(action: action)
59 | self.init(title: title, style: style, target: action, action: action.selector)
60 | retainAction(action, self)
61 | }
62 |
63 | /**
64 | Initializes a new item using the specified title and other properties.
65 |
66 | - parameter title: The item’s title.
67 | - parameter style: The style of the item. One of the constants defined in UIBarButtonItemStyle. (.Plain by default)
68 | - parameter action: The action to be called when the button is tapped
69 | - returns: Newly initialized item with the specified properties.
70 | */
71 | public convenience init(title: String?, style: UIBarButtonItem.Style = .plain, action: @escaping () -> Void) {
72 | let action = VoidAction(action: action)
73 | self.init(title: title, style: style, target: action, action: action.selector)
74 | retainAction(action, self)
75 | }
76 |
77 | // MARK: Init with system item
78 |
79 | /**
80 | Initializes a new item containing the specified system item.
81 |
82 | - parameter systemItem: The system item to use as the first item on the bar. One of the constants defined in UIBarButtonSystemItem.
83 | - parameter action: The action to be called when the button is tapped
84 | - returns: Newly initialized item with the specified properties.
85 | */
86 | public convenience init(barButtonSystemItem systemItem: UIBarButtonItem.SystemItem, action: @escaping (T) -> Void) {
87 | let action = ParametizedAction(action: action)
88 | self.init(barButtonSystemItem: systemItem, target: action, action: action.selector)
89 | retainAction(action, self)
90 | }
91 |
92 | /**
93 | Initializes a new item containing the specified system item.
94 |
95 | - parameter systemItem: The system item to use as the first item on the bar. One of the constants defined in UIBarButtonSystemItem.
96 | - parameter action: The action to be called when the button is tapped
97 | - returns: Newly initialized item with the specified properties.
98 | */
99 | public convenience init(barButtonSystemItem systemItem: UIBarButtonItem.SystemItem, action: @escaping () -> Void) {
100 | let action = VoidAction(action: action)
101 | self.init(barButtonSystemItem: systemItem, target: action, action: action.selector)
102 | retainAction(action, self)
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/actions/actions/UIControl+Actions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIControl+Actions.swift
3 | // actions
4 | //
5 | // Created by Manu on 1/6/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | private protocol ControlAction: Action {
12 | var controlEvent: UIControl.Event { get }
13 | }
14 |
15 | private class ControlVoidAction: VoidAction, ControlAction {
16 | fileprivate let controlEvent: UIControl.Event
17 | init(event: UIControl.Event, action: @escaping () -> Void) {
18 | controlEvent = event
19 | super.init(action: action)
20 | }
21 | }
22 |
23 | private class ControlParametizedAction: ParametizedAction, ControlAction {
24 | fileprivate let controlEvent: UIControl.Event
25 | init(event: UIControl.Event, action: @escaping (T) -> Void) {
26 | controlEvent = event
27 | super.init(action: action)
28 | }
29 | }
30 |
31 | // Action to manage the two parameters selector allowed in controls
32 | private class EventAction: ControlAction {
33 |
34 | fileprivate let controlEvent: UIControl.Event
35 | @objc let key = ProcessInfo.processInfo.globallyUniqueString
36 | @objc let selector: Selector = #selector(perform)
37 |
38 | let action: (T, UIEvent?) -> Void
39 |
40 | @objc func perform(parameter: AnyObject, event: UIEvent?) {
41 | action(parameter as! T, event)
42 | }
43 |
44 | init(event: UIControl.Event, action: @escaping (T, UIEvent?) -> Void) {
45 | self.action = action
46 | controlEvent = event
47 | }
48 | }
49 |
50 |
51 | /// Extension that provides methods to add actions to controls
52 | public extension UIControl {
53 |
54 | // MARK: Single event
55 |
56 | /**
57 | Adds the given action as response to the given control event.
58 | - parameter event: The event that the control must receive to trigger the closure
59 | - parameter action: The closure that will be called when the gesture is detected
60 | - returns: The added action
61 | */
62 | @discardableResult
63 | public func add(event: UIControl.Event, action: @escaping (T, UIEvent?) -> Void) -> Action {
64 | let action = EventAction(event: event, action: action)
65 | add(event: event, action: action)
66 | return action
67 | }
68 |
69 | /**
70 | Adds the given action as response to the given control event.
71 | - parameter event: The event that the control must receive to trigger the closure
72 | - parameter action: The closure that will be called when the gesture is detected
73 | - returns: The added action
74 | */
75 | @discardableResult
76 | public func add(event: UIControl.Event, action: @escaping (T) -> Void) -> Action {
77 | let action = ControlParametizedAction(event: event, action: action)
78 | add(event: event, action: action)
79 | return action
80 | }
81 |
82 | /**
83 | Adds the given action as response to the given control event.
84 | - parameter event: The event that the control must receive to trigger the closure
85 | - parameter action: The closure that will be called when the gesture is detected
86 | - returns: The added action
87 | */
88 | @discardableResult
89 | public func add(event: UIControl.Event, action: @escaping () -> Void) -> Action {
90 | let action = ControlVoidAction(event: event, action: action)
91 | add(event: event, action: action)
92 | return action
93 | }
94 |
95 |
96 | // MARK: Multiple events
97 |
98 | /**
99 | Adds the given action as response to the given control events.
100 | - parameter events: The events that the control must receive to trigger the closure
101 | - parameter action: The closure that will be called when the gesture is detected
102 | - returns: The added actions
103 | */
104 | @discardableResult
105 | public func add(events: [UIControl.Event], action: @escaping (T, UIEvent?) -> Void) -> [Action] {
106 | return events.map { add(event: $0, action: action) }
107 | }
108 |
109 | /**
110 | Adds the given action as response to the given control events.
111 | - parameter events: The events that the control must receive to trigger the closure
112 | - parameter action: The closure that will be called when the gesture is detected
113 | - returns: The added actions
114 | */
115 | @discardableResult
116 | public func addAction(events: [UIControl.Event], action: @escaping (T) -> Void) -> [Action] {
117 | return events.map { add(event: $0, action: action) }
118 | }
119 |
120 | /**
121 | Adds the given action as response to the given control events.
122 | - parameter events: The events that the control must receive to trigger the closure
123 | - parameter action: The closure that will be called when the gesture is detected
124 | - returns: The added actions
125 | */
126 | @discardableResult
127 | public func addAction(events: [UIControl.Event], action: @escaping () -> Void) -> [Action] {
128 | return events.map { add(event: $0, action: action) }
129 | }
130 |
131 | // MARK: Private
132 | private func add(event: UIControl.Event, action: Action) {
133 | retainAction(action, self)
134 | addTarget(action,
135 | action: action.selector,
136 | for: event)
137 | }
138 |
139 | // MARK: Remove
140 | /**
141 | Disable the given action to be launched as response of the received event
142 | - parameter action: The action to disable
143 | - parameter events: The control events that you want to remove for the specified target object
144 | */
145 | @available(*, deprecated, message: "Use remove(_:for:) instead")
146 | public func remove(action: Action, forControlEvents events: UIControl.Event) {
147 | remove(action, for: events)
148 | }
149 |
150 | /**
151 | Disable the given action to be launched as response of the received event
152 | - parameter action: The action to disable
153 | - parameter events: The control events that you want to remove for the specified target object
154 | */
155 | public func remove(_ action: Action, for events: UIControl.Event) {
156 | removeTarget(action, action: action.selector, for: events)
157 | releaseAction(action, self)
158 | }
159 |
160 | /**
161 | Disable all the actions for a given event to be launched as response of the received event.
162 | **NOTE**: Just the actions added using the `Actions` method will be removed!.
163 | - parameter events: The control events that you want to remove for the specified target object
164 | */
165 | public func removeActions(for events: UIControl.Event) {
166 | for (_, value) in actions {
167 | guard let action = value as? ControlAction,
168 | (action.controlEvent.rawValue & events.rawValue) != 0 else {
169 | continue
170 | }
171 |
172 | remove(action, for: events)
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/actions/actions/UIControl+Throttle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIControl+Throttle.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 11/10/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ObjectiveC
11 |
12 | private typealias ThrottleAndAction = (throttle: ThrottleType, action: Action)
13 |
14 | private var throttlesKey: UInt8 = 0
15 |
16 | /// Extension which provides a set of methods to Throttle actions triggered by control events.
17 | extension UIControl {
18 |
19 |
20 | /// Add an action for the given control evetn.
21 | /// The action is not performed immediately, instead it is scheduled to be executed after the given time interval.
22 | /// If the control event is triggered again before the time interval expires, the previous call is canceled.
23 | /// It prevents the action of being triggered more than once in the given time interval.
24 | ///
25 | /// - parameter event: The event which triggers the action
26 | /// - parameter interval: The time interval to wait before performing the action
27 | /// - parameter handler: The action which will be executed when the time itnerval expires.
28 | public func throttle(_ event: UIControl.Event, interval: TimeInterval, handler: @escaping (T, UIEvent?) -> Void) {
29 |
30 | let throttle = Throttle(interval: interval) { (sender, event) in
31 | handler(sender, event)
32 | }
33 |
34 | let action = add(event: event) { (control: T, event: UIEvent?) in
35 | throttle.schedule(with: (control, event))
36 | }
37 |
38 | add(throttle: throttle, action: action, for: event)
39 | }
40 |
41 | /// Add an action for the given control evetn.
42 | /// The action is not performed immediately, instead it is scheduled to be executed after the given time interval.
43 | /// If the control event is triggered again before the time interval expires, the previous call is canceled.
44 | /// It prevents the action of being triggered more than once in the given time interval.
45 | ///
46 | /// - parameter event: The event which triggers the action
47 | /// - parameter interval: The time interval to wait before performing the action
48 | /// - parameter handler: The action which will be executed when the time itnerval expires.
49 | public func throttle(_ event: UIControl.Event, interval: TimeInterval, handler: @escaping (T) -> Void) {
50 |
51 | let throttle = Throttle(interval: interval, action: handler)
52 | let action = add(event: event) { (control: T) in
53 | throttle.schedule(with: control)
54 | }
55 |
56 | add(throttle: throttle, action: action, for: event)
57 | }
58 |
59 | /// Add an action for the given control evetn.
60 | /// The action is not performed immediately, instead it is scheduled to be executed after the given time interval.
61 | /// If the control event is triggered again before the time interval expires, the previous call is canceled.
62 | /// It prevents the action of being triggered more than once in the given time interval.
63 | ///
64 | /// - parameter event: The event which triggers the action
65 | /// - parameter interval: The time interval to wait before performing the action
66 | /// - parameter handler: The action which will be executed when the time itnerval expires.
67 | public func throttle(_ event: UIControl.Event, interval: TimeInterval, handler: @escaping () -> Void) {
68 | let throttle = Throttle(interval: interval, action: handler)
69 | let action = add(event: event) {
70 | throttle.schedule(with: ())
71 | }
72 |
73 | add(throttle: throttle, action: action, for: event)
74 | }
75 |
76 | /// Remove the current Throttle (if any) for the given control event
77 | ///
78 | /// - parameter event: The event whose Throttle will be removed
79 | public func removeThrottle(for event: UIControl.Event) {
80 | if let currentThrottle = self.throttles[event.rawValue] {
81 | currentThrottle.throttle.cancel()
82 | remove(currentThrottle.action, for: event)
83 | }
84 | }
85 |
86 | private func add(throttle: Throttle, action: Action, for event: UIControl.Event) {
87 | removeThrottle(for: event)
88 | self.throttles[event.rawValue] = (throttle, action)
89 | }
90 |
91 | // MARK - Throttles
92 | private var throttles: [UInt: ThrottleAndAction] {
93 | get {
94 | var throttles: [UInt: ThrottleAndAction]
95 |
96 | if let storedThrottles = objc_getAssociatedObject(self, &throttlesKey) as? [UInt: ThrottleAndAction] {
97 | throttles = storedThrottles
98 | }
99 | else {
100 | throttles = [:]
101 | self.throttles = throttles
102 | }
103 |
104 | return throttles
105 | }
106 | set(newValue) {
107 | objc_setAssociatedObject(self, &throttlesKey, newValue, .OBJC_ASSOCIATION_RETAIN)
108 | }
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/actions/actions/UIGestureRecognizer+Actions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIGestureRecognizer+Actions.swift
3 | // actions
4 | //
5 | // Created by Manu on 1/6/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 | /// Extension that provides methods to add actions to gesture recognizer
13 | extension UIGestureRecognizer {
14 |
15 | /**
16 | Initializes a new item with the given action
17 | - parameter action: The action to be called when the button is tapped
18 | - returns: Newly initialized item with the specified action.
19 | */
20 | public convenience init(action: @escaping (T) -> Void) {
21 | let action = ParametizedAction(action: action)
22 | self.init(target: action, action: action.selector)
23 | retainAction(action, self)
24 | }
25 |
26 | /**
27 | Initializes a new item with the given action
28 | - parameter action: The action to be called when the button is tapped
29 | - returns: Newly initialized item with the specified action.
30 | */
31 | public convenience init(action: @escaping () -> Void) {
32 | let action = VoidAction(action: action)
33 | self.init(target: action, action: action.selector)
34 | retainAction(action, self)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/actions/actions/UIView+Actions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Actions.swift
3 | // actions
4 | //
5 | // Created by Manu on 1/6/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | Action where the parameter can be assigned manually
13 | */
14 | private class CustomParametizedAction: Action {
15 |
16 | @objc let key = ProcessInfo.processInfo.globallyUniqueString
17 | @objc let selector: Selector = #selector(perform)
18 |
19 | let action: ((T) -> Void)
20 | weak var parameter: T?
21 |
22 | init(parameter: T?, action: @escaping (T) -> Void) {
23 | self.action = action
24 | self.parameter = parameter
25 | }
26 |
27 | @objc func perform() {
28 | guard let parameter = parameter else { return }
29 | action(parameter)
30 | }
31 | }
32 |
33 | /**
34 | The gestures that can be used to trigger actions in `UIView`
35 | */
36 | public enum Gesture {
37 |
38 | /// A tap gesture with a single finger and the given number of touches
39 | case tap(Int)
40 |
41 | /// A swipe gesture with a single finger and the given direction
42 | case swipe(UISwipeGestureRecognizer.Direction)
43 |
44 | /// A tap gesture with the given number of touches and fingers
45 | case multiTap(taps: Int, fingers: Int)
46 |
47 | /// A swipe gesture with the given direction and number of fingers
48 | case multiSwipe(direction: UISwipeGestureRecognizer.Direction, fingers: Int)
49 |
50 | fileprivate func recognizer(action: Action) -> UIGestureRecognizer {
51 |
52 | switch self {
53 | case let .tap(taps):
54 | let recognizer = UITapGestureRecognizer(target: action, action: action.selector)
55 | recognizer.numberOfTapsRequired = taps
56 | return recognizer
57 |
58 | case let .swipe(direction):
59 | let recognizer = UISwipeGestureRecognizer(target: action, action: action.selector)
60 | recognizer.direction = direction
61 | return recognizer
62 |
63 | case let .multiTap(taps, fingers):
64 | let recognizer = UITapGestureRecognizer(target: action, action: action.selector)
65 | recognizer.numberOfTapsRequired = taps
66 | recognizer.numberOfTouchesRequired = fingers
67 | return recognizer
68 |
69 | case let .multiSwipe(direction, fingers):
70 | let recognizer = UISwipeGestureRecognizer(target: action, action: action.selector)
71 | recognizer.direction = direction
72 | recognizer.numberOfTouchesRequired = fingers
73 | return recognizer
74 | }
75 | }
76 | }
77 |
78 | /// Extension that provides methods to add actions to views
79 | extension UIView {
80 |
81 | /**
82 | Adds the given action as response to the gesture.
83 | - parameter gesture: The gesture that the view must receive to trigger the closure
84 | - parameter action: The closure that will be called when the gesture is detected
85 | - returns: The gesture recognizer that has been added
86 | */
87 | @discardableResult
88 | public func add(gesture: Gesture, action: @escaping (T) -> Void) -> UIGestureRecognizer {
89 | let action = CustomParametizedAction(parameter: (self as! T), action: action)
90 | return add(gesture: gesture, action: action)
91 | }
92 |
93 | /**
94 | Adds the given action as response to the gesture.
95 | - parameter gesture: The gesture that the view must receive to trigger the closure
96 | - parameter action: The closure that will be called when the gesture is detected
97 | - returns: The gesture recognizer that has been added
98 | */
99 | @discardableResult
100 | public func add(gesture: Gesture, action: @escaping () -> Void) -> UIGestureRecognizer {
101 | let action = VoidAction(action: action)
102 | return add(gesture: gesture, action: action)
103 | }
104 |
105 | /**
106 | Adds the given action as response to a single tap gesture.
107 | - parameter gesture: The gesture that the view must receive to trigger the closure
108 | - parameter action: The closure that will be called when the gesture is detected
109 | - returns: The gesture recognizer that has been added
110 | */
111 | @discardableResult
112 | public func addTap(action: @escaping (T) -> Void) -> UIGestureRecognizer {
113 | let action = CustomParametizedAction(parameter: (self as! T), action: action)
114 | return add(gesture: .tap(1), action: action)
115 | }
116 |
117 | /**
118 | Adds the given action as response to a single tap gesture.
119 | - parameter gesture: The gesture that the view must receive to trigger the closure
120 | - parameter action: The closure that will be called when the gesture is detected
121 | - returns: The gesture recognizer that has been added
122 | */
123 | @discardableResult
124 | public func addAction(action: @escaping () -> Void) -> UIGestureRecognizer {
125 | let action = VoidAction(action: action)
126 | return add(gesture: .tap(1), action: action)
127 | }
128 |
129 | @discardableResult
130 | private func add(gesture: Gesture, action: Action) -> UIGestureRecognizer{
131 | retainAction(action, self)
132 | let gesture = gesture.recognizer(action: action)
133 | isUserInteractionEnabled = true
134 | addGestureRecognizer(gesture)
135 | return gesture
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/actions_app/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // actions
4 | //
5 | // Created by Manu on 1/6/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | private func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/actions_app/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/actions_app/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/actions_app/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
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 |
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 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
270 |
277 |
284 |
285 |
286 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
--------------------------------------------------------------------------------
/actions_app/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/actions_app/data/Data.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Data.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 1/11/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct AppData {
12 |
13 | let title: String
14 | let storybordIdentifier: String
15 |
16 | static let all: [AppData] = {
17 | return [
18 | AppData(title: "UIView", storybordIdentifier: "UIViewViewController"),
19 | AppData(title: "UIControl", storybordIdentifier: "UIControlViewController"),
20 | AppData(title: "UIGestureRecognizer", storybordIdentifier: "UIGestureRecognizerViewController"),
21 | AppData(title: "UIBarbuttonItem", storybordIdentifier: "UIBarbuttonItemViewController"),
22 | AppData(title: "Timer", storybordIdentifier: "TimerViewController"),
23 | AppData(title: "NotificationCenter", storybordIdentifier: "NotificationCenterViewController"),
24 | ]
25 | }()
26 | }
27 |
--------------------------------------------------------------------------------
/actions_app/scenes/BaseViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseViewController.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 1/11/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BaseViewController: UIViewController {
12 | deinit {
13 | print("Deinit \(self)")
14 | }
15 |
16 | func showAlert(message: String) {
17 | let alertView = UIAlertController(title: "Actions",
18 | message: message,
19 | preferredStyle: .alert)
20 | alertView.addAction(UIAlertAction(title: "Ok",
21 | style: .cancel,
22 | handler: nil))
23 |
24 | present(alertView, animated: true, completion: nil)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/actions_app/scenes/HomeViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomeViewController.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 1/11/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HomeViewController: UITableViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | self.title = "Actions"
16 | }
17 |
18 | // MARK: - Table view data source
19 |
20 | override func numberOfSections(in tableView: UITableView) -> Int {
21 | return 1
22 | }
23 |
24 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
25 | return AppData.all.count
26 | }
27 |
28 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
29 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
30 | let data = AppData.all[indexPath.row]
31 | cell.textLabel?.text = data.title
32 | return cell
33 | }
34 |
35 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
36 | let data = AppData.all[indexPath.row]
37 | let controller = storyboard!.instantiateViewController(withIdentifier: data.storybordIdentifier)
38 | navigationController?.pushViewController(controller, animated: true)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/actions_app/scenes/NotificationCenterViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NotificationCenterViewController.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 1/11/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Actions
11 |
12 | extension NSNotification.Name {
13 | fileprivate static var notification1: NSNotification.Name { return NSNotification.Name("notification1") }
14 | fileprivate static var notification2: NSNotification.Name { return NSNotification.Name("notification2") }
15 | }
16 |
17 | class NotificationCenterViewController: BaseViewController {
18 |
19 | private var observing = false {
20 | didSet {
21 | let title = observing ? "Stop observing Not. 1" : "Start observing Not. 1"
22 | self.startStopButton.setTitle(title, for: .normal)
23 |
24 | if observing {
25 | NotificationCenter.default.add(observer: self, name: .notification1) { [unowned self] in
26 | self.showAlert(message: "Notification 1 catched")
27 | }
28 | }
29 | else {
30 | NotificationCenter.default.stopObserver(self, name: .notification1)
31 | }
32 | }
33 | }
34 |
35 | deinit {
36 | // post notification 2 to ensure the app does not crash
37 | Timer.scheduledTimer(timeInterval: 2) {
38 | NotificationCenter.default.post(name: .notification2, object: nil)
39 | }
40 | }
41 |
42 | @IBOutlet weak var startStopButton: UIButton!
43 |
44 | override func viewDidLoad() {
45 | super.viewDidLoad()
46 | self.title = "Actions - NotificationCenter"
47 | observing = false
48 |
49 | NotificationCenter.default.add(observer: self, name: .notification2) { [unowned self] in
50 | self.showAlert(message: "Notification 2 catched")
51 | }
52 | }
53 |
54 | @IBAction func didPressStartStop(_ sender: AnyObject) {
55 | observing = !observing
56 | }
57 |
58 | @IBAction func didPressPostNotification1(_ sender: AnyObject) {
59 | NotificationCenter.default.post(name: .notification1, object: nil)
60 | }
61 |
62 | @IBAction func didPressPostNotification2(_ sender: AnyObject) {
63 | NotificationCenter.default.post(name: .notification2, object: nil)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/actions_app/scenes/TimerViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimerViewController.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 1/11/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Actions
11 |
12 | class TimerViewController: BaseViewController {
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 | self.title = "Actions - Timer"
17 | }
18 |
19 | @IBAction func didPressTimer(_ sender: AnyObject) {
20 | Timer.scheduledTimer(timeInterval: 5) { [weak self] (timer: Timer) in
21 | self?.showAlert(message: "Timer \(timer) did expire")
22 | }
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/actions_app/scenes/UIBarbuttonItemViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIBarbuttonItemViewController.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 1/11/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Actions
11 |
12 | class UIBarbuttonItemViewController: BaseViewController {
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 | self.title = "Actions - UIBarButtonItem"
17 |
18 | let systemItem = UIBarButtonItem(barButtonSystemItem: .action) { [unowned self] in
19 | self.showAlert(message: "Did press \"Action\" item")
20 | }
21 |
22 | let titleItem = UIBarButtonItem(title: "Item") { [unowned self] (item: UIBarButtonItem) in
23 | self.showAlert(message: "Did press \(item)")
24 | }
25 |
26 | navigationItem.rightBarButtonItems = [systemItem, titleItem]
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/actions_app/scenes/UIControlViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIControlViewController.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 1/11/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class UIControlViewController: BaseViewController {
12 |
13 | @IBOutlet weak var switchButton: UISwitch!
14 | @IBOutlet weak var button: UIButton!
15 | @IBOutlet weak var textField: UITextField!
16 | override func viewDidLoad() {
17 |
18 | super.viewDidLoad()
19 | self.title = "Actions - UIControl"
20 |
21 | switchButton.add(event: .valueChanged) { [unowned self] in
22 | self.showAlert(message: "Switch pressed")
23 | }
24 |
25 | button.add(event: .touchUpInside) { [unowned self] (button: UIButton) in
26 | self.showAlert(message: "Button \(button) pressed")
27 | }
28 |
29 | textField.throttle(.editingChanged, interval: 1) { [unowned self] (textField: UITextField) in
30 | self.showAlert(message: "Textfield change text to \"\(textField.text ?? "")\"")
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/actions_app/scenes/UIGestureRecognizerViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIGestureRecognizerViewController.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 1/11/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Actions
11 |
12 | class UIGestureRecognizerViewController: BaseViewController {
13 |
14 | @IBOutlet weak var dragView: UIView!
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 | self.title = "Actions - UIGestureRecognizer"
18 |
19 | let panRecognizer = UIPanGestureRecognizer { (pan: UIPanGestureRecognizer) in
20 |
21 | guard let view = pan.view else { return }
22 |
23 | let translation = pan.translation(in: view)
24 | view.center = CGPoint(x:view.center.x + translation.x,
25 | y:view.center.y + translation.y)
26 |
27 | pan.setTranslation(CGPoint(), in: view)
28 | }
29 |
30 | dragView.gestureRecognizers = [panRecognizer]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/actions_app/scenes/UIViewViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewViewController.swift
3 | // actions
4 | //
5 | // Created by Manuel García-Estañ on 1/11/16.
6 | // Copyright © 2016 manuege. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Actions
11 |
12 | class UIViewViewController: BaseViewController {
13 | @IBOutlet weak var tapView: UIView!
14 | @IBOutlet weak var tapTwiceView: UIView!
15 | @IBOutlet weak var tapTwoFingersView: UIView!
16 | @IBOutlet weak var swipeView: UIView!
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 | self.title = "Actions - UIView"
21 | addActions()
22 | }
23 |
24 | private func addActions() {
25 | tapView.addAction { [unowned self] in
26 | self.showAlert(message: "View tapped")
27 | }
28 |
29 | tapTwiceView.add(gesture: .tap(2)) { [unowned self] (view: UIView) in
30 | self.showAlert(message: "View \(view) tapped twice")
31 | }
32 |
33 | tapTwoFingersView.add(gesture: .multiTap(taps: 1, fingers: 2)) { [unowned self] (view: UIView) in
34 | self.showAlert(message: "View \(view) tapped with two fingers")
35 | }
36 |
37 | swipeView.add(gesture: .swipe(.left)) { [unowned self] in
38 | self.showAlert(message: "View swipped left")
39 | }
40 |
41 | swipeView.add(gesture: .swipe(.right)) { [unowned self] in
42 | self.showAlert(message: "View swipped right")
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------