├── .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 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](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 | --------------------------------------------------------------------------------