Swift использовать аргумент селектора, как закрытие
Мне просто интересно, можно ли передать функцию на действие кнопки (обычно это селектор).
Например, обычно я бы сказал:
UIBarButtonItem(title: "Press", style: .Done, target: self, action: "functionToCall")
func functionToCall() {
// Do something
}
Но мне было интересно, можно ли сделать что-то вроде:
UIBarButtonItem(title: "Press", style: .Done, target: self, action: {
// Do Something
})
Причина, по которой я хочу это сделать, состоит в том, что моя функция супер проста, и похоже, что она была бы более аккуратной и более быстрой, чем с акцентом, который они ставят на закрытие.
Ответы
Ответ 1
Здесь обновленное решение для 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
Это альтернативное решение без подкласса:
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?()
}
}
Он использует связанные объекты из среды выполнения Objective-C, чтобы добавить свойство закрытия/блокировки.
При установке он изменяет цель на себя и указывает селектор на новую функцию, которая вызывает замыкание, если она существует.
С этим в любой момент вы можете просто установить actionClosure
любого UIBarButtonItem
и ожидать, что все будет работать, вот пример:
let button = UIBarButtonItem(
barButtonSystemItem: .action,
target: nil, action: nil
)
button.actionClosure = {
print("hello")
}
Ответ 3
Сообщение о Reddit объясняет решение для этого с помощью настраиваемого компонента - https://www.reddit.com/r/swift/comments/3fjzap/creating_button_action_programatically_using
Чтобы использовать его, хотя я должен был добавить кнопку программно, а не через раскадровку. Вот как я это сделал.
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)!!!")
})
Ответ 4
К сожалению, Apple не может использовать инициализаторы. То, как это работает в фоновом режиме, - это отражение, и обеспечение закрытия - это нечто совершенно другое, которое в настоящее время не поддерживается.
Возможно, вы сможете создать собственное решение с некоторыми хаками или Apple может представить это в будущем.
Ответ 5
- Обновлено для Swift 5.
- Реализует все удобные инициализаторы, которые имеют цели.
- Добавляет
typealias
для сигнатуры функции для очистки синтаксиса. -
UIBarButtonItem
в обработчик действия в соответствии с соглашениями Cocoa.
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)
}
}