iOS
UIControl - ブロックによるイベント処理
サーチ…
前書き
通常、 UIControl
またはUIButton
を使用する場合、ユーザーがボタンを押すか、コントロールに触れるなど、ボタンまたはコントロールでイベントが発生した場合のコールバックアクションとしてselector
を追加しUIButton
。
たとえば、次のようにします。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 44))
button.addTarget(self, action: #selector(self.onButtonPress(_:)), for: .touchUpInside)
self.view.addSubview(button)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func onButtonPress(_ button: UIButton!) {
print("PRESSED")
}
}
selector
に関しては、コンパイラはそれが存在することだけを知る必要があります。これはprotocol
を通して実行でき、実装されません。
たとえば、次のようにするとアプリケーションがクラッシュします。
import UIKit
@objc
protocol ButtonEvent {
@objc optional func onButtonPress(_ button: UIButton)
}
class ViewController: UIViewController, ButtonEvent {
@IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 44))
button.addTarget(self, action: #selector(ButtonEvent.onButtonPress(_:)), for: .touchUpInside)
self.view.addSubview(button)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
これは、アプリケーションがonButtonPress
関数を実装していないためです。
ボタンの初期化とともにこのすべてを行うことができたらどうなりますか?コールバックを指定する必要がなく、いつでも追加または削除できるブロックを指定することができますか?なぜセレクタの実装が心配ですか?
溶液
import Foundation
import UIKit
protocol RemovableTarget {
func enable();
func disable();
}
extension UIControl {
func addEventHandler(event: UIControlEvents, runnable: (control: UIControl) -> Void) -> RemovableTarget {
class Target : RemovableTarget {
private var event: UIControlEvents
private weak var control: UIControl?
private var runnable: (control: UIControl) -> Void
private init(event: UIControlEvents, control: UIControl, runnable: (control: UIControl) -> Void) {
self.event = event
self.control = control
self.runnable = runnable
}
@objc
private func run(_ control: UIControl) {
runnable(control: control)
}
private func enable() {
control?.addTarget(self, action: #selector(Target.run(_:)), for: event)
objc_setAssociatedObject(self, unsafeAddress(of: self), self, .OBJC_ASSOCIATION_RETAIN)
}
private func disable() {
control?.removeTarget(self, action: #selector(Target.run(_:)), for: self.event)
objc_setAssociatedObject(self, unsafeAddress(of: self), nil, .OBJC_ASSOCIATION_ASSIGN)
}
}
let target = Target(event: event, control: self, runnable: runnable)
target.enable()
return target
}
}
上記はUIControl
簡単な拡張UIControl
。これは、イベントのアクションとして使用されるコールバックfunc run(_ control: UIControl)
を持つ内部プライベートクラスを追加します。
次に、 UIControl
によって保持されないため、 object association
を使用してターゲットを追加および削除します。
イベントハンドラ関数は、 Target
クラスの内部動作を隠すためにProtocol
を返しますが、任意の時点でターゲットをenable
またはdisable
にすることもできます。
使用例:
import Foundation
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Create a button.
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 44))
//Add an event action block/listener -- Handles Button Press.
let target = button.addEventHandler(event: .touchUpInside) { (control) in
print("Pressed")
}
self.view.addSubview(button)
//Example of enabling/disabling the listener/event-action-block.
DispatchQueue.main.after(when: DispatchTime.now() + 5) {
target.disable() //Disable the listener.
DispatchQueue.main.after(when: DispatchTime.now() + 5) {
target.enable() //Enable the listener.
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow