Suche…


Einführung

Normalerweise fügen wir bei Verwendung von UIControl oder UIButton einen selector als UIControl UIButton , wenn ein Ereignis auf einer Schaltfläche oder einem Steuerelement auftritt, z.

Zum Beispiel würden wir Folgendes tun:

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")
    }
}

Wenn es um selector , muss der Compiler nur wissen, dass er existiert. Dies kann über ein protocol und nicht implementiert werden.

Zum Beispiel würde die folgende Anwendung Ihre Anwendung zum Absturz bringen:

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()
    }
}

Dies liegt daran, dass Ihre Anwendung die onButtonPress Funktion NICHT implementiert.

Was wäre, wenn Sie all dies neben der Initialisierung der Schaltfläche tun könnten? Was wäre, wenn Sie keine Rückrufe angeben müssten und stattdessen Blöcke angeben könnten, die jederzeit hinzugefügt und entfernt werden können? Warum sollten Sie sich Gedanken über die Implementierung von Selektoren machen?


Lösung

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
    }
}

Das obige ist eine einfache Erweiterung von UIControl . Es fügt eine innere private Klasse hinzu, die über eine Callback-Funktion func run(_ control: UIControl) , die als Ereignisaktion verwendet wird.

Als Nächstes verwenden wir die object association , um das Ziel hinzuzufügen und zu entfernen, da es von UIControl nicht beibehalten wird.

Die Event-Handler-Funktion gibt ein Protocol zurück, um die inneren Abläufe der Target Klasse auszublenden, aber auch, disable das Target jederzeit enable und disable zu können.


Verwendungsbeispiel:

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow