Délégués en vitesse?


132

Comment fait-on pour faire un délégué, c'est- NSUserNotificationCenterDelegateà- dire rapidement?


4
Voulez-vous dire implémenter un délégué ou définir votre propre délégué?
tirage le

Réponses:


72

Ce n'est pas si différent de obj-c. Tout d'abord, vous devez spécifier le protocole dans votre déclaration de classe, comme suit:

class MyClass: NSUserNotificationCenterDelegate

La mise en œuvre ressemblera à ceci:

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

Bien sûr, vous devez définir le délégué. Par exemple:

NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self;

1
Que se passe-t-il lorsque vous souhaitez étendre UIViewController, par exemple, dans objective-c, vous pouvez avoir quelque chose à cela @interface MyCustomClass: UIViewController <ClassIWantToUseDelegate>, vous permettant d'initier / configurer le viewcontroller, ainsi que d'appeler des méthodes de délégué sur les sous-vues? Quelque chose de semblable à ce ?
Mahmud Ahmad

1
Bonjour Adam, question rapide, comment puis-je définir delegate = self, si je ne peux pas instancier un objet car il s'agit d'une classe générique à laquelle je n'ai pas accès dans l'autre classe, mais je veux que la classe générique appelle une fonction dans l'autre classe, d'où le besoin de délégué?
Marin du

234

Voici une petite aide sur les délégués entre deux contrôleurs de vue:

Étape 1: Créez un protocole dans l'UIViewController que vous supprimerez / enverrez les données.

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

Étape 2: Déclarez le délégué dans la classe d'envoi (c'est-à-dire UIViewcontroller)

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

Étape 3: utilisez le délégué dans une méthode de classe pour envoyer les données à la méthode de réception, qui est toute méthode qui adopte le protocole.

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

Étape 4: Adoptez le protocole dans la classe de réception

class ViewController: UIViewController, FooTwoViewControllerDelegate {

Étape 5: implémenter la méthode déléguée

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

Étape 6: définissez le délégué dans prepareForSegue:

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

Et cela devrait fonctionner. Il ne s'agit bien sûr que de fragments de code, mais cela devrait vous donner une idée. Pour une longue explication de ce code, vous pouvez consulter mon article de blog ici:

segues et délégués

Si vous êtes intéressé par ce qui se passe sous le capot avec un délégué, j'ai écrit à ce sujet ici:

sous le capot avec des délégués


23
Step2 ne devrait-il pas y avoir une faible référence à déléguer? si j'ai raison, veuillez le modifier. Btw, vous pouvez en faire une valeur facultative. Ce serait plus rapide. délégué var faible: FooTwoViewControllerDelegate? PS: le délégué devrait être faible en raison du cercle de rétention, l'enfant devrait garder une forte référence au parent
Shial

1
À ma manière, lorsque vous rendrez le délégué facultatif, vous résoudrez votre erreur de déballage. Delegate? .myVCDidFinish Prenez garde si le délégué n'est pas défini, le cod ne s'exécutera pas maintenant :) Dans votre version, il essaiera de s'exécuter et échouera à se dérouler si le délégué est nul et vous l'est.
Shial

4
vous devez déclarer un protocole comme celui-ci afin de rendre possible une référence faible pour le protocole délégué FooTwoViewControllerDelegate: class {}
codingrhythm

Pourriez-vous s'il vous plaît définir par chaque étape dans laquelle VC vous êtes comme VC1 et VC2. Je ne sais pas vraiment où les mettre.
Cing

2
@Shial - En fait, cela semble être un peu compliqué. weakn'est nécessaire que pour les classes et non pour les structures et les énumérations. Si le délégué doit être une structure ou une énumération, vous n'avez pas à vous soucier des cycles de conservation. Cependant, le délégué est une classe (cela est vrai dans de nombreux cas car il s'agit souvent d'un ViewController), alors vous avez besoin weakmais vous devez déclarer votre protocole en tant que classe. Il y a plus d'informations ici stackoverflow.com/a/34566876/296446
Robert

94

Les délégués m'ont toujours confondu jusqu'à ce que je réalise qu'un délégué n'est qu'une classe qui fait du travail pour une autre classe . C'est comme avoir quelqu'un d'autre là pour faire tout le sale boulot que vous ne voulez pas faire vous-même.

J'ai écrit une petite histoire pour illustrer cela. Lisez-le dans un Playground si vous le souhaitez.

Il était une fois...

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

En revue, il y a trois parties clés pour créer et utiliser le modèle de délégué.

  1. le protocole qui définit ce que le travailleur doit faire
  2. la classe de boss qui a une variable déléguée, qu'elle utilise pour indiquer à la classe de travail ce qu'elle doit faire
  3. la classe de travail qui adopte le protocole et fait ce qui est requis

Vrai vie

Par rapport à notre histoire Bossy Big Brother ci-dessus, les délégués sont souvent utilisés pour les applications pratiques suivantes:

  1. Communication : une classe doit envoyer des informations à une autre classe.
  2. Personnalisation : une classe veut permettre à une autre classe de la personnaliser.

La grande partie est que ces classes n'ont pas besoin de se connaître au préalable, sauf que la classe déléguée est conforme au protocole requis.

Je recommande vivement la lecture des deux articles suivants. Ils m'ont aidé à comprendre les délégués encore mieux que la documentation .

Encore une note

Les délégués qui référencent d'autres classes qu'ils ne possèdent pas doivent utiliser le weakmot - clé pour éviter des cycles de référence forts. Voir cette réponse pour plus de détails.


3
Enfin quelqu'un qui peut expliquer le protocole et déléguer avec bon sens! Merci mec!
Engineeroholic

Que se passe-t-il quand Bossy Big Brother ne sait pas qu'il est un frère (Generics)?
Marin

@Marin, je ne suis pas vraiment sûr de comprendre votre question. La liste des règles (protocole) ne se soucie pas de savoir qui appelle les règles à suivre ou qui suit les règles. Ce ne sont que des règles.
Suragch

En gros, je fais référence à ma question, un peu simplifiée ici. stackoverflow.com/questions/41195203/…
Marin

47

J'ai quelques corrections à publier sur @MakeAppPie

Tout d'abord, lorsque vous créez un protocole délégué, il doit être conforme au protocole de classe. Comme dans l'exemple ci-dessous.

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

Deuxièmement, votre délégué doit être faible pour éviter le cycle de rétention.

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

Enfin, vous êtes en sécurité car votre protocole est une valeur facultative. Cela signifie que son message "nil" ne sera pas envoyé à cette propriété. C'est similaire à l'instruction conditionnelle avec respondToselectordans objC mais ici vous avez tout sur une seule ligne:

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

Ci-dessus, vous avez un exemple obj-C et ci-dessous vous avez un exemple Swift de son apparence.

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

vous êtes en sécurité parce que votre protocole est une valeur facultative ..... parce que vous utilisez le chaînage facultatif delegate?.myMethodne plantera pas parce que si le délégué est nilalors rien ne se passerait. Toutefois , si vous avez fait erreur et écrit que delegate!.myMethodvous pourriez tomber en panne si un délégué n'est pas défini, il est donc essentiellement un moyen pour vous d'être en sécurité ...
Chérie

33

Voici l' essentiel que j'ai mis en place. Je me demandais la même chose et cela a aidé à améliorer ma compréhension. Ouvrez-le dans un Xcode Playground pour voir ce qui se passe.

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

Aime ça. Très utile
Aspen

@SeeMeCode Salut, C'était d'abord un bon exemple, mais j'ai toujours un problème. Comment puis-je rendre ma UIViewControllerclasse conforme au délégué que nous avons créé? Doivent-ils être déclarés dans un seul fichier swift? Toute aide signifiera beaucoup.
Faruk

@Faruk Cela fait un moment que j'ai publié ceci, mais je pense que ce que vous demandez devrait être assez simple (si je ne comprends pas bien, je m'excuse). Ajoutez simplement le délégué à votre UIViewController après les deux points. Donc quelque chose comme class ViewController : UIViewController NameOfDelegate.
SeeMeCode

@SeeMeCode oui, vous avez bien compris ma question. J'ai essayé votre suggestion, mais lorsque je crée une classe de délégué en a.swiftfonction de votre réponse ci-dessus, cela ne vient pas b.swift. Je ne peux accéder à aucune classe en dehors de mon fichier swift. des durs?
Faruk

une chose que je ne comprends pas, c'est pourquoi devrais-je créer une nouvelle instance de YelpApi juste pour que j'appelle le délégué de YelpApi? Que se passe-t-il si l'instance en cours d'exécution est différente de la «nouvelle» instance que je viens de créer ... comment sait-elle quel délégué appartient à quelle instance de YelpApi?
Marin

15

DÉLÉGUÉS DANS SWIFT 2

J'explique avec l'exemple de Delegate avec deux viewControllers.Dans ce cas, SecondVC Object renvoie des données au premier View Controller.

Classe avec déclaration de protocole

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

}

Dans First ViewController Protocol, la conformité se fait ici:

class ViewController: UIViewController, getDataDelegate

Définition de la méthode de protocole dans First View Controller (ViewController)

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

Pendant la poussée du SecondVC à partir du contrôleur First View (ViewController)

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

Vos 3 dernières lignes m'ont aidé à comprendre mon scénario et à résoudre mon problème. Merci mec! :)
iHarshil

6

Première classe:

protocol NetworkServiceDelegate: class {

    func didCompleteRequest(result: String)
}


class NetworkService: NSObject {

    weak var delegate: NetworkServiceDelegate?

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

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


}

lors de la compilation du code ci-dessus, il affiche une erreur Type 'ViewController' does not conform to protocol 'NetworkServiceDelegate'suggérée par plz. C'est mon 6ème jour sur Swift :)
Vaibhav Saran

4

Très facile étape par étape (100% fonctionnel et testé)

étape1: créer une méthode sur le premier contrôleur de vue

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

étape2: définir le délégué tout en poussant vers le deuxième contrôleur de vue

@IBAction func buttonAction(_ sender: Any) {

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

étape3: définir le délégué comme

classe ViewController: UIViewController, ProcessStatusDelegate {

étape 4: créer un protocole

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

step5: prendre une variable

var delegate:ProcessStatusDelegate?

Étape 6: Tout en revenant à la méthode de délégué d'appel du contrôleur de vue précédente, le contrôleur de vue doit d'abord notifier avec des données

@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

Exemple simple:

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

pourquoi utilisez-vous le mot-clé "classe" dans la description du protocole? quelle est la différence entre l'utiliser et ne pas l'utiliser?
Vlad

2
Le mot-clé class signifie que c'est un protocole de classe uniquement. Vous pouvez limiter l'adoption du protocole aux types de classes, et non aux structures ou aux énumérations, en ajoutant le mot-clé class. Je n'aurais probablement pas dû l'ajouter pour éviter toute confusion, mais puisque vous l'avez demandé, je vais continuer.
Bobby

2

Les délégués sont un modèle de conception qui permet à un objet d'envoyer des messages à un autre objet lorsqu'un événement spécifique se produit. Imaginez qu'un objet A appelle un objet B pour effectuer une action. Une fois l'action terminée, l'objet A doit savoir que B a terminé la tâche et prendre les mesures nécessaires, cela peut être réalisé avec l'aide de délégués! Voici un tutoriel implémentant les délégués étape par étape dans Swift 3

Lien du didacticiel


0

Les solutions ci-dessus semblaient un peu couplées et en même temps évitent de réutiliser le même protocole dans d'autres contrôleurs, c'est pourquoi je suis venu avec la solution qui est plus forte typée en utilisant l'effacement de type générique.

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

sortie : nouvelle valeur newValue


Je souhaite en savoir plus à ce sujet. Pouvez-vous expliquer plus en détail les termes que vous utilisez: couplé, "éviter de réutiliser le même protocole", "effacement de type générique". Pourquoi l'abstraction est-elle si importante? Doit-on toujours faire ça?
Suragch le

0

Dans Swift 4.0

Créer un délégué sur la classe qui doit envoyer des données ou fournir des fonctionnalités à d'autres classes

Comme

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

Après cela dans la classe qui va confirmer à ce délégué

class SnakesAndLadders: GetGameStatus {
    func getPlayerDetails() {

 }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.