我的应用程序有许多按钮,其行为取决于它们生成的事件:.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)
}
}
我试图避免经常写这样的东西,这也很冗长
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
}
}
非常感谢您的帮助。谢谢你。