Come posso creare delegati in Objective-C?


Risposte:


889

Un delegato Objective-C è un oggetto che è stato assegnato alla delegateproprietà un altro oggetto. Per crearne uno, devi definire una classe che implementa i metodi delegati che ti interessano e contrassegnare quella classe come implementazione del protocollo delegato.

Ad esempio, supponiamo di avere un UIWebView. Se desideri implementare il webViewDidStartLoad:metodo del suo delegato , puoi creare una classe come questa:

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

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

Quindi è possibile creare un'istanza di MyClass e assegnarla come delegato della vista Web:

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

Sul UIWebViewlato, probabilmente ha un codice simile a questo per vedere se il delegato risponde al webViewDidStartLoad:messaggio usando respondsToSelector:e inviarlo se appropriato.

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

La proprietà del delegato stessa viene in genere dichiarata weak(in ARC) o assign(pre-ARC) per evitare loop di mantenimento, poiché il delegato di un oggetto contiene spesso un forte riferimento a quell'oggetto. (Ad esempio, un controller di vista è spesso il delegato di una vista che contiene.)

Fare delegati per le tue lezioni

Per definire i tuoi delegati, dovrai dichiarare i loro metodi da qualche parte, come discusso in Apple Docs sui protocolli . Di solito dichiari un protocollo formale. La dichiarazione, parafrasata da UIWebView.h, sarebbe simile a questa:

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

Ciò è analogo a un'interfaccia o a una classe base astratta, in quanto crea un tipo speciale per il delegato, UIWebViewDelegatein questo caso. Gli implementatori delegati dovrebbero adottare questo protocollo:

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

E quindi implementare i metodi nel protocollo. Per i metodi dichiarati nel protocollo come @optional(come la maggior parte dei metodi delegati), è necessario verificare -respondsToSelector:prima di chiamare un particolare metodo su di esso.

Naming

I metodi delegati vengono in genere denominati a partire dal nome della classe delegante e prendono l'oggetto delegante come primo parametro. Spesso usano anche una volontà, una forma o una forma. Quindi, webViewDidStartLoad:(il primo parametro è la vista Web) anziché loadStarted(non prendere parametri) per esempio.

Ottimizzazioni della velocità

Invece di verificare se un delegato risponde a un selettore ogni volta che vogliamo inviarlo un messaggio, è possibile memorizzare tali informazioni nella cache quando i delegati sono impostati. Un modo molto pulito per farlo è usare un bitfield, come segue:

@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

Quindi, nel corpo, possiamo verificare che il nostro delegato gestisca i messaggi accedendo alla nostra delegateRespondsTostruttura, piuttosto che inviando -respondsToSelector:più e più volte.

Delegati informali

Prima di protocolli esistevano, era comune l'uso di un categoria su NSObjectdi dichiarare i metodi di un delegato potrebbe implementare. Ad esempio, CALayercontinua a fare questo:

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

Questo dice al compilatore che qualsiasi oggetto potrebbe implementare displayLayer:.

Dovresti quindi utilizzare lo stesso -respondsToSelector:approccio descritto sopra per chiamare questo metodo. I delegati implementano questo metodo e assegnano la delegateproprietà, e il gioco è fatto (non è possibile dichiarare la conformità a un protocollo). Questo metodo è comune nelle librerie di Apple, ma il nuovo codice dovrebbe usare l'approccio di protocollo più moderno sopra, poiché questo approccio inquina NSObject(il che rende meno utile il completamento automatico) e rende difficile per il compilatore avvisarti di errori di battitura e errori simili.


Penso che sia necessario eseguire il cast del unsigned inttipo in BOOLquanto il valore restituito delegate respondsToSelectorè di tipo BOOL.
Roland,

Il delegato può essere utilizzato per il polimorfismo come in C ++?

@ Dan Sì, certo. I protocolli in generale sono usati per il polimorfismo.
Jesse Rusak,

@JesseRusak Penso che "JSSomethingDelegate" dovrebbe essere "SomethingDelegate" per coerenza :)
Hans Knöchel,

382

La risposta approvata è ottima, ma se stai cercando una risposta da 1 minuto prova questo:

Il file MyClass.h dovrebbe apparire così (aggiungi righe delegate con commenti!)

#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

Il file MyClass.m dovrebbe apparire così

#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

Per utilizzare il delegato in un'altra classe (in questo caso UIViewController chiamato MyVC) 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

Implementare il metodo delegato

- (void) myClassDelegateMethod: (MyClass *) sender {
    NSLog(@"Delegates are great!");
}

4
Ottimo per usare questa risposta come riferimento rapido. Ma perché la proprietà del delegato in MyClass.h è contrassegnata come "IBOutlet"?
Arno van der Meer,

4
@ArnovanderMeer Buona cattura! Non ricordo il perché. Ne ho bisogno nel mio progetto ma non in questo esempio, l'ho rimosso. grazie
Tibidabo,

Grazie. Per quanto bella e completa sia la risposta accettata, imparo meglio da un codice di esempio compatto. È bello avere due risposte.
sudo,

@Tibidabo Totalmente eccezionale. Vorrei davvero che tutti potessero spiegare concetti di programmazione come questo. Nel corso degli anni ho visto centinaia di spiegazioni sui "delegati" e finora non ho mai compreso questa teoria! Grazie mille ...
Charles Robertson,

5
Dove viene myClassistanziata in MyVC.m?
Lane Rettig,

18

Quando si utilizza il metodo di protocollo formale per la creazione del supporto delegato, ho scoperto che è possibile garantire il controllo del tipo corretto (anche se, tempo di esecuzione, non tempo di compilazione) aggiungendo qualcosa di simile:

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

nel tuo codice di accesso delegato (setDelegate). Questo aiuta a minimizzare gli errori.


18

Per favore! controlla di seguito un semplice tutorial passo passo per capire come funzionano i delegati in iOS.

Delegare in iOS

Ho creato due ViewController (per l'invio di dati da uno all'altro)

  1. FirstViewController implementa delegate (che fornisce dati).
  2. SecondViewController dichiara il delegato (che riceverà i dati).

17

Forse questo è più sulla falsariga di ciò che ti manca:

Se provieni da un punto di vista simile al C ++, i delegati impiegano un po 'ad abituarsi, ma fondamentalmente "funzionano e basta".

Il modo in cui funziona è che imposti alcuni oggetti che hai scritto come delegato su NSWindow, ma il tuo oggetto ha implementazioni (metodi) solo per uno o alcuni dei molti possibili metodi delegati. Quindi succede qualcosa e NSWindowvuole chiamare il tuo oggetto - usa solo il respondsToSelectormetodo di Objective-c per determinare se il tuo oggetto vuole quel metodo chiamato, e poi lo chiama. Ecco come funziona Object-C: i metodi vengono cercati su richiesta.

È del tutto banale farlo con i tuoi oggetti, non c'è niente di speciale in corso, potresti ad esempio avere uno NSArraydi 27 oggetti, tutti i diversi tipi di oggetti, solo 18 alcuni hanno il metodo -(void)setToBue;Gli altri 9 no. Quindi, per chiamare setToBluetutti i 18 che ne hanno bisogno, qualcosa del genere:

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

L'altro aspetto dei delegati è che non vengono mantenuti, quindi è sempre necessario impostare il delegato nilnel proprio MyClass deallocmetodo.


15

Come buona pratica raccomandata da Apple, è bene che il delegato (che è un protocollo, per definizione), si conformi al NSObjectprotocollo.

@protocol MyDelegate <NSObject>
    ...
@end

e per creare metodi opzionali all'interno del tuo delegato (ovvero metodi che non devono necessariamente essere implementati), puoi usare l' @optionalannotazione in questo modo:

@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

Pertanto, quando si utilizzano metodi specificati come facoltativi, è necessario (nella propria classe) verificare respondsToSelectorse la vista (conforme al proprio delegato) ha effettivamente implementato i metodi opzionali o meno.


11

Penso che tutte queste risposte abbiano molto senso dopo aver capito i delegati. Personalmente vengo dalla terra di C / C ++ e prima di quei linguaggi procedurali come Fortran ecc. Quindi ecco la mia opinione di 2 minuti per trovare analoghi simili nel paradigma C ++.

Se dovessi spiegare i delegati a un programmatore C ++ / Java, direi

Cosa sono i delegati? Questi sono puntatori statici alle classi all'interno di un'altra classe. Dopo aver assegnato un puntatore, è possibile chiamare funzioni / metodi in quella classe. Quindi alcune funzioni della tua classe sono "delegate" (nel mondo C ++ - puntatore a da un puntatore oggetto di classe) a un'altra classe.

Quali sono i protocolli? Concettualmente serve come scopo simile al file di intestazione della classe che si sta assegnando come classe delegata. Un protocollo è un modo esplicito per definire quali metodi devono essere implementati nella classe il cui puntatore è stato impostato come delegato all'interno di una classe.

Come posso fare qualcosa di simile in C ++? Se hai provato a farlo in C ++, definiresti i puntatori alle classi (oggetti) nella definizione della classe e poi collegandoli ad altre classi che forniranno funzioni aggiuntive come delegati alla tua classe base. Ma questo cablaggio deve essere mantenuto nel codice e sarà goffo e soggetto a errori. L'obiettivo C presuppone solo che i programmatori non siano in grado di mantenere questa deciplina e fornisce restrizioni al compilatore per imporre un'implementazione pulita.


Quello di cui stai parlando è la semantica mentre stavo parlando dell'intuizione. Quello di cui stai parlando è la funzione virtuale, ma solo abituarsi alla nuova terminologia può essere impegnativo. La risposta serve ai principianti che vogliono pensare a un parallelo in C ++ / C
DrBug

Quello che stai dicendo non è molto chiaro per me. Perché non scrivi una nuova risposta e vediamo se più persone lo trovano utile, lo voteranno?
DrBug

9

Versione rapida

Un delegato è solo una classe che fa del lavoro per un'altra classe. Leggi il seguente codice per un esempio di Playground un po 'sciocco (ma si spera illuminante) che mostra come questo viene fatto in 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!"
}

Nella pratica reale, i delegati vengono spesso utilizzati nelle seguenti situazioni

  1. Quando una classe deve comunicare alcune informazioni a un'altra classe
  2. Quando una classe vuole consentire a un'altra classe di personalizzarla

Le classi non devono prima conoscersi reciprocamente, tranne per il fatto 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 .


8

Ok, questa non è davvero una risposta alla domanda, ma se stai cercando come rendere il tuo delegato forse qualcosa di molto più semplice potrebbe essere una risposta migliore per te.

Difficilmente implemento i miei delegati perché raramente ne ho bisogno. Posso avere UN SOLO delegato per un oggetto delegato. Quindi, se si desidera che il proprio delegato comunichi / passi i dati in un modo di quanto sia meglio con le notifiche.

NSNotification può passare oggetti a più destinatari ed è molto facile da usare. Funziona così:

Il file MyClass.m dovrebbe apparire così

#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

Per utilizzare la notifica in altre classi: Aggiungi classe come osservatore:

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

Implementa il selettore:

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

Non dimenticare di rimuovere la tua classe come osservatore se

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

8

supponiamo che tu abbia una classe sviluppata e desideri dichiarare una proprietà delegata per poterla notificare quando si verifica un evento:

@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

pertanto si dichiara un protocollo nel MyClassfile di intestazione (o in un file di intestazione separato) e si dichiarano i gestori di eventi obbligatori / facoltativi che il delegato deve / deve implementare, quindi si dichiara una proprietà in MyClassdi tipo ( id< MyClassDelegate>) che significa qualsiasi classe c obiettiva conforme a il protocollo MyClassDelegate, noterai che la proprietà del delegato è dichiarata debole, questo è molto importante per impedire il ciclo di conservazione (il più delle volte il delegato mantiene ilMyClass istanza, quindi se hai dichiarato il delegato come mantenimento, entrambi si manterranno reciprocamente e nessuno dei due di loro sarà mai rilasciato).

noterai anche che i metodi del protocollo passano l' MyClassistanza al delegato come parametro, questa è la migliore pratica nel caso in cui il delegato desideri chiamare alcuni metodi MyClasssull'istanza e aiuta anche quando il delegato si dichiara MyClassDelegatesu più MyClassistanze, come quando hai più UITableView'sistanze nel tuo ViewControllere si dichiara UITableViewDelegatea tutte.

e all'interno della tua MyClassnotifica al delegato con eventi dichiarati come segue:

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

per prima cosa controlla se il tuo delegato risponde al metodo del protocollo che stai per chiamare nel caso in cui il delegato non lo attui e l'app si arresterà in modo anomalo (anche se è richiesto il metodo del protocollo).


6

Ecco un metodo semplice per creare delegati

Crea protocollo nel file .h. Assicurarsi che sia definito prima del protocollo usando @class seguito dal nome di UIViewController< As the protocol I am going to use is UIViewController class>.

Passaggio: 1: creare un nuovo protocollo di classe denominato "YourViewController" che sarà la sottoclasse della classe UIViewController e assegnare questa classe al secondo ViewController.

Passaggio: 2: vai al file "YourViewController" e modificalo come di seguito:

#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

I metodi definiti nel comportamento del protocollo possono essere controllati con @optional e @required come parte della definizione del protocollo.

Passaggio: 3: implementazione del delegato

    #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

// verifica se il metodo è stato definito prima di chiamarlo

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

5

Per creare il proprio delegato, è innanzitutto necessario creare un protocollo e dichiarare i metodi necessari, senza implementare. Quindi implementare questo protocollo nella classe di intestazione in cui si desidera implementare i metodi delegato o delegato.

Un protocollo deve essere dichiarato come di seguito:

@protocol ServiceResponceDelegate <NSObject>

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

@end

Questa è la classe di servizio in cui è necessario eseguire alcune attività. Mostra come definire il delegato e come impostare il delegato. Nella classe di implementazione dopo che l'attività è stata completata vengono chiamati i metodi del delegato.

@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

Questa è la classe della vista principale da cui viene chiamata la classe di servizio impostando il delegato su se stesso. E anche il protocollo è implementato nella classe header.

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

- (void) go;

@end

@implementation viewController

//
//some methods
//

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

Ecco fatto, e implementando metodi delegati in questa classe, il controllo tornerà al termine dell'operazione / attività.


4

Disclaimer: questa è la Swiftversione di come creare un delegate.

Quindi, cosa sono i delegati? ... nello sviluppo del software, esistono architetture di soluzioni riutilizzabili generali che aiutano a risolvere i problemi più comuni in un determinato contesto, questi "modelli", per così dire, sono meglio conosciuti come modelli di progettazione. 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!

Per una spiegazione migliore, ti mostrerò come creare un delegato personalizzato che passa i dati tra le classi, con Swift in una semplice applicazione, inizia scaricando o clonando questo progetto iniziale ed eseguirlo!

Puoi vedere un'app con due classi ViewController Ae ViewController B. B ha due viste che alla spina cambiano il colore di sfondo del ViewController, niente di troppo complicato giusto? bene ora pensiamo in modo semplice a cambiare anche il colore di sfondo della classe A quando vengono toccate le viste sulla classe B.

Il problema è che queste visualizzazioni fanno parte della classe B e non hanno idea della classe A, quindi dobbiamo trovare un modo per comunicare tra queste due classi, ed è qui che brilla la delega. Ho suddiviso l'implementazione in 6 passaggi in modo da poterlo utilizzare come cheat sheet quando ne hai bisogno.

passaggio 1: cercare il segno pragma passaggio 1 nel file ClassBVC e aggiungerlo

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

Il primo passo è creare un protocol, in questo caso, creeremo il protocollo in classe B, all'interno del protocollo puoi creare tutte le funzioni che desideri in base ai requisiti della tua implementazione. In questo caso, abbiamo solo una semplice funzione che accetta un opzionale UIColorcome argomento. È buona norma nominare i protocolli aggiungendo la parola delegatealla fine del nome della classe, in questo caso ClassBVCDelegate.

passaggio 2: cercare il segno pragma passaggio 2 in ClassVBCe aggiungere questo

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

Qui creiamo solo una proprietà delegata per la classe, questa proprietà deve adottare il protocol tipo e dovrebbe essere facoltativa. Inoltre, dovresti aggiungere la parola chiave debole prima della proprietà per evitare cicli di conservazione e potenziali perdite di memoria, se non sai cosa significa che non ti preoccupare per ora, ricorda di aggiungere questa parola chiave.

Fase 3: Cercare il punto marchio pragma 3 all'interno del handleTap methodin ClassBVCe aggiungere questo

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

Una cosa che dovresti sapere, eseguire l'app e toccare qualsiasi vista, non vedrai alcun nuovo comportamento ed è corretto, ma la cosa che voglio sottolineare è che l'app non si arresta in modo anomalo quando viene chiamato il delegato, e è perché lo creiamo come valore opzionale ed è per questo che non si arresta in modo anomalo anche se il delegato non esiste ancora. Andiamo ora al ClassAVCfile e rendiamolo delegato.

passaggio 4: cercare il segno pragma passaggio 4 all'interno del metodo handleTap ClassAVCe aggiungerlo accanto al tipo di classe in questo modo.

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

Ora ClassAVC ha adottato il ClassBVCDelegateprotocollo, puoi vedere che il tuo compilatore ti sta dando un errore che dice "Tipo" ClassAVC non è conforme al protocollo "ClassBVCDelegate" e questo significa solo che non hai ancora usato i metodi del protocollo, immagina che quando la classe A adotta il protocollo è come firmare un contratto con la classe B e questo contratto dice "Qualsiasi classe che mi adotta DEVE usare le mie funzioni!"

Nota rapida: se provieni da uno Objective-Csfondo probabilmente stai pensando che puoi anche chiudere quell'errore rendendo quel metodo facoltativo, ma per mia sorpresa, e probabilmente il tuo, la Swiftlingua non supporta gli optional protocols, se vuoi farlo puoi creare un'estensione per la tua protocolo usa la parola chiave @objc nella tuaprotocol implementazione.

Personalmente, se devo creare un protocollo con diversi metodi opzionali preferirei dividerlo in diversi protocols, in questo modo seguirò il concetto di dare una singola responsabilità ai miei oggetti, ma può variare in base all'implementazione specifica.

qui è un buon articolo sui metodi opzionali.

passaggio 5: cercare il segno pragma passaggio 5 all'interno del metodo di preparazione per segue e aggiungere questo

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

Qui stiamo solo creando un'istanza di ClassBVCe assegniamo il suo delegato a sé, ma che cos'è il sé qui? bene, l'io è ilClassAVC delegato!

passaggio 6: Infine, cerca il pragma passaggio 6 in ClassAVCe usiamo le funzioni di protocol, inizia a digitare func changeBackgroundColor e vedrai che lo sta completando automaticamente per te. Puoi aggiungere qualsiasi implementazione al suo interno, in questo esempio, cambieremo solo il colore di sfondo, aggiungilo.

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

Ora esegui l'app!

Delegatessono ovunque e probabilmente li usi anche senza preavviso, se tableviewin passato hai creato una delegazione che hai usato, molte classi di UIKITlavori intorno a loro e molte altre frameworks, risolvono questi problemi principali.

  • Evitare l'accoppiamento stretto di oggetti.
  • Modifica il comportamento e l'aspetto senza la necessità di sottoclassare oggetti.
  • Consenti alle attività di essere gestite su qualsiasi oggetto arbitrario.

Congratulazioni, hai appena implementato un delegato personalizzato, so che probabilmente stai pensando, così tanti problemi solo per questo? bene, la delega è un modello di progettazione molto importante per capire se vuoi diventare uno iOSsviluppatore e tieni sempre presente che hanno una relazione uno a uno tra gli oggetti.

Puoi vedere il tutorial originale qui


4

In realtà la risposta è una risposta, ma vorrei darti un "foglietto illustrativo" per la creazione di un delegato:

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

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;

Metodo:

-(void)delegateMEthod: (ArgType) arg{
}

2

Dal mio punto di vista, crea una classe separata per quel metodo delegato e puoi usare dove vuoi.

nel mio DropDownClass.h personalizzato

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;

dopo che il file in.m crea un array con oggetti,

 - (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];
        }
    }
}];
}

Qui tutti sono impostati per la classe delegata personalizzata. Dopo che è possibile utilizzare questo metodo delegato dove si desidera.Ad esempio ...

nel mio altro viewcontroller importare dopo

creare un'azione per chiamare il metodo delegato come questo

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

dopo quella chiamata metodo delegato come questo

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

0

Delegato: - Crea

@protocol addToCartDelegate <NSObject>

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

@end

Invia e ti preghiamo di assegnare un delegato per visualizzare l'invio dei dati

[self.delegate addToCartAction:itemsModel isAdded:YES];

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. Implementare il metodo nella classe .m - (void) didRemoveCellWithTag: tag (NSInteger) {NSLog @ ("Tag% d", tag);

}


0

Cominciamo con un esempio, se acquistiamo un prodotto online, passa attraverso un processo come la spedizione / consegna gestita da diversi team, quindi se la spedizione viene completata, il team di spedizione dovrebbe notificare il team di consegna e dovrebbe essere una comunicazione uno a uno come trasmissione di queste informazioni sarebbe sovraccarico per altre persone / fornitore potrebbe voler passare queste informazioni solo alle persone richieste.

Quindi, se pensiamo in termini della nostra app, un evento può essere un ordine online e team diversi possono essere come visualizzazioni multiple.

Ecco il codice considera ShippingView come team di spedizione e DeliveryView come team di consegna:

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