Delegierte schnell?

132

Wie macht man einen Delegierten, dh NSUserNotificationCenterDelegateschnell?

user3718173
quelle
4
Meinen Sie damit, einen Delegierten zu implementieren oder einen eigenen Delegierten zu definieren?
Drawag

Antworten:

72

Es ist nicht so verschieden von obj-c. Zunächst müssen Sie das Protokoll in Ihrer Klassendeklaration wie folgt angeben:

class MyClass: NSUserNotificationCenterDelegate

Die Implementierung sieht folgendermaßen aus:

// NSUserNotificationCenterDelegate implementation
func userNotificationCenter(center: NSUserNotificationCenter, didDeliverNotification notification: NSUserNotification) {
    //implementation
}

func userNotificationCenter(center: NSUserNotificationCenter, didActivateNotification notification: NSUserNotification) {
    //implementation
}

func userNotificationCenter(center: NSUserNotificationCenter, shouldPresentNotification notification: NSUserNotification) -> Bool {
    //implementation
    return true
}

Natürlich müssen Sie den Delegaten einstellen. Beispielsweise:

NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self;
Adam
quelle
1
Was passiert, wenn Sie UIViewController erweitern möchten, z. B. in Objective-C. Sie können dies lügen lassen @interface MyCustomClass: UIViewController <ClassIWantToUseDelegate>, sodass Sie den Viewcontroller initiieren / konfigurieren und Delegierungsmethoden für die Unteransichten aufrufen können. Etwas ähnliches wie diese ?
Mahmud Ahmad
1
Hallo Adam, kurze Frage, wie kann ich delegate = self setzen, wenn ich ein Objekt nicht instanziieren kann, weil es eine generische Klasse ist, auf die ich in der anderen Klasse keinen Zugriff habe, aber ich möchte, dass die generische Klasse eine Funktion aufruft die andere Klasse, daher die Notwendigkeit eines Delegierten?
Marin
234

Hier ist eine kleine Hilfe für Delegierte zwischen zwei Ansichtscontrollern:

Schritt 1: Erstellen Sie im UIViewController ein Protokoll, das Sie entfernen / die Daten senden.

protocol FooTwoViewControllerDelegate:class {
    func myVCDidFinish(_ controller: FooTwoViewController, text: String)
}

Schritt 2: Deklarieren Sie den Delegaten in der sendenden Klasse (dh UIViewcontroller).

class FooTwoViewController: UIViewController {
    weak var delegate: FooTwoViewControllerDelegate?
    [snip...]
}

Schritt 3: Verwenden Sie den Delegaten in einer Klassenmethode, um die Daten an die Empfangsmethode zu senden. Hierbei handelt es sich um eine beliebige Methode, die das Protokoll übernimmt.

@IBAction func saveColor(_ sender: UIBarButtonItem) {
        delegate?.myVCDidFinish(self, text: colorLabel.text) //assuming the delegate is assigned otherwise error
}

Schritt 4: Übernehmen Sie das Protokoll in die empfangende Klasse

class ViewController: UIViewController, FooTwoViewControllerDelegate {

Schritt 5: Implementieren Sie die Delegate-Methode

func myVCDidFinish(_ controller: FooTwoViewController, text: String) {
    colorLabel.text = "The Color is " +  text
    controller.navigationController.popViewController(animated: true)
}

Schritt 6: Legen Sie den Delegaten in prepareForSegue fest:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "mySegue" {
        let vc = segue.destination as! FooTwoViewController
        vc.colorString = colorLabel.text
        vc.delegate = self
    }
}

Und das sollte funktionieren. Dies sind natürlich nur Codefragmente, sollten Ihnen aber die Idee geben. Für eine lange Erklärung dieses Codes können Sie hier zu meinem Blogeintrag gehen:

Segues und Delegierte

Wenn Sie daran interessiert sind, was mit einem Delegierten unter der Haube los ist, habe ich hier darüber geschrieben:

unter der Haube mit Delegierten

MakeAppPie
quelle
23
Schritt 2 sollte es keine schwache Referenz zum Delegieren geben? Wenn ich recht habe, bearbeiten Sie es bitte. Übrigens können Sie es als optionalen Wert festlegen. Das wäre schneller. schwacher var-Delegat: FooTwoViewControllerDelegate? PS: Delegierter sollte schwach sein, weil das Kind einen starken Bezug zum Elternteil hat
Shial
1
Auf meine Weise werden Sie den Fehler beim Auspacken beheben, wenn Sie das Delegieren optional machen. delegate? .myVCDidFinish Wird angezeigt, wenn delegate nicht festgelegt ist, wird der Kabeljau jetzt nicht ausgeführt :) In Ihrer Version wird versucht, ihn auszuführen, und es wird nicht entpackt, wenn delegate null ist und Sie es sind.
Shial
4
Sie müssen das Protokoll wie folgt deklarieren, um eine schwache Referenz für das Delegatenprotokoll FooTwoViewControllerDelegate: class {}
Kodierungsrhythmus
Könnten Sie bitte bei jedem Schritt festlegen, in welchem ​​VC Sie wie VC1 und VC2 sind. Ich bin mir nicht sicher, wo ich sie hinstellen soll.
Cing
2
@Shial - Eigentlich scheint es ein wenig kompliziert zu sein. weakwird nur für Klassen benötigt, nicht für Strukturen und Aufzählungen. Wenn der Delegat eine Struktur oder eine Aufzählung sein soll, müssen Sie sich keine Gedanken über Aufbewahrungszyklen machen. Der Delegat ist jedoch eine Klasse (dies gilt in vielen Fällen, da es sich häufig um einen ViewController handelt). Dann müssen weakSie Ihr Protokoll als Klasse deklarieren. Es gibt mehr Informationen hier stackoverflow.com/a/34566876/296446
Robert
94

Die Delegierten haben mich immer verwirrt, bis mir klar wurde, dass ein Delegierter nur eine Klasse ist, die für eine andere Klasse arbeitet . Es ist, als hätte jemand anderes die Drecksarbeit für Sie erledigt, die Sie nicht selbst erledigen möchten.

Ich habe eine kleine Geschichte geschrieben, um dies zu veranschaulichen. Lesen Sie es auf einem Spielplatz, wenn Sie möchten.

Es war einmal...

// MARK: Background to the story

// A protocol is like a list of rules that need to be followed.
protocol OlderSiblingDelegate: class {
    // The following command (ie, method) must be obeyed by any 
    // underling (ie, delegate) of the older sibling.
    func getYourNiceOlderSiblingAGlassOfWater()
}

// MARK: Characters in the story

class BossyBigBrother {
    
    // I can make whichever little sibling is around at 
    // the time be my delegate (ie, slave)
    weak var delegate: OlderSiblingDelegate?
    
    func tellSomebodyToGetMeSomeWater() {
        // The delegate is optional because even though 
        // I'm thirsty, there might not be anyone nearby 
        // that I can boss around.
        delegate?.getYourNiceOlderSiblingAGlassOfWater()
    }
}

// Poor little sisters have to follow (or at least acknowledge) 
// their older sibling's rules (ie, protocol)
class PoorLittleSister: OlderSiblingDelegate {

    func getYourNiceOlderSiblingAGlassOfWater() {
        // Little sis follows the letter of the law (ie, protocol),
        // but no one said exactly how she had to respond.
        print("Go get it yourself!")
    }
}

// MARK: The Story

// Big bro is laying on the couch watching basketball on TV.
let bigBro = BossyBigBrother()

// He has a little sister named Sally.
let sally = PoorLittleSister()

// Sally walks into the room. How convenient! Now big bro 
// has someone there to boss around.
bigBro.delegate = sally

// So he tells her to get him some water.
bigBro.tellSomebodyToGetMeSomeWater()

// Unfortunately no one lived happily ever after...

// The end.

Im Rückblick gibt es drei Schlüsselelemente für die Erstellung und Verwendung des Delegatenmusters.

  1. das Protokoll , das definiert, was der Mitarbeiter tun muss
  2. Die Boss-Klasse mit einer Delegatenvariablen, mit der sie der Worker-Klasse mitteilt, was zu tun ist
  3. Die Arbeiterklasse , die das Protokoll übernimmt und das tut, was erforderlich ist

Wahres Leben

Im Vergleich zu unserer obigen Bossy Big Brother-Geschichte werden Delegierte häufig für die folgenden praktischen Anwendungen verwendet:

  1. Kommunikation : Eine Klasse muss einige Informationen an eine andere Klasse senden.
  2. Anpassung : Eine Klasse möchte einer anderen Klasse erlauben, sie anzupassen.

Der große Teil ist, dass diese Klassen vorher nichts voneinander wissen müssen, außer dass die Delegatenklasse dem erforderlichen Protokoll entspricht.

Ich empfehle dringend, die folgenden zwei Artikel zu lesen. Sie haben mir geholfen, die Delegierten noch besser zu verstehen als die Dokumentation .

Noch eine Anmerkung

Delegaten, die auf andere Klassen verweisen, die sie nicht besitzen, sollten das weakSchlüsselwort verwenden, um starke Referenzzyklen zu vermeiden. Siehe diese Antwort für weitere Details.

Suragch
quelle
3
Endlich jemand, der das Protokoll erklären und mit gesundem Menschenverstand delegieren kann! Danke, Mann!
Engineeroholic
Was passiert, wenn Bossy Big Brother nicht weiß, dass er ein Bruder ist (Generika)?
Marin
@Marin, ich bin mir nicht sicher, ob ich deine Frage verstehe. Der Liste der Regeln (Protokoll) ist es egal, wer die Einhaltung der Regeln fordert oder wer die Regeln befolgt. Sie sind nur Regeln.
Suragch
Grundsätzlich beziehe ich mich auf meine Frage, die hier etwas vereinfacht ist. stackoverflow.com/questions/41195203/…
Marin
47

Ich habe einige Korrekturen für @MakeAppPie erhalten

Wenn Sie ein Delegatenprotokoll erstellen, sollte es zunächst dem Klassenprotokoll entsprechen. Wie im folgenden Beispiel.

protocol ProtocolDelegate: class {
    func myMethod(controller:ViewController, text:String)
}

Zweitens sollte Ihr Delegierter schwach sein, um einen Aufbewahrungszyklus zu vermeiden.

class ViewController: UIViewController {
    weak var delegate: ProtocolDelegate?
}

Zuletzt sind Sie sicher, da Ihr Protokoll ein optionaler Wert ist. Das bedeutet, dass die Nachricht "Null" nicht an diese Eigenschaft gesendet wird. Es ähnelt der bedingten Anweisung mit respondToselectorin objC, aber hier haben Sie alles in einer Zeile:

if ([self.delegate respondsToSelector:@selector(myMethod:text:)]) {
    [self.delegate myMethod:self text:@"you Text"];
}

Oben haben Sie ein obj-C-Beispiel und unten haben Sie ein Swift-Beispiel dafür, wie es aussieht.

delegate?.myMethod(self, text:"your Text")
Shial
quelle
Sie sind sicher, weil Ihr Protokoll ein optionaler Wert ist ..... weil Sie eine optionale Verkettung verwenden delegate?.myMethod, stürzt dies nicht ab, denn wenn der Delegat ist nil, würde nichts passieren. Allerdings , wenn Sie Fehler gemacht und schrieb delegate!.myMethodman könnte abstürzen , wenn ein Delegierter nicht gesetzt ist, so ist es im Grunde eine Möglichkeit für Sie , um sicher zu sein ...
Honig
32

Hier ist ein Kern, den ich zusammengestellt habe. Ich habe mich das auch gefragt und dies hat mir geholfen, mein Verständnis zu verbessern. Öffne dies auf einem Xcode-Spielplatz, um zu sehen, was los ist.

protocol YelpRequestDelegate {
    func getYelpData() -> AnyObject
    func processYelpData(data: NSData) -> NSData
}

class YelpAPI {
    var delegate: YelpRequestDelegate?

    func getData() {
        println("data being retrieved...")
        let data: AnyObject? = delegate?.getYelpData()
    }

    func processYelpData(data: NSData) {
        println("data being processed...")
        let data = delegate?.processYelpData(data)
    }
}

class Controller: YelpRequestDelegate {
    init() {
        var yelpAPI = YelpAPI()
        yelpAPI.delegate = self
        yelpAPI.getData()
    }
    func getYelpData() -> AnyObject {
        println("getYelpData called")
        return NSData()
    }
    func processYelpData(data: NSData) -> NSData {
        println("processYelpData called")
        return NSData()
    }
}

var controller = Controller()
SeeMeCode
quelle
Ich liebe das. Sehr hilfreich
Aspen
@SeeMeCode Hallo, es war zunächst ein gutes Beispiel, aber ich habe immer noch ein Problem. Wie kann ich meine UIViewControllerKlasse dazu bringen, den von uns erstellten Delegierten zu entsprechen? Müssen sie in einer schnellen Datei deklariert werden? Jede Hilfe wird viel bedeuten.
Faruk
@Faruk Es ist schon eine Weile her, seit ich das gepostet habe, aber ich denke, was du fragst, sollte ziemlich einfach sein (Wenn ich falsch verstehe, entschuldige ich mich). Fügen Sie den Delegaten einfach nach dem Doppelpunkt zu Ihrem UIViewController hinzu. Also so etwas wie class ViewController : UIViewController NameOfDelegate.
SeeMeCode
@SeeMeCode ja, du hast meine Frage gut verstanden. Ich habe Ihren Vorschlag übrigens ausprobiert, aber wenn ich eine Delegatenklasse a.swiftgemäß Ihrer obigen Antwort erstelle , wird sie nicht angezeigt b.swift. Ich kann keine Klasse außerhalb meiner schnellen Datei erreichen. irgendwelche Schwierigkeiten?
Faruk
Eine Sache, die ich nicht verstehe, ist, warum ich eine neue Instanz von YelpApi erstellen sollte, nur damit ich den Delegierten von YelpApi anrufe. Was ist, wenn sich die ausgeführte Instanz von der gerade erstellten 'neuen' unterscheidet? Woher weiß sie, welcher Delegat zu welcher Instanz von YelpApi gehört?
Marin
15

DELEGATE IN SWIFT 2

Ich erkläre anhand eines Beispiels für Delegate mit zwei viewControllern. In diesem Fall sendet SecondVC Object Daten an den ersten View Controller zurück.

Klasse mit Protokollerklärung

protocol  getDataDelegate  {
    func getDataFromAnotherVC(temp: String)
}


import UIKit
class SecondVC: UIViewController {

    var delegateCustom : getDataDelegate?
    override func viewDidLoad() {
        super.viewDidLoad()
     }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    @IBAction func backToMainVC(sender: AnyObject) {
      //calling method defined in first View Controller with Object  
      self.delegateCustom?.getDataFromAnotherVC("I am sending data from second controller to first view controller.Its my first delegate example. I am done with custom delegates.")
        self.navigationController?.popViewControllerAnimated(true)
    }

}

Im First ViewController-Protokoll wird hier Folgendes angepasst:

class ViewController: UIViewController, getDataDelegate

Definition der Protokollmethode in First View Controller (ViewController)

func getDataFromAnotherVC(temp : String)
{
  // dataString from SecondVC
   lblForData.text = dataString
}

Während des Pushs der SecondVC vom First View Controller (ViewController)

let objectPush = SecondVC()
objectPush.delegateCustom = self
self.navigationController.pushViewController(objectPush, animated: true)
Maninderjit Singh
quelle
Ihre letzten drei Zeilen haben mir geholfen, mein Szenario zu verstehen und mein Problem zu lösen. Danke, Mann! :)
iHarshil
6

Erste Klasse:

protocol NetworkServiceDelegate: class {

    func didCompleteRequest(result: String)
}


class NetworkService: NSObject {

    weak var delegate: NetworkServiceDelegate?

    func fetchDataFromURL(url : String) {
        delegate?.didCompleteRequest(url)
    }
}

Zweite Klasse:

class ViewController: UIViewController, NetworkServiceDelegate {

    let network = NetworkService()

    override func viewDidLoad() {
        super.viewDidLoad()
        network.delegate = self
        network.fetchDataFromURL("Success!")
    }



    func didCompleteRequest(result: String) {
        print(result)
    }


}
Ekambaram E.
quelle
Beim Kompilieren des obigen Codes wird der Fehler Type 'ViewController' does not conform to protocol 'NetworkServiceDelegate'angezeigt. Es ist mein 6. Tag auf Swift :)
Vaibhav Saran
4

Sehr einfach Schritt für Schritt (100% arbeiten und getestet)

Schritt 1: Erstellen Sie eine Methode auf dem First View Controller

 func updateProcessStatus(isCompleted : Bool){
    if isCompleted{
        self.labelStatus.text = "Process is completed"
    }else{
        self.labelStatus.text = "Process is in progress"
    }
}

Schritt 2: Legen Sie den Delegaten fest, während Sie auf den Controller für die zweite Ansicht drücken

@IBAction func buttonAction(_ sender: Any) {

    let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
    secondViewController.delegate = self
    self.navigationController?.pushViewController(secondViewController, animated: true)
}

Schritt 3: Delegate wie einstellen

Klasse ViewController: UIViewController, ProcessStatusDelegate {

Schritt 4: Protokoll erstellen

protocol ProcessStatusDelegate:NSObjectProtocol{
func updateProcessStatus(isCompleted : Bool)
}

Schritt 5: Nehmen Sie eine Variable

var delegate:ProcessStatusDelegate?

Schritt 6: Während Sie zur vorherigen View Controller Call Delegate-Methode zurückkehren, benachrichtigen Sie zuerst den View Controller mit Daten

@IBAction func buttonActionBack(_ sender: Any) {
    delegate?.updateProcessStatus(isCompleted: true)
    self.navigationController?.popViewController(animated: true)
}

@IBAction func buttonProgress(_ sender: Any) {
    delegate?.updateProcessStatus(isCompleted: false)
    self.navigationController?.popViewController(animated: true)

}
Mr.Javed Multani
quelle
3

Einfaches Beispiel:

protocol Work: class {
    func doSomething()
}

class Manager {
    weak var delegate: Work?
    func passAlong() {
        delegate?.doSomething()
    }
}

class Employee: Work {
    func doSomething() {
        print("Working on it")
    }
}

let manager = Manager()
let developer = Employee()
manager.delegate = developer
manager.passAlong() // PRINTS: Working on it
Bobby
quelle
Warum verwenden Sie das Schlüsselwort "Klasse" in der Protokollbeschreibung? Was ist der Unterschied, wenn man es benutzt und nicht benutzt?
Vlad
2
Das Schlüsselwort class bedeutet, dass es sich um ein Protokoll nur für Klassen handelt. Sie können die Protokollübernahme auf Klassentypen und nicht auf Strukturen oder Aufzählungen beschränken, indem Sie das Schlüsselwort class hinzufügen. Ich hätte es wahrscheinlich nicht hinzufügen sollen, um Verwirrung zu vermeiden, aber da Sie gefragt haben, werde ich es behalten.
Bobby
2

Delegaten sind ein Entwurfsmuster, mit dem ein Objekt Nachrichten an ein anderes Objekt senden kann, wenn ein bestimmtes Ereignis eintritt. Stellen Sie sich vor, ein Objekt A ruft ein Objekt B auf, um eine Aktion auszuführen. Sobald die Aktion abgeschlossen ist, sollte Objekt A wissen, dass B die Aufgabe abgeschlossen hat, und die erforderlichen Maßnahmen ergreifen. Dies kann mit Hilfe von Delegierten erreicht werden! Hier ist ein Tutorial, in dem Delegierte Schritt für Schritt in Swift 3 implementiert werden

Tutorial Link

James Rochabrun
quelle
0

Die oben genannten Lösungen schienen ein wenig gekoppelt zu sein und gleichzeitig zu vermeiden, dass dasselbe Protokoll in anderen Controllern wiederverwendet wird. Deshalb habe ich eine Lösung gefunden, die mit generischer Typlöschung stärker typisiert ist.

@noreturn public func notImplemented(){
    fatalError("not implemented yet")
}


public protocol DataChangedProtocol: class{
    typealias DataType

    func onChange(t:DataType)
}

class AbstractDataChangedWrapper<DataType> : DataChangedProtocol{

    func onChange(t: DataType) {
        notImplemented()
    }
}


class AnyDataChangedWrapper<T: DataChangedProtocol> : AbstractDataChangedWrapper<T.DataType>{

    var base: T

    init(_ base: T ){
        self.base = base
    }

    override func onChange(t: T.DataType) {
        base.onChange(t)
    }
}


class AnyDataChangedProtocol<DataType> : DataChangedProtocol{

    var base: AbstractDataChangedWrapper<DataType>

    init<S: DataChangedProtocol where S.DataType == DataType>(_ s: S){
        self.base = AnyDataChangedWrapper(s)
    }

    func onChange(t: DataType) {
        base.onChange(t)
    }
}



class Source : DataChangedProtocol {
    func onChange(data: String) {
        print( "got new value \(data)" )
    }
}


class Target {
    var delegate: AnyDataChangedProtocol<String>?

    func reportChange(data:String ){
        delegate?.onChange(data)
    }
}


var source = Source()
var target = Target()

target.delegate = AnyDataChangedProtocol(source)
target.reportChange("newValue")    

Ausgabe : hat neuen Wert newValue

Jans
quelle
Ich bin daran interessiert, mehr darüber zu erfahren. Können Sie mehr über die von Ihnen verwendeten Begriffe erklären: gekoppelt, "Vermeiden Sie die Wiederverwendung des gleichen Protokolls", "generische Typlöschung". Warum ist es so wichtig, es so zu abstrahieren? Sollte man das immer tun?
Suragch
0

In schnellem 4.0

Erstellen Sie einen Delegaten für eine Klasse, der Daten senden oder Funktionen für andere Klassen bereitstellen muss

Mögen

protocol GetGameStatus {
    var score: score { get }
    func getPlayerDetails()
}

Danach in der Klasse, die diesem Delegierten bestätigen wird

class SnakesAndLadders: GetGameStatus {
    func getPlayerDetails() {

 }
}
Saranjith
quelle