Comment créer des délégués dans Objective-C?

Réponses:

889

Un délégué Objective-C est un objet qui a été affecté à la delegatepropriété un autre objet. Pour en créer une, vous définissez une classe qui implémente les méthodes déléguées qui vous intéressent et marquez cette classe comme implémentant le protocole délégué.

Par exemple, supposons que vous ayez un UIWebView. Si vous souhaitez implémenter la webViewDidStartLoad:méthode de son délégué , vous pouvez créer une classe comme celle-ci:

@interface MyClass<UIWebViewDelegate>
// ...
@end

@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView { 
    // ... 
}
@end

Ensuite, vous pouvez créer une instance de MyClass et l'affecter en tant que délégué de la vue Web:

MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;

Sur le UIWebViewcôté, il a probablement un code similaire à celui-ci pour voir si le délégué répond au webViewDidStartLoad:message en utilisant respondsToSelector:et l'envoyer le cas échéant.

if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
    [self.delegate webViewDidStartLoad:self];
}

La propriété déléguée elle-même est généralement déclarée weak(dans ARC) ou assign(pré-ARC) pour éviter de conserver les boucles, car le délégué d'un objet détient souvent une référence forte à cet objet. (Par exemple, un contrôleur de vue est souvent le délégué d'une vue qu'il contient.)

Faire des délégués pour vos cours

Pour définir vos propres délégués, vous devrez déclarer leurs méthodes quelque part, comme indiqué dans les documents Apple sur les protocoles . Vous déclarez généralement un protocole formel. La déclaration, paraphrasée de UIWebView.h, ressemblerait à ceci:

@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end

Ceci est analogue à une interface ou à une classe de base abstraite, car elle crée un type spécial pour votre délégué, UIWebViewDelegatedans ce cas. Les exécutants délégués devraient adopter ce protocole:

@interface MyClass <UIWebViewDelegate>
// ...
@end

Et puis implémentez les méthodes dans le protocole. Pour les méthodes déclarées dans le protocole comme @optional(comme la plupart des méthodes déléguées), vous devez vérifier avec -respondsToSelector:avant d'appeler une méthode particulière dessus.

Appellation

Les méthodes déléguées sont généralement nommées en commençant par le nom de la classe délégante et prennent l'objet délégué comme premier paramètre. Ils utilisent aussi souvent un testament, un devoir ou un didacticiel. Ainsi, webViewDidStartLoad:(le premier paramètre est la vue Web) plutôt que loadStarted(en ne prenant aucun paramètre) par exemple.

Optimisations de vitesse

Au lieu de vérifier si un délégué répond à un sélecteur chaque fois que nous voulons lui envoyer un message, vous pouvez mettre en cache ces informations lorsque les délégués sont définis. Une façon très simple de le faire est d'utiliser un champ de bits, comme suit:

@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end

@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end

@implementation Something {
  struct {
    unsigned int didFinishLoadingItem:1;
    unsigned int didFailWithError:1;
  } delegateRespondsTo;
}
@synthesize delegate;

- (void)setDelegate:(id <SomethingDelegate>)aDelegate {
  if (delegate != aDelegate) {
    delegate = aDelegate;

    delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
    delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
  }
}
@end

Ensuite, dans le corps, nous pouvons vérifier que notre délégué gère les messages en accédant à notre delegateRespondsTostructure, plutôt qu'en envoyant -respondsToSelector:encore et encore.

Délégués informels

Avant l'existence des protocoles, il était courant d'utiliser une catégorie sur NSObjectpour déclarer les méthodes qu'un délégué pouvait implémenter. Par exemple, fait CALayertoujours ceci:

@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end

Cela indique au compilateur que tout objet peut implémenter displayLayer:.

Vous utiliseriez alors la même -respondsToSelector:approche que celle décrite ci-dessus pour appeler cette méthode. Les délégués implémentent cette méthode et assignent la delegatepropriété, et c'est tout (il n'y a pas de déclaration de conformité à un protocole). Cette méthode est courante dans les bibliothèques d'Apple, mais le nouveau code devrait utiliser l'approche de protocole plus moderne ci-dessus, car cette approche pollue NSObject(ce qui rend la saisie semi-automatique moins utile) et rend difficile pour le compilateur de vous avertir des fautes de frappe et des erreurs similaires.

Jesse Rusak
la source
Je pense que vous devez convertir le unsigned inttype en BOOLtant que la valeur de retour de delegate respondsToSelectorest de type BOOL.
Roland
Le délégué peut-il être utilisé pour le polymorphisme comme en C ++?
@Dan Oui, bien sûr. Les protocoles en général sont utilisés pour le polymorphisme.
Jesse Rusak
@JesseRusak Je pense que "JSSomethingDelegate" devrait être "SomethingDelegate" pour plus de cohérence :)
Hans Knöchel
382

La réponse approuvée est excellente, mais si vous cherchez une réponse d'une minute, essayez ceci:

Le fichier MyClass.h devrait ressembler à ceci (ajoutez des lignes de délégué avec des commentaires!)

#import <BlaClass/BlaClass.h>

@class MyClass;             //define class, so protocol can see MyClass
@protocol MyClassDelegate <NSObject>   //define delegate protocol
    - (void) myClassDelegateMethod: (MyClass *) sender;  //define delegate method to be implemented within another class
@end //end protocol

@interface MyClass : NSObject {
}
@property (nonatomic, weak) id <MyClassDelegate> delegate; //define MyClassDelegate as delegate

@end

Le fichier MyClass.m devrait ressembler à ceci

#import "MyClass.h"
@implementation MyClass 
@synthesize delegate; //synthesise  MyClassDelegate delegate

- (void) myMethodToDoStuff {
    [self.delegate myClassDelegateMethod:self]; //this will call the method implemented in your other class    
}

@end

Pour utiliser votre délégué dans une autre classe (UIViewController appelé MyVC dans ce cas) MyVC.h:

#import "MyClass.h"
@interface MyVC:UIViewController <MyClassDelegate> { //make it a delegate for MyClassDelegate
}

MyVC.m:

myClass.delegate = self;          //set its delegate to self somewhere

Implémenter la méthode déléguée

- (void) myClassDelegateMethod: (MyClass *) sender {
    NSLog(@"Delegates are great!");
}
Tibidabo
la source
4
Super d'utiliser cette réponse comme référence rapide. Mais pourquoi la propriété déléguée de votre MyClass.h est-elle marquée comme «IBOutlet»?
Arno van der Meer
4
@ArnovanderMeer Bonne prise! Je ne me souviens pas pourquoi. J'en ai besoin dans mon projet mais pas dans cet exemple, je l'ai supprimé. thx
Tibidabo
Merci. Aussi agréable et approfondie que soit la réponse acceptée, j'apprends mieux à partir d'un exemple de code compact. C'est bien d'avoir deux réponses.
sudo
@Tibidabo Totalement exceptionnel. Je souhaite vraiment que tout le monde puisse expliquer des concepts de programmation comme celui-ci. J'ai vu des centaines d'explications, au sujet des «délégués», au fil des ans et je n'ai jamais vraiment saisi cette théorie jusqu'à présent! Merci beaucoup ...
Charles Robertson
5
Où est myClassinstancié dans MyVC.m?
Lane Rettig
18

Lors de l'utilisation de la méthode de protocole formelle pour créer la prise en charge des délégués, j'ai constaté que vous pouvez garantir une vérification de type appropriée (bien que, au moment de l'exécution, pas de temps de compilation) en ajoutant quelque chose comme:

if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
    [NSException raise:@"MyDelegate Exception"
                format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}

dans votre code d'accesseur délégué (setDelegate). Cela permet de minimiser les erreurs.

umop
la source
18

S'il vous plaît! consultez le didacticiel simple étape par étape ci-dessous pour comprendre le fonctionnement des délégués dans iOS.

Délégué dans iOS

J'ai créé deux ViewControllers (pour envoyer des données de l'un à l'autre)

  1. FirstViewController implémente le délégué (qui fournit les données).
  2. SecondViewController déclare le délégué (qui recevra les données).
swiftBoy
la source
17

C'est peut-être plus dans le sens de ce qui vous manque:

Si vous venez d'un point de vue similaire à C ++, les délégués prennent un peu de temps pour s'y habituer - mais en gros 'ils fonctionnent juste'.

La façon dont cela fonctionne est que vous définissez un objet que vous avez écrit en tant que délégué à NSWindow, mais votre objet n'a que des implémentations (méthodes) pour une ou quelques-unes des nombreuses méthodes de délégué possibles. Donc, quelque chose se passe et NSWindowveut appeler votre objet - il utilise simplement la méthode d'Objective-c respondsToSelectorpour déterminer si votre objet veut que cette méthode soit appelée, puis l'appelle. Voici comment fonctionne objective-c - les méthodes sont recherchées à la demande.

C'est totalement trivial de le faire avec vos propres objets, il n'y a rien de spécial, vous pouvez par exemple avoir un NSArraydes 27 objets, toutes sortes d'objets, seulement 18 certains ayant la méthode -(void)setToBue;Les 9 autres n'en ont pas. Donc, pour appeler setToBluetous les 18 qui en ont besoin, quelque chose comme ceci:

for (id anObject in myArray)
{
  if ([anObject respondsToSelector:@selector(@"setToBlue")])
     [anObject setToBlue]; 
}

L'autre chose à propos des délégués est qu'ils ne sont pas conservés, vous devez donc toujours définir le délégué nildans votre MyClass deallocméthode.

Tom Andersen
la source
15

En tant que bonne pratique recommandée par Apple, il est bon que le délégué (qui est un protocole, par définition), se conforme au NSObjectprotocole.

@protocol MyDelegate <NSObject>
    ...
@end

& pour créer des méthodes facultatives au sein de votre délégué (c'est-à-dire des méthodes qui ne doivent pas nécessairement être implémentées), vous pouvez utiliser l' @optionalannotation comme ceci:

@protocol MyDelegate <NSObject>
    ...
    ...
      // Declaration for Methods that 'must' be implemented'
    ...
    ...
    @optional
    ...
      // Declaration for Methods that 'need not necessarily' be implemented by the class conforming to your delegate
    ...
@end

Ainsi, lorsque vous utilisez des méthodes que vous avez spécifiées comme facultatives, vous devez (dans votre classe) vérifier respondsToSelectorsi la vue (conforme à votre délégué) a réellement mis en œuvre votre ou vos méthodes facultatives.

Jean
la source
11

Je pense que toutes ces réponses ont beaucoup de sens une fois que vous comprenez les délégués. Personnellement, je viens du pays du C / C ++ et avant cela, des langages procéduraux comme Fortran, etc. voici donc mon avis de 2 minutes sur la recherche d'analogues similaires dans le paradigme C ++.

Si je devais expliquer les délégués à un programmeur C ++ / Java, je dirais

Que sont les délégués? Ce sont des pointeurs statiques vers des classes d'une autre classe. Une fois que vous avez affecté un pointeur, vous pouvez appeler des fonctions / méthodes dans cette classe. Par conséquent, certaines fonctions de votre classe sont «déléguées» (dans le monde C ++ - pointeur vers par un pointeur d'objet de classe) vers une autre classe.

Quels sont les protocoles? Sur le plan conceptuel, il sert un objectif similaire au fichier d'en-tête de la classe que vous affectez en tant que classe déléguée. Un protocole est un moyen explicite de définir quelles méthodes doivent être implémentées dans la classe dont le pointeur a été défini comme délégué au sein d'une classe.

Comment puis-je faire quelque chose de similaire en C ++? Si vous avez essayé de le faire en C ++, vous le feriez en définissant des pointeurs vers des classes (objets) dans la définition de classe, puis en les câblant à d'autres classes qui fourniront des fonctions supplémentaires en tant que délégués à votre classe de base. Mais ce câblage doit être maintenu dans le code et sera maladroit et sujet aux erreurs. Objective C suppose simplement que les programmeurs ne sont pas les meilleurs pour maintenir cette discipline et fournit des restrictions de compilation pour appliquer une implémentation propre.

DrBug
la source
Vous parlez de sémantique alors que je parlais de l'intuition. Ce dont vous parlez, c'est de la fonction virtuelle - mais juste pour vous habituer à la nouvelle terminologie peut être difficile. La réponse sert les débutants qui veulent penser à une parallèle dans C ++ / C
DrBug
Ce que vous dites n'est pas vraiment clair pour moi. Pourquoi n'écrivez-vous pas une nouvelle réponse et laissez-vous voir si plus de gens la trouvent utile, ils la voteront?
DrBug
9

Version Swift

Un délégué n'est qu'une classe qui travaille pour une autre classe. Lisez le code suivant pour un exemple de Playground quelque peu stupide (mais avec un peu de chance) qui montre comment cela est fait dans Swift.

// A protocol is just a list of methods (and/or properties) that must
// be used by any class that adopts the protocol.
protocol OlderSiblingDelegate: class {
    // This protocol only defines one required method
    func getYourNiceOlderSiblingAGlassOfWater() -> String
}

class BossyBigBrother {

    // The delegate is the BossyBigBrother's slave. This position can 
    // be assigned later to whoever is available (and conforms to the 
    // protocol).
    weak var delegate: OlderSiblingDelegate?

    func tellSomebodyToGetMeSomeWater() -> String? {
        // The delegate is optional because there might not be anyone
        // nearby to boss around.
        return delegate?.getYourNiceOlderSiblingAGlassOfWater()
    }
}

// PoorLittleSister conforms to the OlderSiblingDelegate protocol
class PoorLittleSister: OlderSiblingDelegate {

    // This method is repquired by the protocol, but the protocol said
    // nothing about how it needs to be implemented.
    func getYourNiceOlderSiblingAGlassOfWater() -> String {
        return "Go get it yourself!"
    }

}

// initialize the classes
let bigBro = BossyBigBrother()
let lilSis = PoorLittleSister()

// Set the delegate 
// bigBro could boss around anyone who conforms to the 
// OlderSiblingDelegate protocol, but since lilSis is here, 
// she is the unlucky choice.
bigBro.delegate = lilSis

// Because the delegate is set, there is a class to do bigBro's work for him.
// bigBro tells lilSis to get him some water.
if let replyFromLilSis = bigBro.tellSomebodyToGetMeSomeWater() {
    print(replyFromLilSis) // "Go get it yourself!"
}

Dans la pratique, les délégués sont souvent utilisés dans les situations suivantes

  1. Quand une classe doit communiquer des informations à une autre classe
  2. Quand une classe veut autoriser une autre classe à la personnaliser

Les 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 fortement de lire les deux articles suivants. Ils m'ont aidé à mieux comprendre les délégués que la documentation .

Suragch
la source
8

D'accord, ce n'est pas vraiment une réponse à la question, mais si vous cherchez à faire votre propre délégué, peut-être que quelque chose de beaucoup plus simple pourrait être une meilleure réponse pour vous.

J'implémente à peine mes délégués car j'en ai rarement besoin. Je ne peux avoir qu'UN délégué pour un objet délégué. Donc, si vous voulez que votre délégué communique ou transmette des données à sens unique, vous êtes bien meilleur avec les notifications.

NSNotification peut transmettre des objets à plusieurs destinataires et il est très facile à utiliser. Cela fonctionne comme ceci:

Le fichier MyClass.m devrait ressembler à ceci

#import "MyClass.h"
@implementation MyClass 

- (void) myMethodToDoStuff {
//this will post a notification with myClassData (NSArray in this case)  in its userInfo dict and self as an object
[[NSNotificationCenter defaultCenter] postNotificationName:@"myClassUpdatedData"
                                                    object:self
                                                  userInfo:[NSDictionary dictionaryWithObject:selectedLocation[@"myClassData"] forKey:@"myClassData"]];
}
@end

Pour utiliser votre notification dans une autre classe: Ajoutez une classe en tant qu'observateur:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];

Implémentez le sélecteur:

- (void) otherClassUpdatedItsData:(NSNotification *)note {
    NSLog(@"*** Other class updated its data ***");
    MyClass *otherClass = [note object];  //the object itself, you can call back any selector if you want
    NSArray *otherClassData = [note userInfo][@"myClassData"]; //get myClass data object and do whatever you want with it
}

N'oubliez pas de retirer votre classe en tant qu'observateur si

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
Tibidabo
la source
8

disons que vous avez une classe que vous avez développée et que vous souhaitez déclarer une propriété déléguée pour pouvoir la notifier lorsqu'un événement se produit:

@class myClass;

@protocol myClassDelegate <NSObject>

-(void)myClass:(MyClass*)myObject requiredEventHandlerWithParameter:(ParamType*)param;

@optional
-(void)myClass:(MyClass*)myObject optionalEventHandlerWithParameter:(ParamType*)param;

@end


@interface MyClass : NSObject

@property(nonatomic,weak)id< MyClassDelegate> delegate;

@end

vous déclarez donc un protocole dans le MyClassfichier d' en- tête (ou un fichier d'en-tête séparé), déclarez les gestionnaires d'événements requis / facultatifs que votre délégué doit / devrait implémenter, puis déclarez une propriété dans MyClassde type ( id< MyClassDelegate>) qui signifie toute classe c objective conforme à le protocole MyClassDelegate, vous remarquerez que la propriété delegate est déclarée faible, ceci est très important pour empêcher le cycle de conservation (le plus souvent, le délégué conserve l' MyClassinstance donc si vous avez déclaré le délégué comme retenue, les deux se conserveront et aucun d'entre eux seront jamais publiés).

vous remarquerez également que les méthodes de protocole transmettent l' MyClassinstance au délégué en tant que paramètre, c'est la meilleure pratique dans le cas où le délégué souhaite appeler certaines méthodes sur l' MyClassinstance et aide également lorsque le délégué se déclare quant MyClassDelegateà plusieurs MyClassinstances, comme lorsque vous en avez plusieurs UITableView'sinstances dans votre ViewControlleret se déclare comme un UITableViewDelegateà tous.

et à l'intérieur de votre, MyClassvous notifiez le délégué avec les événements déclarés comme suit:

if([_delegate respondsToSelector:@selector(myClass: requiredEventHandlerWithParameter:)])
{
     [_delegate myClass:self requiredEventHandlerWithParameter:(ParamType*)param];
}

vous vérifiez d'abord si votre délégué répond à la méthode de protocole que vous êtes sur le point d'appeler au cas où le délégué ne l'implémenterait pas et l'application se bloquerait alors (même si la méthode de protocole est requise).

m.eldehairy
la source
6

Voici une méthode simple pour créer des délégués

Créez le protocole dans le fichier .h. Assurez-vous qu'il est défini avant le protocole à l'aide de @class suivi du nom de UIViewController< As the protocol I am going to use is UIViewController class>.

Étape: 1: Créez un nouveau protocole de classe nommé "YourViewController" qui sera la sous-classe de la classe UIViewController et affectez cette classe au deuxième ViewController.

Étape: 2: Accédez au fichier "YourViewController" et modifiez-le comme ci-dessous:

#import <UIKit/UIkit.h>
@class YourViewController;

@protocol YourViewController Delegate <NSObject>

 @optional
-(void)defineDelegateMethodName: (YourViewController *) controller;

@required
-(BOOL)delegateMethodReturningBool: (YourViewController *) controller;

  @end
  @interface YourViewController : UIViewController

  //Since the property for the protocol could be of any class, then it will be marked as a type of id.

  @property (nonatomic, weak) id< YourViewController Delegate> delegate;

@end

Les méthodes définies dans le comportement du protocole peuvent être contrôlées avec @optional et @required dans le cadre de la définition du protocole.

Étape: 3: Mise en œuvre du délégué

    #import "delegate.h"

   @interface YourDelegateUser ()
     <YourViewControllerDelegate>
   @end

   @implementation YourDelegateUser

   - (void) variousFoo {
      YourViewController *controller = [[YourViewController alloc] init];
      controller.delegate = self;
   }

   -(void)defineDelegateMethodName: (YourViewController *) controller {
      // handle the delegate being called here
   }

   -(BOOL)delegateMethodReturningBool: (YourViewController *) controller {
      // handle the delegate being called here
      return YES;
   }

   @end

// teste si la méthode a été définie avant de l'appeler

 - (void) someMethodToCallDelegate {
     if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
           [self.delegate delegateMethodName:self]; 
     }
  }
soumya
la source
5

Pour créer votre propre délégué, vous devez d'abord créer un protocole et déclarer les méthodes nécessaires, sans implémenter. Et puis implémentez ce protocole dans votre classe d'en-tête où vous souhaitez implémenter le délégué ou les méthodes de délégué.

Un protocole doit être déclaré comme suit:

@protocol ServiceResponceDelegate <NSObject>

- (void) serviceDidFailWithRequestType:(NSString*)error;
- (void) serviceDidFinishedSucessfully:(NSString*)success;

@end

Il s'agit de la classe de service où une tâche doit être effectuée. Il montre comment définir un délégué et comment définir le délégué. Dans la classe d'implémentation, une fois la tâche terminée, les méthodes du délégué sont appelées.

@interface ServiceClass : NSObject
{
id <ServiceResponceDelegate> _delegate;
}

- (void) setDelegate:(id)delegate;
- (void) someTask;

@end

@implementation ServiceClass

- (void) setDelegate:(id)delegate
{
_delegate = delegate;
}

- (void) someTask
{
/*

   perform task

*/
if (!success)
{
[_delegate serviceDidFailWithRequestType:@”task failed”];
}
else
{
[_delegate serviceDidFinishedSucessfully:@”task success”];
}
}
@end

Il s'agit de la classe de vue principale à partir de laquelle la classe de service est appelée en définissant le délégué sur lui-même. Et le protocole est également implémenté dans la classe d'en-tête.

@interface viewController: UIViewController <ServiceResponceDelegate>
{
ServiceClass* _service;
}

- (void) go;

@end

@implementation viewController

//
//some methods
//

- (void) go
{
_service = [[ServiceClass alloc] init];
[_service setDelegate:self];
[_service someTask];
}

C'est tout, et en implémentant des méthodes déléguées dans cette classe, le contrôle reviendra une fois l'opération / tâche terminée.

Mahesh
la source
4

Avertissement: il s'agit de la Swiftversion de la création d'un fichier delegate.

Alors, quels sont les délégués? … Dans le développement de logiciels, il existe des architectures de solutions générales réutilisables qui aident à résoudre les problèmes courants dans un contexte donné, ces «modèles», pour ainsi dire, sont mieux connus sous le nom de modèles de conception. 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 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 des délégués!

Pour une meilleure explication, je vais vous montrer comment créer un délégué personnalisé qui passe des données entre les classes, avec Swift dans une application simple, commencez par télécharger ou cloner ce projet de démarrage et exécutez-le!

Vous pouvez voir une application avec deux classes, ViewController Aet ViewController B. B a deux vues qui au toucher modifient la couleur d'arrière-plan du ViewController, rien de trop compliqué non? Eh bien, réfléchissons maintenant de manière simple à la couleur d'arrière-plan de la classe A lorsque les vues sur la classe B sont exploitées.

Le problème est que ces vues font partie de la classe B et n'ont aucune idée de la classe A, nous devons donc trouver un moyen de communiquer entre ces deux classes, et c'est là que la délégation brille. J'ai divisé l'implémentation en 6 étapes afin que vous puissiez l'utiliser comme une feuille de triche lorsque vous en avez besoin.

étape 1: recherchez la marque pragma étape 1 dans le fichier ClassBVC et ajoutez-la

//MARK: step 1 Add Protocol here.
protocol ClassBVCDelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}

La première étape consiste à créer un protocol, dans ce cas, nous allons créer le protocole en classe B, à l'intérieur du protocole, vous pouvez créer autant de fonctions que vous le souhaitez en fonction des exigences de votre implémentation. Dans ce cas, nous avons juste une fonction simple qui accepte une option UIColorcomme argument. Est une bonne pratique pour nommer vos protocoles en ajoutant le mot delegateà la fin du nom de la classe, dans ce cas ClassBVCDelegate,.

étape 2: recherchez la marque pragma étape 2 ClassVBCet ajoutez-la

//MARK: step 2 Create a delegate property here.
weak var delegate: ClassBVCDelegate?

Ici, nous venons de créer une propriété déléguée pour la classe, cette propriété doit adopter le protocoltype et elle doit être facultative. En outre, vous devez ajouter le mot clé faible avant la propriété pour éviter de conserver les cycles et les fuites de mémoire potentielles.Si vous ne savez pas ce que cela signifie, ne vous inquiétez pas pour le moment, n'oubliez pas d'ajouter ce mot clé.

Étape 3: Recherchez la marque pragma étape 3 à l' intérieur du handleTap methoddans ClassBVCet ajoutez cette

//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(tapGesture.view?.backgroundColor)

Une chose que vous devez savoir, exécutez l'application et appuyez sur n'importe quelle vue, vous ne verrez aucun nouveau comportement et c'est correct, mais la chose que je veux souligner est que l'application ne se bloque pas lorsque le délégué est appelé, et c'est parce que nous le créons en tant que valeur facultative et c'est pourquoi il ne plantera pas même le délégué n'existe pas encore. Allons maintenant au ClassAVCfichier et le faire, le délégué.

étape 4: recherchez la marque de pragma étape 4 dans la méthode handleTap ClassAVCet ajoutez-la à côté de votre type de classe comme ceci.

//MARK: step 4 conform the protocol here.
class ClassAVC: UIViewController, ClassBVCDelegate {
}

Maintenant ClassAVC a adopté le ClassBVCDelegateprotocole, vous pouvez voir que votre compilateur vous donne une erreur qui dit "Type" ClassAVC n'est pas conforme au protocole "ClassBVCDelegate" et cela signifie seulement que vous n'avez pas encore utilisé les méthodes du protocole, imaginez que quand la classe A adopte le protocole, c'est comme signer un contrat avec la classe B et ce contrat dit "Toute classe qui m'adopte DOIT utiliser mes fonctions!"

Remarque rapide: si vous venez d'un Objective-Carrière-plan, vous pensez probablement que vous pouvez également fermer cette erreur en rendant cette méthode facultative, mais à ma grande surprise, et probablement la vôtre, le Swiftlangage ne prend pas en charge facultatif protocols, si vous voulez le faire, vous pouvez créer une extension pour votre protocolou utilisez le mot clé @objc dans votre protocolimplémentation.

Personnellement, si je dois créer un protocole avec différentes méthodes optionnelles, je préférerais le diviser en différents protocols, de cette façon je suivrai le concept de donner une seule responsabilité à mes objets, mais cela peut varier en fonction de l'implémentation spécifique.

voici un bon article sur les méthodes optionnelles.

étape 5: recherchez la marque de pragma étape 5 à l'intérieur de la méthode de préparation à la séquence et ajoutez-la

//MARK: step 5 create a reference of Class B and bind them through the `prepareforsegue` method.
if let nav = segue.destination as? UINavigationController, let classBVC = nav.topViewController as? ClassBVC {
classBVC.delegate = self
}

Ici, nous créons simplement une instance de ClassBVCet affectons son délégué à soi, mais qu'est-ce que le moi ici? eh bien, c'est soi ClassAVCqui a été délégué!

étape 6: Enfin, recherchez le pragma étape 6 dans ClassAVCet utilisons les fonctions de la protocol, commencez à taper func changeBackgroundColor et vous verrez qu'il le remplit automatiquement pour vous. Vous pouvez ajouter n'importe quelle implémentation à l'intérieur, dans cet exemple, nous allons simplement changer la couleur d'arrière-plan, ajoutez ceci.

//MARK: step 6 finally use the method of the contract
func changeBackgroundColor(_ color: UIColor?) {
view.backgroundColor = color
}

Maintenant, lancez l'application!

Delegatessont partout et vous les utilisez probablement sans même avis, si vous créez une tableviewdélégation dans le passé, vous avez utilisé de nombreuses classes d' UIKITœuvres autour d'eux et bien d'autres frameworksaussi, ils résolvent ces principaux problèmes.

  • Évitez les couplages serrés d'objets.
  • Modifiez le comportement et l'apparence sans avoir besoin de sous-classer les objets.
  • Autorisez le traitement des tâches vers n'importe quel objet arbitraire.

Félicitations, vous venez d'implémenter un délégué personnalisé, je sais que vous pensez probablement, tant de mal juste pour ça? eh bien, la délégation est un modèle de conception très important pour comprendre si vous voulez devenir iOSdéveloppeur, et gardez toujours à l'esprit qu'ils ont une relation un à un entre les objets.

Vous pouvez voir le tutoriel original ici

James Rochabrun
la source
4

La réponse est en fait répondue, mais je voudrais vous donner une "feuille de triche" pour créer un délégué:

DELEGATE SCRIPT

CLASS A - Where delegate is calling function

@protocol <#Protocol Name#> <NSObject>

-(void)delegateMethod;

@end

@interface <#Some ViewController#> : <#UIViewController#> 

@property (nonatomic, assign) id <<#Protocol Name#>> delegate;

@end


@implementation <#Some ViewController#> 

-(void)someMethod {
    [self.delegate methodName];
}

@end




CLASS B - Where delegate is called 

@interface <#Other ViewController#> (<#Delegate Name#>) {}
@end

@implementation <#Other ViewController#> 

-(void)otherMethod {
    CLASSA *classA = [[CLASSA alloc] init];

    [classA setDelegate:self];
}

-delegateMethod() {

}

@end
Miras Maratuly
la source
2

ViewController.h

@protocol NameDelegate <NSObject>

-(void)delegateMEthod: (ArgType) arg;

@end

@property id <NameDelegate> delegate;

ViewController.m

[self.delegate delegateMEthod: argument];

MainViewController.m

ViewController viewController = [ViewController new];
viewController.delegate = self;

Méthode:

-(void)delegateMEthod: (ArgType) arg{
}
Lal Krishna
la source
2

À mon avis, créez une classe distincte pour cette méthode déléguée et vous pouvez l'utiliser où vous le souhaitez.

dans mon DropDownClass.h personnalisé

typedef enum
{
 DDSTATE,
 DDCITY
}DropDownType;

@protocol DropDownListDelegate <NSObject>
@required
- (void)dropDownDidSelectItemWithString:(NSString*)itemString     DropDownType:(DropDownType)dropDownType;
@end
@interface DropDownViewController : UIViewController
{
 BOOL isFiltered;
}
@property (nonatomic, assign) DropDownType dropDownType;
@property (weak) id <DropDownListDelegate> delegate;
@property (strong, nonatomic) NSMutableArray *array1DropDown;
@property (strong, nonatomic) NSMutableArray *array2DropDown;

après ce fichier in.m créer un tableau avec des objets,

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat rowHeight = 44.0f;
return rowHeight;
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return isFiltered?[self.array1DropDown count]:[self.array2DropDown count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = @"TableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}

if (self.delegate) {
    if (self.dropDownType == DDCITY) {
        cell.textLabel.text = [self.array1DropDown objectAtIndex:indexPath.row];
    }
    else if (self.dropDownType == DDSTATE) {
        cell.textLabel.text = [self.array2DropDown objectAtIndex:indexPath.row];
    }
}
return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
 [self dismissViewControllerAnimated:YES completion:^{
    if(self.delegate){
        if(self.dropDownType == DDCITY){
            [self.delegate dropDownDidSelectItemWithString:[self.array1DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
        }
        else if (self.dropDownType == DDSTATE) {
            [self.delegate dropDownDidSelectItemWithString:[self.array2DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
        }
    }
}];
}

Ici, tous sont définis pour la classe déléguée personnalisée.Après cela, vous pouvez utiliser cette méthode déléguée où vous le souhaitez.Par exemple ...

dans mon autre importation de viewcontroller après cela

créer une action pour appeler la méthode déléguée comme celle-ci

- (IBAction)dropDownBtn1Action:(id)sender {
DropDownViewController *vehicleModelDropView = [[DropDownViewController alloc]init];
vehicleModelDropView.dropDownType = DDCITY;
vehicleModelDropView.delegate = self;
[self presentViewController:vehicleModelDropView animated:YES completion:nil];
}

après cette méthode d'appel délégué comme celui-ci

- (void)dropDownDidSelectItemWithString:(NSString *)itemString DropDownType:(DropDownType)dropDownType {
switch (dropDownType) {
    case DDCITY:{
        if(itemString.length > 0){
            //Here i am printing the selected row
            [self.dropDownBtn1 setTitle:itemString forState:UIControlStateNormal];
        }
    }
        break;
    case DDSTATE: {
        //Here i am printing the selected row
        [self.dropDownBtn2 setTitle:itemString forState:UIControlStateNormal];
    }

    default:
        break;
}
}
Utilisateur558
la source
0

Délégué: - Créer

@protocol addToCartDelegate <NSObject>

-(void)addToCartAction:(ItemsModel *)itemsModel isAdded:(BOOL)added;

@end

Envoyez et veuillez affecter un délégué pour voir que vous envoyez des données

[self.delegate addToCartAction:itemsModel isAdded:YES];
Vaibhav Gaikwad
la source
0
//1.
//Custom delegate 
@protocol TB_RemovedUserCellTag <NSObject>

-(void)didRemoveCellWithTag:(NSInteger)tag;

@end

//2.
//Create a weak reference in a class where you declared the delegate
@property(weak,nonatomic)id <TB_RemovedUserCellTag> removedCellTagDelegate;

//3. 
// use it in the class
  [self.removedCellTagDelegate didRemoveCellWithTag:self.tag];

//4. import the header file in the class where you want to conform to the protocol
@interface MyClassUsesDelegate ()<TB_RemovedUserCellTag>

@end

// 5. Implémentez la méthode dans la classe .m - (void) didRemoveCellWithTag: (NSInteger) tag {NSLog @ ("Tag% d", tag);

}

Rohit Kashyap
la source
0

Commençons par un exemple, si nous achetons un produit en ligne, il passe par un processus tel que l'expédition / la livraison géré par différentes équipes. serait une surcharge pour d'autres personnes / le fournisseur pourrait vouloir transmettre ces informations uniquement aux personnes requises.

Donc, si nous pensons en termes de notre application, un événement peut être une commande en ligne et différentes équipes peuvent être comme des vues multiples.

Voici le code considérer ShippingView comme équipe d'expédition et DeliveryView comme équipe de livraison:

//Declare the protocol with functions having info which needs to be communicated
protocol ShippingDelegate : class {
    func productShipped(productID : String)
}
//shippingView which shows shipping status of products
class ShippingView : UIView
{

    weak var delegate:ShippingDelegate?
    var productID : String

    @IBAction func checkShippingStatus(sender: UIButton)
    {
        // if product is shipped
        delegate?.productShipped(productID: productID)
    }
}
//Delivery view which shows delivery status & tracking info
class DeliveryView: UIView,ShippingDelegate
{
    func productShipped(productID : String)
    {
        // update status on view & perform delivery
    }
}

//Main page on app which has both views & shows updated info on product whole status
class ProductViewController : UIViewController
{
    var shippingView : ShippingView
    var deliveryView : DeliveryView

    override func viewDidLoad() {
        super.viewDidLoad()
        // as we want to update shipping info on delivery view, so assign delegate to delivery object
        // whenever shipping status gets updated it will call productShipped method in DeliveryView & update UI.
        shippingView.delegate = deliveryView
        //
    }
}
Ellen
la source