ActionKit is a experimental, light-weight, easy to use framework that wraps the target-action design paradigm into a less verbose, cleaner format. It shortens target-action method calls by removing the target and replacing the selector with a closure.
Licensed under the terms of the MIT license
button.addTarget(self, action: Selector("buttonWasTapped:"), forControlEvents: .TouchUpInside)
func buttonWasTapped(sender: UIButton!) {
self.button.setTitle("Button was tapped!", forState: .Normal)
}
button.addTarget(self, action: #selector(ViewController.buttonWasTapped(_:)), forControlEvents: .TouchUpInside)
func buttonWasTapped(sender: UIButton!) {
self.button.setTitle("Button was tapped!", forState: .Normal)
}
button.addControlEvent(.touchUpInside) {
self.button.setTitle("Button was tapped!", forState: .Normal)
}
button.addControlEvent(.touchUpInside) { (control: UIControl) in
guard let button = control as? UIButton else {
return
}
button.setTitle("Button was tapped!", forState: .Normal)
}
- addControlEvent(_ controlEvents: UIControlEvents, _ closure: @escaping () -> ())
- addControlEvent(_ controlEvents: UIControlEvents, _ controlClosure: @escaping (UIControl) -> ())
button.addControlEvent(.touchUpInside) {
self.button.setTitle("Button was tapped!", forState: .Normal)
}
button.addControlEvent(.touchUpInside) { (control: UIControl) in
guard let button = control as? UIButton else {
return
}
button.setTitle("Button was tapped!", forState: .Normal)
}
- removeControlEvent(controlEvents: UIControlEvents)
button.removeControlEvent(.touchUpInside)
Note: when a UIControl with associated actions is removed from it's superview, all associated actions are removed. This behavior can be overriden, but should be done at your own risk since this ensures references to those UIControls aren't kept longer than needed (and therefore causing a memory leak).
- init(_ name: String, closure: @escaping () -> ())
- init(_ name: String, gestureClosure: @escaping (UIGestureRecognizer) -> ())
var singleTapGestureRecognizer = UITapGestureRecognizer() {
self.view.backgroundColor = UIColor.redColor()
}
var singleTapGestureRecognizer = UITapGestureRecognizer() { (gesture: UIGestureRecognizer) in
if gesture.state == .Began {
let locInView = gesture.locationInView(self.view)
...
}
}
- addClosure(name: String, closure: () -> ())
- addClosure(name: String, closureWithGesture: (UIGestureRecognizer) -> ())
singleTapGestureRecognizer.addClosure("makeBlue") {
self.view.backgroundColor = UIColor.blueColor()
}
- removeClosure(_ name: String)
singleTapGestureRecognizer.removeClosure("makeBlue")
Note: when a UIGestureRecognizer is no longer needed, any references kept in closures added to the gesture need to be removed. Either by calling removeClosure
as shown above for each named closure, or by simply calling clearActionKit()
on the gestureRecognizer which will remove all associated named closures for the recognizer.
// Init with image
- init(image: UIImage, landscapeImagePhone: UIImage? = nil, style: UIBarButtonItemStyle = .Plain, actionClosure: () -> Void)
// Init with title
- init(title: String, style: UIBarButtonItemStyle = .Plain, actionClosure: () -> Void)
// Init with barButtonSystemInit
- init(barButtonSystemItem systemItem: UIBarButtonSystemItem, actionClosure: () -> Void)
// Init with image
- init(image: UIImage, landscapeImagePhone: UIImage? = nil, style: UIBarButtonItemStyle = .Plain, actionWithItem: UIBarButtonItem -> Void)
// Init with title
- init(title: String, style: UIBarButtonItemStyle = .Plain, actionWithItem: UIBarButtonItem -> Void)
// Init with barButtonSystemInit
- init(barButtonSystemItem systemItem: UIBarButtonSystemItem, actionWithItem: UIBarButtonItem -> Void)
let titleItem = UIBarButtonItem(title: "Press me") {
print("Title item pressed")
}
let image = UIImage(named: "alert")!
let imageItem = UIBarButtonItem(image: image) { (item: UIBarButtonItem) in
print("Item \(item) pressed")
}
let systemItem = UIBarButtonItem(barButtonSystemItem: .Action) {
print("System item pressed")
}
- addActionClosure(actionClosure: () -> ())
titleItem.addActionClosure {
print("new action")
}
- clearActionKit()
titleItem.clearActionKit()
ActionKit extends target-action functionality by providing easy to use methods that take closures instead of a selector. ActionKit uses a singleton which stores the closures and acts as the target. Closures capture and store references to any constants and variables from their context, so the user is free to use variables from the context in which the closure was defined in.
Version 2.0 is a full rewrite of the library. As much as possible, backwards compability was kept in mind, and with that said, only a couple things will break:
- inferred parameter types are more generic, now, and will need to be cast to specific control type within closure (think UIGestureRecognizer to UITapGestureRecognizer, for example)
- unnecessary parameter names can now be ommitted, and the compiler will likely warn users about this when updating to version 2.
- memory leaks should now be fixed! Through a couple automatic use cases plus exposing a more clear pattern for clearing references, ActionKit should now be memory leak proof while being fully written for Swift -- no Objective-C associated references!
What's planned:
- automated build tooling, including adding unit tests to ensure stability of ActionKit
- bugs -- with a full rewrite, things will likely break...bug reports and issues will be gladly accepted and fixed ASAP. Additionally, PRs for open issues and bug reports are also welcome, but I will also put in more effort into fixing any issues opened.
Version 1.1.0 adds an optional UIControl
or UIGestureRecognizer
to the closure. This might lead to possible backwards-incompatibility.
We made sure you can still call the closures without any parameters, like the following:
button.addControlEvent(.TouchUpInside) {
print("the button was tapped")
}
However, with previous versions of ActionKit, due to the peculiarity of Swift, it was also possible to call the closure with an unused parameter:
button.addControlEvent(.TouchUpInside) { _ in
print("the button was tapped")
}
In this example the _
refers to the empty input tuple ()
.
Now, with these extra closure parameters, the above is no longer valid, as it is ambiguous which method is being called: addControlEvent
without closure parameters or with a UIControl
as closure parameter. When you have this Xcode will report: Ambiguous use of 'addControlEvent'.
If you're using _ in
in your code and you get this ambiguous error, migrate by either removing the _ in
all together or by replacing it with (control: UIControl) in
. (For gesture recognizers use (gesture: UIGestureRecognizer) in
.)
- Adding and removing an action to concrete gesture-recognizer objects, eg. UITapGestureRecognizer, UISwipeGestureRecognizer
- Adding and removing an action for UIControl objects, eg. UIButton, UIView
ActionKit is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'ActionKit', '~> 2.0'
-
- Add the following to your Cartfile:
github "ActionKit/ActionKit" == 2.0
-
- Run
carthage update
- Run
-
- Add the framework as described in Carthage Readme