问题
I was just wondering if it was possible to pass a function to a button action (which is usually a selector).
For example, normally I'd say:
UIBarButtonItem(title: "Press", style: .Done, target: self, action: "functionToCall")
func functionToCall() {
// Do something
}
But I was wondering if it's possible to do something like:
UIBarButtonItem(title: "Press", style: .Done, target: self, action: {
// Do Something
})
Reason I want to do this is because my function is super simple and it seems like it would be neater and more Swift-like what with the emphasis they are placing on closures.
回答1:
Here's an updated solution for Swift 3.
class BlockBarButtonItem: UIBarButtonItem {
private var actionHandler: ((Void) -> Void)?
convenience init(title: String?, style: UIBarButtonItemStyle, actionHandler: ((Void) -> Void)?) {
self.init(title: title, style: style, target: nil, action: #selector(barButtonItemPressed))
self.target = self
self.actionHandler = actionHandler
}
convenience init(image: UIImage?, style: UIBarButtonItemStyle, actionHandler: ((Void) -> Void)?) {
self.init(image: image, style: style, target: nil, action: #selector(barButtonItemPressed))
self.target = self
self.actionHandler = actionHandler
}
func barButtonItemPressed(sender: UIBarButtonItem) {
actionHandler?()
}
}
回答2:
This is an alternative solution without subclassing:
extension UIBarButtonItem {
private struct AssociatedObject {
static var key = "action_closure_key"
}
var actionClosure: (()->Void)? {
get {
return objc_getAssociatedObject(self, &AssociatedObject.key) as? ()->Void
}
set {
objc_setAssociatedObject(self, &AssociatedObject.key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
target = self
action = #selector(didTapButton(sender:))
}
}
@objc func didTapButton(sender: Any) {
actionClosure?()
}
}
It relies on the associated objects from the Objective-C runtime to add a closure/block property.
When set, it changes the target to itself and points the selector to a new function that calls the closure if it exists.
With this, in any moment you can just set the actionClosure of any UIBarButtonItem and expect everything to work, here's an example:
let button = UIBarButtonItem(
barButtonSystemItem: .action,
target: nil, action: nil
)
button.actionClosure = {
print("hello")
}
回答3:
- Updated for Swift 5.
- Implements all of the convenience initializers that have targets.
- Adds a
typealiasfor the function signature to clean up the syntax. - Passes the
UIBarButtonIteminto the action handler in keeping with Cocoa conventions.
import UIKit
class SwiftBarButtonItem: UIBarButtonItem {
typealias ActionHandler = (UIBarButtonItem) -> Void
private var actionHandler: ActionHandler?
convenience init(image: UIImage?, style: UIBarButtonItem.Style, actionHandler: ActionHandler?) {
self.init(image: image, style: style, target: nil, action: #selector(barButtonItemPressed(sender:)))
target = self
self.actionHandler = actionHandler
}
convenience init(title: String?, style: UIBarButtonItem.Style, actionHandler: ActionHandler?) {
self.init(title: title, style: style, target: nil, action: #selector(barButtonItemPressed(sender:)))
target = self
self.actionHandler = actionHandler
}
convenience init(barButtonSystemItem systemItem: UIBarButtonItem.SystemItem, actionHandler: ActionHandler?) {
self.init(barButtonSystemItem: systemItem, target: nil, action: #selector(barButtonItemPressed(sender:)))
target = self
self.actionHandler = actionHandler
}
@objc func barButtonItemPressed(sender: UIBarButtonItem) {
actionHandler?(sender)
}
}
回答4:
A post on Reddit explains a solution for this with a custom component - https://www.reddit.com/r/swift/comments/3fjzap/creating_button_action_programatically_using
To use it though I had to add the Button programatically rather than through storyboard. Here was how I did it.
let tempVariableIWantToReference = "Classy"
navigationTitle.leftBarButtonItem = BlockBarButtonItem.init(
title: "< Back", style: UIBarButtonItemStyle.Plain,
actionHandler: { () -> Void in
print("Hey I'm in a closure")
print("I can even reference temporary variables. \(self.tempVariableIWantToReference)!!!")
})
回答5:
Unfortunatelly it is not possible with Apple provided initialisers. The way this works in the background is with reflection, and providing a closure is something completely different which isn't currently supported.
You might be able to create a custom solution with some hacking or Apple may introduce that in the future.
来源:https://stackoverflow.com/questions/33652284/swift-use-selector-argument-like-a-closure