Delegati in rapida?


132

Come si fa a fare un delegato, cioè NSUserNotificationCenterDelegatein fretta?


4
Intendi implementare un delegato o definire il tuo delegato?
Drewag,

Risposte:


72

Non è così diverso da obj-c. Innanzitutto, devi specificare il protocollo nella dichiarazione di classe, come il seguente:

class MyClass: NSUserNotificationCenterDelegate

L'implementazione sarà simile alla seguente:

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

Certo, devi impostare il delegato. Per esempio:

NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self;

1
Cosa succede quando si desidera estendere UIViewController, ad esempio, in Object-C, si può avere qualcosa di simile a ciò @interface MyCustomClass: UIViewController <ClassIWantToUseDelegate>, che consente di avviare / configurare il viewcontroller, nonché di chiamare i metodi delegati nelle viste secondarie? Qualcosa di simile a questo ?
Mahmud Ahmad,

1
Ciao Adam, domanda veloce, come posso impostare delegate = self, se non riesco a creare un'istanza di un oggetto perché è una classe generica a cui non ho accesso nell'altra classe, ma voglio che la classe generics chiami una funzione in l'altra classe, quindi la necessità di delegare?
Marin,

234

Ecco un piccolo aiuto sui delegati tra due controller di visualizzazione:

Passaggio 1: crea un protocollo in UIViewController che rimuoverai / invierai i dati.

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

Step2: Dichiara il delegato nella classe di invio (es. UIViewcontroller)

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

Passaggio 3: utilizzare il delegato in un metodo di classe per inviare i dati al metodo di ricezione, ovvero qualsiasi metodo che adotti il ​​protocollo.

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

Passaggio 4: adottare il protocollo nella classe di ricezione

class ViewController: UIViewController, FooTwoViewControllerDelegate {

Passaggio 5: implementare il metodo delegato

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

Passaggio 6: impostare il delegato nel preparForSegue:

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

E dovrebbe funzionare. Questo ovviamente è solo frammenti di codice, ma dovrebbe darti l'idea. Per una lunga spiegazione di questo codice puoi andare al mio blog qui:

segues e delegati

Se sei interessato a quello che succede sotto il cofano con un delegato, l'ho scritto qui:

sotto il cofano con i delegati


23
Step2 non dovrebbe esserci un debole riferimento al delegato? se ho ragione, per favore, modificalo. Tra l'altro puoi renderlo valore opzionale. Sarebbe più rapido. delegato var debole: FooTwoViewControllerDelegate? PS: il delegato dovrebbe essere debole cus del mantenimento del cerchio, il bambino non dovrebbe tenere un forte riferimento al genitore
Shial

1
A mio modo, quando renderai il delegato facoltativo, risolverai il tuo errore di scartamento. delegate? .myVCDidFinish Perché se il delegato non è impostato, il merluzzo non verrà eseguito ora :) Nella tua versione tenterà di eseguire e non riuscirà a scartarlo se il delegato è zero e tu lo è.
Shial,

4
è necessario dichiarare un protocollo come questo al fine di rendere possibile un riferimento debole per il protocollo delegato FooTwoViewControllerDelegate: class {}
codingrhythm

Potresti impostare ogni passaggio in cui VC sei come VC1 e VC2. Non sono sicuro di dove metterli.
Cing,

2
@Shial - In realtà sembra essere un po 'complicato. weakè necessario solo per le classi non per strutture ed enumerazioni. Se il delegato diventerà una struttura o un enum allora non dovrai preoccuparti dei cicli di mantenimento. Tuttavia, il delegato è una classe (questo è vero per molti casi poiché abbastanza spesso è un ViewController), quindi è necessario weakma è necessario dichiarare il protocollo come classe. Maggiori informazioni qui stackoverflow.com/a/34566876/296446
Robert,

94

I delegati mi hanno sempre confuso fino a quando ho capito che un delegato è solo una classe che fa un po 'di lavoro per un'altra classe . È come avere qualcun altro lì a fare tutto il lavoro sporco per te che non vuoi fare da solo.

Ho scritto una piccola storia per illustrare questo. Leggilo in un parco giochi se vuoi.

C'era una volta...

// 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.

In revisione, ci sono tre parti chiave per creare e usare il modello delegato.

  1. il protocollo che definisce ciò che il lavoratore deve fare
  2. la classe boss che ha una variabile delegata, che utilizza per dire alla classe lavoratore cosa fare
  3. la classe operaia che adotta il protocollo e fa ciò che è richiesto

Vita reale

Rispetto alla nostra storia Bossy Big Brother sopra, i delegati sono spesso usati per le seguenti applicazioni pratiche:

  1. Comunicazione : una classe deve inviare alcune informazioni a un'altra classe.
  2. Personalizzazione : una classe vuole consentire a un'altra classe di personalizzarla.

La parte migliore è che queste classi non hanno bisogno di conoscersi prima dell'altro, tranne che la classe delegata è conforme al protocollo richiesto.

Consiglio vivamente di leggere i seguenti due articoli. Mi hanno aiutato a capire i delegati anche meglio della documentazione .

Un'altra nota

I delegati che fanno riferimento ad altre classi che non possiedono dovrebbero usare la weakparola chiave per evitare cicli di riferimento forti. Vedi questa risposta per maggiori dettagli.


3
Finalmente qualcuno che sa spiegare il protocollo e delegare con buon senso! grazie uomo!
Engineeroholic

Cosa succede quando Bossy Big Brother non sa di essere un fratello (Generics)?
Marin,

@Marin, non sono proprio sicuro di aver capito la tua domanda. L'elenco delle regole (protocollo) non si preoccupa di chi è che richiede il rispetto delle regole o di chi le segue. Sono solo regole.
Suragch il

Fondamentalmente mi riferisco alla mia domanda, leggermente semplificata qui. stackoverflow.com/questions/41195203/…
Marin

47

Ho ricevuto alcune correzioni per pubblicare @MakeAppPie

Prima di tutto quando si crea un protocollo delegato, dovrebbe essere conforme al protocollo di classe. Come nell'esempio seguente.

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

In secondo luogo, il delegato dovrebbe essere debole per evitare il ciclo di mantenimento.

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

Infine, sei al sicuro perché il tuo protocollo è un valore opzionale. Ciò significa che il suo messaggio "zero" non verrà inviato a questa proprietà. È simile all'istruzione condizionale con respondToselectorin objC ma qui hai tutto in una riga:

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

Sopra hai un esempio obj-C e sotto hai un esempio Swift di come appare.

delegate?.myMethod(self, text:"your Text")

sei sicuro perché il tuo protocollo è un valore opzionale ..... perché usi il concatenamento opzionale delegate?.myMethodnon si arresta in modo anomalo perché se il delegato è nilallora non succederebbe nulla. Tuttavia, se hai commesso un errore e delegate!.myMethodhai scritto che potresti bloccarti se non è stato impostato un delegato, quindi in pratica è un modo per essere al sicuro ...
Miele

33

Ecco un riassunto che ho messo insieme. Mi chiedevo lo stesso e questo mi ha aiutato a migliorare la mia comprensione. Apri questo in un Xcode Playground per vedere cosa sta succedendo.

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

Ama questo. Molto utile
Aspen,

@SeeMeCode Ciao, innanzitutto è stato un buon esempio, ma ho ancora un problema. Come posso fare in modo che ogni mia UIViewControllerclasse sia conforme al delegato che abbiamo creato? Devono essere dichiarati in un unico file rapido? Qualsiasi aiuto significherà molto.
Faruk,

@Faruk È passato un po 'di tempo da quando l'ho pubblicato, ma penso che ciò che mi stai chiedendo dovrebbe essere piuttosto semplice (se ho frainteso, mi scuso). Aggiungi il delegato al tuo UIViewController dopo i due punti. Quindi qualcosa del genere class ViewController : UIViewController NameOfDelegate.
SeeMeCode

@ Vedi Codice Sì, hai capito bene la mia domanda. Ho provato il tuo suggerimento tra l'altro, ma quando creo una classe delegata in a.swiftbase alla tua risposta sopra, non compare b.swift. Non riesco a raggiungere alcuna classe al di fuori del mio file rapido. qualche duro?
Faruk,

una cosa che non capisco è perché dovrei creare una nuova istanza di YelpApi solo per chiamare il delegato di YelpApi? Cosa succede se l'istanza in esecuzione è diversa da quella "nuova" che ho appena creato ... come fa a sapere quale delegato appartiene a quale istanza di YelpApi?
Marin,

15

DELEGATI IN SWIFT 2

Sto spiegando con l'esempio di Delegato con due viewController. In questo caso, SecondVC Object restituisce i dati al primo View Controller.

Classe con dichiarazione del protocollo

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

}

Nel protocollo First ViewController la conformità viene eseguita qui:

class ViewController: UIViewController, getDataDelegate

Definizione del metodo di protocollo in First View Controller (ViewController)

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

Durante il push del SecondVC dal controller First View (ViewController)

let objectPush = SecondVC()
objectPush.delegateCustom = self
self.navigationController.pushViewController(objectPush, animated: true)

Le tue ultime 3 righe mi hanno aiutato a capire il mio scenario e risolto il mio problema. Grazie uomo! :)
iHarshil il

6

Prima classe:

protocol NetworkServiceDelegate: class {

    func didCompleteRequest(result: String)
}


class NetworkService: NSObject {

    weak var delegate: NetworkServiceDelegate?

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

Seconda classe:

class ViewController: UIViewController, NetworkServiceDelegate {

    let network = NetworkService()

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



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


}

durante la compilazione sopra il codice mostra l'errore per cui Type 'ViewController' does not conform to protocol 'NetworkServiceDelegate'suggerisco. È il mio sesto giorno in fretta :)
Vaibhav Saran,

4

Step by step molto semplice (funzionante e testato al 100%)

step1: crea il metodo sul controller della prima vista

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

step2: imposta il delegato mentre premi il controller della seconda vista

@IBAction func buttonAction(_ sender: Any) {

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

step3: imposta il delegato come

class ViewController: UIViewController, ProcessStatusDelegate {

step4: Crea protocollo

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

step5: prendere una variabile

var delegate:ProcessStatusDelegate?

step6: Mentre torni alla visualizzazione precedente, chiama il metodo delegato, quindi prima controlla il controller con i dati

@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)

}

3

Esempio semplice:

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

perché usi la parola chiave "class" nella descrizione del protocollo? qual è la differenza nell'usarlo e nel non usarlo?
Vlad,

2
La parola chiave class significa che è un protocollo solo di classe. È possibile limitare l'adozione del protocollo ai tipi di classe e non alle strutture o alle enumerazioni, aggiungendo la parola chiave class. Probabilmente non avrei dovuto aggiungerlo per evitare confusione, ma dato che mi hai chiesto che continuerò.
Bobby,

2

I delegati sono un modello di progettazione che consente a un oggetto di inviare messaggi a un altro oggetto quando si verifica un evento specifico. Immagina un oggetto A chiama un oggetto B per eseguire un'azione. Una volta completata l'azione, l'oggetto A dovrebbe sapere che B ha completato l'attività e intraprendere le azioni necessarie, ciò può essere realizzato con l'aiuto dei delegati! Ecco un tutorial che implementa i delegati passo dopo passo in swift 3

Link tutorial


0

Le soluzioni di cui sopra sembravano un po 'accoppiate e allo stesso tempo evitano di riutilizzare lo stesso protocollo in altri controller, ecco perché sono arrivato con la soluzione più tipizzata usando la cancellazione generica del tipo.

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

output : ottenuto nuovo valore newValue


Sono interessato a saperne di più su questo. Puoi spiegarci di più sui termini che usi: accoppiati, "evita di riutilizzare lo stesso protocollo", "cancellazione di tipi generici". Perché astrarlo è così importante? Si dovrebbe sempre fare questo?
Suragch,

0

In rapido 4.0

Creare un delegato sulla classe che deve inviare alcuni dati o fornire alcune funzionalità ad altre classi

Piace

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

Dopo quello nella classe che sta per confermare a questo delegato

class SnakesAndLadders: GetGameStatus {
    func getPlayerDetails() {

 }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.