Мое приложение имеет множество кнопок, поведение которых зависит от событий, которые они генерируют: .touchDown
, .touchUpInside
, .touchUpOutside
и т. Д.
Я использую типичный механизм Target-Action для установки следующих режимов:
class MyViewController: UIViewController {
let redButton = UIButton()
let blueButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
self.redButton.addTarget(self, action: #selector(redButtonTouchDown(_:)), for: .touchDown)
self.redButton.addTarget(self, action: #selector(redButtonTouchUpInside(_:)), for: .touchUpInside)
self.redButton.addTarget(self, action: #selector(redButtonTouchUpOutside(_:)), for: .touchUpOutside)
self.blueButton.addTarget(self, action: #selector(blueButtonTouchDown(_:)), for: .touchDown)
self.blueButton.addTarget(self, action: #selector(blueButtonTouchUpInside(_:)), for: .touchUpInside)
self.blueButton.addTarget(self, action: #selector(blueButtonTouchUpOutside(_:)), for: .touchUpOutside)
}
@objc func redButtonTouchDown(_ sender: UIButton) {
print("redButton touchDown")
}
@objc func redButtonTouchUpInside(_ sender: UIButton) {
print("redButton touchUpInside")
}
@objc func redButtonTouchUpOutside(_ sender: UIButton) {
print("redButton touchUpOutside")
}
@objc func blueButtonTouchDown(_ sender: UIButton) {
print("blueButton touchDown")
}
@objc func blueButtonTouchUpInside(_ sender: UIButton) {
print("blueButton touchUpInside")
}
@objc func blueButtonTouchUpOutside(_ sender: UIButton) {
print("blueButton touchUpOutside")
}
}
Это очень многословно.У меня вопрос: есть ли более эффективный способ сделать это, не требуя отдельных функций для каждого типа событий?Вот как может выглядеть код:
class MyViewController: UIViewController {
let redButton = UIButton()
let blueButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
self.redButton.addActions(for: [.touchDown, .touchUpInside, .touchUpOutside], #selector(redButtonEvents(_:_:)))
self.blueButton.addActions(for: [.touchDown, .touchUpInside, .touchUpOutside], #selector(blueButtonEvents(_:_:)))
}
func redButtonEvents(_ sender: UIButton, _ event: UIControl.Event) {
switch event {
case .touchDown:
print("redButton touchDown")
case .touchUpInside:
print("redButton touchUpInside")
case .touchUpOutside:
print("redButton touchUpOutside")
default:
break
}
}
func blueButtonEvents(_ sender: UIButton, _ event: UIControl.Event) {
switch event {
case .touchDown:
print("blueButton touchDown")
case .touchUpInside:
print("blueButton touchUpInside")
case .touchUpOutside:
print("blueButton touchUpOutside")
default:
break
}
}
}
Я искал различные решения, которые не используют Selector
, а вместо этого заключают метод в замыкание.Боюсь, я еще не достаточно опытен в obj-c или Swift, чтобы понять, как выполнить рефакторинг моего кода, чтобы сделать это, а также слабо зафиксировать мою цель и объект, идентифицировать мою цель, устранить неоднозначность между отправителями и т. Д.
Согласно этому ответу: Добавление замыкания в качестве цели к UIButton
class ClosureSleeve {
let closure: ()->()
init (_ closure: @escaping ()->()) {
self.closure = closure
}
@objc func invoke () {
closure()
}
}
extension UIControl {
func addAction(for controlEvents: UIControl.Event, _ closure: @escaping ()->()) {
let sleeve = ClosureSleeve(closure)
addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
objc_setAssociatedObject(self, String(format: "[%d]", arc4random()), sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
Я стараюсь избегать постоянной необходимости писать что-то подобное, что также довольноverbose
class MyViewController: UIViewController {
let redButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
// this is also quite verbose
for event in [UIControl.Event]([.touchDown, .touchUpInside, .touchUpOutside]) {
self.button.addAction(for: event) { [weak self] in
if let strongSelf = self {
strongSelf.redButtonActions(strongSelf.button, event)
}
}
}
}
func redButtonActions(_ sender: UIButton, _ event: UIControl.Event) {
// handle red button actions
}
}
Помощь с благодарностью.Спасибо.