So come funzionano i delegati e so come posso usarli.
Ma come li creo?
So come funzionano i delegati e so come posso usarli.
Ma come li creo?
Risposte:
Un delegato Objective-C è un oggetto che è stato assegnato alla delegate
proprietà 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 UIWebView
lato, 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.)
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, UIWebViewDelegate
in 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.
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.
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 delegateRespondsTo
struttura, piuttosto che inviando -respondsToSelector:
più e più volte.
Prima di protocolli esistevano, era comune l'uso di un categoria su NSObject
di dichiarare i metodi di un delegato potrebbe implementare. Ad esempio, CALayer
continua 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 delegate
proprietà, 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.
unsigned int
tipo in BOOL
quanto il valore restituito delegate respondsToSelector
è di tipo BOOL
.
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!");
}
myClass
istanziata in MyVC.m?
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.
Per favore! controlla di seguito un semplice tutorial passo passo per capire come funzionano i delegati in iOS.
Ho creato due ViewController (per l'invio di dati da uno all'altro)
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 NSWindow
vuole chiamare il tuo oggetto - usa solo il respondsToSelector
metodo 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 NSArray
di 27 oggetti, tutti i diversi tipi di oggetti, solo 18 alcuni hanno il metodo -(void)setToBue;
Gli altri 9 no. Quindi, per chiamare setToBlue
tutti 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 nil
nel proprio MyClass dealloc
metodo.
Come buona pratica raccomandata da Apple, è bene che il delegato (che è un protocollo, per definizione), si conformi al NSObject
protocollo.
@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' @optional
annotazione 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 respondsToSelector
se la vista (conforme al proprio delegato) ha effettivamente implementato i metodi opzionali o meno.
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.
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
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 .
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];
}
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 MyClass
file 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 MyClass
di 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' MyClass
istanza al delegato come parametro, questa è la migliore pratica nel caso in cui il delegato desideri chiamare alcuni metodi MyClass
sull'istanza e aiuta anche quando il delegato si dichiara MyClassDelegate
su più MyClass
istanze, come quando hai più UITableView's
istanze nel tuo ViewController
e si dichiara UITableViewDelegate
a tutte.
e all'interno della tua MyClass
notifica 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).
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];
}
}
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à.
Disclaimer: questa è la Swift
versione 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 A
e 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 UIColor
come argomento. È buona norma nominare i protocolli aggiungendo la parola delegate
alla fine del nome della classe, in questo caso ClassBVCDelegate
.
passaggio 2: cercare il segno pragma passaggio 2 in ClassVBC
e 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 method
in ClassBVC
e 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 ClassAVC
file e rendiamolo delegato.
passaggio 4: cercare il segno pragma passaggio 4 all'interno del metodo handleTap ClassAVC
e 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 ClassBVCDelegate
protocollo, 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-C
sfondo probabilmente stai pensando che puoi anche chiudere quell'errore rendendo quel metodo facoltativo, ma per mia sorpresa, e probabilmente il tuo, la Swift
lingua non supporta gli optional protocols
, se vuoi farlo puoi creare un'estensione per la tua protocol
o 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 ClassBVC
e 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 ClassAVC
e 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!
Delegates
sono ovunque e probabilmente li usi anche senza preavviso, se tableview
in passato hai creato una delegazione che hai usato, molte classi di UIKIT
lavori intorno a loro e molte altre frameworks
, risolvono questi problemi principali.
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 iOS
sviluppatore e tieni sempre presente che hanno una relazione uno a uno tra gli oggetti.
Puoi vedere il tutorial originale qui
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
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{
}
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;
}
}
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];
//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);
}
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
//
}
}