iOS
UIControl - Händelsehantering med block
Sök…
Introduktion
När du använder UIControl eller UIButton lägger vi UIButton till en selector som en återuppringning för när en händelse inträffar på en knapp eller en kontroll, till exempel att användaren trycker på knappen eller trycker på kontrollen.
Till exempel skulle vi göra följande:
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")
}
}
När det gäller selector behöver kompilatorn bara veta att den finns .. Detta kan göras genom ett protocol och inte implementeras.
Till exempel skulle följande krascha din ansökan:
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()
}
}
Detta beror på att din applikation INTE implementerar onButtonPress funktionen.
Vad händer nu om du kunde göra allt detta tillsammans med initialiseringen av knappen? Vad händer om du inte behövde ange återuppringningar och istället kunde specificera block som kan läggas till och tas bort när som helst? Varför oroa dig för att implementera väljare?
Lösning
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
}
}
Ovanstående är en enkel förlängning på UIControl . Det lägger till en inre privatklass som har en återuppringning av func run(_ control: UIControl) som används som händelsens handling.
Därefter använder vi object association att lägga till och ta bort målet eftersom det inte kommer att behållas av UIControl .
Eventhanteringsfunktionen returnerar ett Protocol för att dölja den inre funktionen i Target men också för att du ska kunna enable och disable målet vid en viss tidpunkt.
Exempel på användning:
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()
}
}