私のアプリには、生成するイベントに応じて動作が異なる多数のボタンがあります: .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
}
}
助けていただければ幸いです。ありがとうございました。