iOS
UIControl - Gestion des événements avec des blocs
Recherche…
introduction
Généralement, lorsque vous utilisez UIControl
ou UIButton
, nous ajoutons un selector
tant qu'action de rappel lorsqu'un événement se produit sur un bouton ou un contrôle, par exemple lorsque l'utilisateur appuie sur le bouton ou touche le contrôle.
Par exemple, nous ferions ce qui suit:
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")
}
}
En ce qui concerne le selector
, le compilateur doit seulement savoir qu'il existe. Cela peut être fait via un protocol
et ne pas être implémenté.
Par exemple, ce qui suit planterait votre application:
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()
}
}
C'est parce que votre application onButtonPress
PAS la fonction onButtonPress
.
Maintenant, si vous pouviez faire tout cela en même temps que l'initialisation du bouton? Que faire si vous n'aviez pas à spécifier de rappels et que vous pouviez spécifier des blocs pouvant être ajoutés et supprimés à tout moment? Pourquoi se soucier de la mise en œuvre des sélecteurs?
Solution
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
}
}
Ce qui précède est une simple extension sur UIControl
. Il ajoute une classe privée interne qui a une func run(_ control: UIControl)
rappel func run(_ control: UIControl)
utilisée comme action des événements.
Ensuite, nous utilisons l' object association
pour ajouter et supprimer la cible car elle ne sera pas conservée par UIControl
.
La fonction de gestionnaire d'événements renvoie un Protocol
afin de masquer le fonctionnement interne de la classe Target
, mais également pour vous enable
d' enable
et de disable
la cible à tout moment.
Exemple d'utilisation:
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()
}
}