Ich weiß, wie Delegierte arbeiten, und ich weiß, wie ich sie verwenden kann.
Aber wie erstelle ich sie?
Ich weiß, wie Delegierte arbeiten, und ich weiß, wie ich sie verwenden kann.
Aber wie erstelle ich sie?
Antworten:
Ein Objective-C-Delegat ist ein Objekt, dem der delegate
Eigenschaft ein anderes Objekt zugewiesen wurde . Um eine zu erstellen, definieren Sie eine Klasse, die die Delegatenmethoden implementiert, an denen Sie interessiert sind, und markieren diese Klasse als Implementierung des Delegatenprotokolls.
Angenommen, Sie haben eine UIWebView
. Wenn Sie die webViewDidStartLoad:
Methode des Delegaten implementieren möchten , können Sie eine Klasse wie die folgende erstellen:
@interface MyClass<UIWebViewDelegate>
// ...
@end
@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView {
// ...
}
@end
Anschließend können Sie eine Instanz von MyClass erstellen und als Delegat der Webansicht zuweisen:
MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;
Auf der UIWebView
Seite, hat es wahrscheinlich Code ähnlich wie diese zu sehen , ob die Delegierten reagieren auf die webViewDidStartLoad:
Nachricht mit respondsToSelector:
ihm gegebenenfalls und senden.
if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.delegate webViewDidStartLoad:self];
}
Die Delegate-Eigenschaft selbst wird normalerweise weak
(in ARC) oder assign
(vor ARC) deklariert , um Aufbewahrungsschleifen zu vermeiden, da der Delegat eines Objekts häufig einen starken Verweis auf dieses Objekt enthält. (Beispielsweise ist ein Ansichtscontroller häufig der Delegat einer darin enthaltenen Ansicht.)
Um Ihre eigenen Delegaten zu definieren, müssen Sie ihre Methoden irgendwo deklarieren, wie in den Apple Docs zu Protokollen beschrieben . Normalerweise deklarieren Sie ein formelles Protokoll. Die von UIWebView.h umschriebene Deklaration würde folgendermaßen aussehen:
@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end
Dies ist analog zu einer Schnittstelle oder einer abstrakten Basisklasse, da UIWebViewDelegate
in diesem Fall ein spezieller Typ für Ihren Delegaten erstellt wird. Delegierte Implementierer müssten dieses Protokoll übernehmen:
@interface MyClass <UIWebViewDelegate>
// ...
@end
Und dann implementieren Sie die Methoden im Protokoll. Bei Methoden, die im Protokoll als @optional
(wie die meisten delegierten Methoden) deklariert sind , müssen Sie dies überprüfen, -respondsToSelector:
bevor Sie eine bestimmte Methode aufrufen.
Delegierungsmethoden werden normalerweise beginnend mit dem Namen der delegierenden Klasse benannt und nehmen das delegierende Objekt als ersten Parameter. Sie verwenden auch oft eine Willens-, Soll- oder Did-Form. Also webViewDidStartLoad:
(erster Parameter ist die Webansicht) anstatt loadStarted
(ohne Parameter) zum Beispiel.
Anstatt jedes Mal zu überprüfen, ob ein Delegat auf einen Selektor antwortet, wenn wir ihm eine Nachricht senden möchten, können Sie diese Informationen zwischenspeichern, wenn Delegierte festgelegt werden. Eine sehr saubere Möglichkeit, dies zu tun, besteht darin, ein Bitfeld wie folgt zu verwenden:
@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
Dann können wir im Hauptteil überprüfen, ob unser Delegat Nachrichten verarbeitet, indem wir auf unsere delegateRespondsTo
Struktur zugreifen -respondsToSelector:
und nicht immer wieder senden .
Vor Protokolle gab, war es üblich , eine verwenden Kategorie auf , NSObject
um die Verfahren zu erklären Delegierter implementieren könnte. Zum Beispiel CALayer
tut dies immer noch:
@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end
Dies teilt dem Compiler mit, dass ein Objekt implementiert werden kann displayLayer:
.
Sie würden dann den gleichen -respondsToSelector:
Ansatz wie oben beschrieben verwenden, um diese Methode aufzurufen. Delegaten implementieren diese Methode und weisen die delegate
Eigenschaft zu, und das war's (es gibt keine Erklärung, dass Sie einem Protokoll entsprechen). Diese Methode ist in Apples Bibliotheken üblich, aber neuer Code sollte den oben beschriebenen moderneren Protokollansatz verwenden, da dieser Ansatz die Umweltverschmutzung verschmutzt NSObject
(was die automatische Vervollständigung weniger nützlich macht) und es dem Compiler erschwert, Sie vor Tippfehlern und ähnlichen Fehlern zu warnen.
unsigned int
Typ umwandeln, BOOL
da der Rückgabewert von delegate respondsToSelector
vom Typ ist BOOL
.
Die genehmigte Antwort ist großartig, aber wenn Sie nach einer 1-minütigen Antwort suchen, versuchen Sie Folgendes:
Die Datei MyClass.h sollte folgendermaßen aussehen (Delegatenzeilen mit Kommentaren hinzufügen!)
#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
Die Datei MyClass.m sollte so aussehen
#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
So verwenden Sie Ihren Delegaten in einer anderen Klasse (in diesem Fall UIViewController 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
Implementieren Sie die Delegate-Methode
- (void) myClassDelegateMethod: (MyClass *) sender {
NSLog(@"Delegates are great!");
}
myClass
in MyVC.m instanziiert?
Bei Verwendung der formalen Protokollmethode zum Erstellen der Delegatenunterstützung habe ich festgestellt, dass Sie eine ordnungsgemäße Typprüfung (allerdings Laufzeit, keine Kompilierungszeit) sicherstellen können, indem Sie Folgendes hinzufügen:
if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
[NSException raise:@"MyDelegate Exception"
format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}
in Ihrem Delegate Accessor (setDelegate) Code. Dies hilft, Fehler zu minimieren.
Bitte! In der folgenden einfachen Schritt-für-Schritt-Anleitung erfahren Sie, wie Delegates in iOS funktioniert.
Ich habe zwei ViewController erstellt (zum Senden von Daten von einem zum anderen)
Vielleicht entspricht dies eher dem, was Sie vermissen:
Wenn Sie aus einer C ++ - Perspektive kommen, sind die Delegierten etwas gewöhnungsbedürftig - aber im Grunde funktionieren sie einfach.
So funktioniert es: Sie legen ein Objekt fest, das Sie als Delegat an NSWindow geschrieben haben, aber Ihr Objekt verfügt nur über Implementierungen (Methoden) für eine oder einige der vielen möglichen Delegatmethoden. Es passiert also etwas und Sie NSWindow
möchten Ihr Objekt aufrufen. Es verwendet lediglich die respondsToSelector
Methode von Objective-c, um zu bestimmen, ob Ihr Objekt diese Methode aufrufen möchte, und ruft sie dann auf. So funktioniert Objective-C - Methoden werden bei Bedarf nachgeschlagen.
Es ist völlig trivial, dies mit Ihren eigenen Objekten zu tun, es ist nichts Besonderes los, Sie könnten zum Beispiel eines NSArray
von 27 Objekten haben, alle verschiedene Arten von Objekten, nur 18 von ihnen haben die Methode -(void)setToBue;
Die anderen 9 nicht. Also, um setToBlue
alle 18 anzurufen , die es tun müssen, so etwas:
for (id anObject in myArray)
{
if ([anObject respondsToSelector:@selector(@"setToBlue")])
[anObject setToBlue];
}
Das andere an Delegaten ist, dass sie nicht beibehalten werden, sodass Sie den Delegaten nil
in Ihrer MyClass dealloc
Methode immer festlegen müssen .
Als von Apple empfohlene bewährte Methode empfiehlt es sich, dass der Delegierte (der per Definition ein Protokoll ist) dem NSObject
Protokoll entspricht.
@protocol MyDelegate <NSObject>
...
@end
& Um optionale Methoden in Ihrem Delegaten zu erstellen (dh Methoden, die nicht unbedingt implementiert werden müssen), können Sie die @optional
Annotation wie folgt verwenden:
@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
Wenn Sie also Methoden verwenden, die Sie als optional angegeben haben, müssen Sie (in Ihrer Klasse) überprüfen, respondsToSelector
ob die Ansicht (die Ihrem Delegaten entspricht) Ihre optionalen Methoden tatsächlich implementiert hat oder nicht.
Ich denke, all diese Antworten sind sehr sinnvoll, wenn Sie die Delegierten verstehen. Persönlich kam ich aus dem Land von C / C ++ und davor aus prozeduralen Sprachen wie Fortran usw. Hier ist meine 2-minütige Darstellung ähnlicher Analoga im C ++ - Paradigma.
Wenn ich einem C ++ / Java-Programmierer die Delegierten erklären würde, würde ich sagen
Was sind Delegierte? Dies sind statische Zeiger auf Klassen innerhalb einer anderen Klasse. Sobald Sie einen Zeiger zugewiesen haben, können Sie Funktionen / Methoden in dieser Klasse aufrufen. Daher werden einige Funktionen Ihrer Klasse an eine andere Klasse "delegiert" (in C ++ world - Zeiger auf durch einen Klassenobjektzeiger).
Was sind Protokolle? Konzeptionell dient es einem ähnlichen Zweck wie die Header-Datei der Klasse, die Sie als Delegatenklasse zuweisen. Ein Protokoll ist eine explizite Methode, um zu definieren, welche Methoden in der Klasse implementiert werden müssen, deren Zeiger als Delegat innerhalb einer Klasse festgelegt wurde.
Wie kann ich in C ++ etwas Ähnliches tun? Wenn Sie dies in C ++ versuchen würden, würden Sie Zeiger auf Klassen (Objekte) in der Klassendefinition definieren und diese dann mit anderen Klassen verbinden, die zusätzliche Funktionen als Delegaten für Ihre Basisklasse bereitstellen. Diese Verkabelung muss jedoch im Code enthalten sein und ist ungeschickt und fehleranfällig. Ziel C geht lediglich davon aus, dass Programmierer diese Disziplin nicht am besten einhalten können, und bietet Compiler-Einschränkungen, um eine saubere Implementierung durchzusetzen.
Ein Delegat ist nur eine Klasse, die für eine andere Klasse arbeitet. Lesen Sie den folgenden Code für ein etwas albernes (aber hoffentlich aufschlussreiches) Spielplatzbeispiel, das zeigt, wie dies in Swift gemacht wird.
// 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!"
}
In der Praxis werden Delegierte häufig in den folgenden Situationen eingesetzt
Die Klassen müssen vorher nichts voneinander wissen, außer dass die Delegatenklasse dem erforderlichen Protokoll entspricht.
Ich empfehle dringend, die folgenden zwei Artikel zu lesen. Sie haben mir geholfen, die Delegierten noch besser zu verstehen als die Dokumentation .
Ok, dies ist nicht wirklich eine Antwort auf die Frage, aber wenn Sie nachschlagen, wie Sie Ihren eigenen Delegierten bilden können, könnte etwas viel Einfacheres eine bessere Antwort für Sie sein.
Ich setze meine Delegierten kaum um, weil ich sie selten brauche. Ich kann NUR EINEN Delegierten für ein Delegatenobjekt haben. Wenn Sie also möchten, dass Ihr Delegierter in eine Richtung kommuniziert / Daten weitergibt, sind Sie mit Benachrichtigungen viel besser dran.
NSNotification kann Objekte an mehr als einen Empfänger übergeben und ist sehr einfach zu verwenden. Es funktioniert so:
Die Datei MyClass.m sollte so aussehen
#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
So verwenden Sie Ihre Benachrichtigung in anderen Klassen: Fügen Sie eine Klasse als Beobachter hinzu:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];
Implementieren Sie den Selektor:
- (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
}
Vergessen Sie nicht, Ihre Klasse als Beobachter zu entfernen, wenn
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Angenommen, Sie haben eine Klasse, die Sie entwickelt haben, und möchten eine Delegate-Eigenschaft deklarieren, um sie benachrichtigen zu können, wenn ein Ereignis eintritt:
@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
Sie deklarieren also ein Protokoll in der MyClass
Header-Datei (oder einer separaten Header-Datei) und deklarieren die erforderlichen / optionalen Ereignishandler, die Ihr Delegat implementieren muss / sollte, und deklarieren dann eine Eigenschaft MyClass
vom Typ ( id< MyClassDelegate>
), was bedeutet, dass jede objektive c-Klasse konform ist Im Protokoll MyClassDelegate
werden Sie feststellen, dass die Delegate-Eigenschaft als schwach deklariert ist. Dies ist sehr wichtig, um den Aufbewahrungszyklus zu verhindern (meistens behält der Delegate dieMyClass
Wenn Sie Instanz bei. Wenn Sie den Delegaten als Retain deklariert haben, behalten sich beide gegenseitig und keine von ihnen werden jemals veröffentlicht werden).
Sie werden auch feststellen, dass die Protokollmethoden die MyClass
Instanz als Parameter an den Delegaten übergeben. Dies ist eine bewährte Methode für den Fall, dass der Delegat einige Methoden für eine MyClass
Instanz aufrufen möchte, und hilft auch, wenn sich der Delegat MyClassDelegate
für mehrere MyClass
Instanzen deklariert , z. B. wenn Sie mehrere haben UITableView's
Instanzen in Ihrem ViewController
und erklärt sich als UITableViewDelegate
zu allen von ihnen.
und in Ihrem MyClass
benachrichtigen Sie den Delegierten mit deklarierten Ereignissen wie folgt:
if([_delegate respondsToSelector:@selector(myClass: requiredEventHandlerWithParameter:)])
{
[_delegate myClass:self requiredEventHandlerWithParameter:(ParamType*)param];
}
Sie überprüfen zunächst, ob Ihr Delegat auf die Protokollmethode reagiert, die Sie aufrufen möchten, falls der Delegat sie nicht implementiert und die App dann abstürzt (auch wenn die Protokollmethode erforderlich ist).
Hier ist eine einfache Methode zum Erstellen von Delegaten
Protokoll in .h-Datei erstellen. Stellen Sie sicher, dass dies vor dem Protokoll mit @class gefolgt vom Namen des UIViewControllers definiert ist< As the protocol I am going to use is UIViewController class>.
Schritt: 1: Erstellen Sie ein neues Klassenprotokoll mit dem Namen "YourViewController", das die Unterklasse der UIViewController-Klasse ist, und weisen Sie diese Klasse dem zweiten ViewController zu.
Schritt: 2: Gehen Sie zur Datei "YourViewController" und ändern Sie sie wie folgt:
#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
Die im Protokollverhalten definierten Methoden können mit @optional und @required als Teil der Protokolldefinition gesteuert werden.
Schritt: 3: Implementierung des Delegierten
#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, ob die Methode definiert wurde, bevor du sie aufrufst
- (void) someMethodToCallDelegate {
if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
[self.delegate delegateMethodName:self];
}
}
Um Ihren eigenen Delegaten zu erstellen, müssen Sie zunächst ein Protokoll erstellen und die erforderlichen Methoden deklarieren, ohne sie zu implementieren. Implementieren Sie dieses Protokoll dann in Ihre Header-Klasse, in der Sie den Delegaten oder die Delegatmethoden implementieren möchten.
Ein Protokoll muss wie folgt deklariert werden:
@protocol ServiceResponceDelegate <NSObject>
- (void) serviceDidFailWithRequestType:(NSString*)error;
- (void) serviceDidFinishedSucessfully:(NSString*)success;
@end
Dies ist die Serviceklasse, in der eine Aufgabe ausgeführt werden sollte. Es zeigt, wie Sie einen Delegaten definieren und wie Sie den Delegaten festlegen. In der Implementierungsklasse werden nach Abschluss der Aufgabe die Methoden des Delegaten aufgerufen.
@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
Dies ist die Hauptansichtsklasse, von der aus die Serviceklasse aufgerufen wird, indem der Delegat auf sich selbst gesetzt wird. Außerdem ist das Protokoll in der Header-Klasse implementiert.
@interface viewController: UIViewController <ServiceResponceDelegate>
{
ServiceClass* _service;
}
- (void) go;
@end
@implementation viewController
//
//some methods
//
- (void) go
{
_service = [[ServiceClass alloc] init];
[_service setDelegate:self];
[_service someTask];
}
Das war's, und durch die Implementierung von Delegate-Methoden in dieser Klasse wird die Steuerung wieder hergestellt, sobald die Operation / Aufgabe abgeschlossen ist.
Haftungsausschluss: Dies ist die Swift
Version zum Erstellen eines delegate
.
Was sind Delegierte? … In der Softwareentwicklung gibt es allgemeine wiederverwendbare Lösungsarchitekturen, die dazu beitragen, häufig auftretende Probleme in einem bestimmten Kontext zu lösen. Diese „Vorlagen“ werden sozusagen am besten als Entwurfsmuster bezeichnet. Delegaten sind ein Entwurfsmuster, mit dem ein Objekt Nachrichten an ein anderes Objekt senden kann, wenn ein bestimmtes Ereignis eintritt. Stellen Sie sich vor, ein Objekt A ruft ein Objekt B auf, um eine Aktion auszuführen. Sobald die Aktion abgeschlossen ist, sollte Objekt A wissen, dass B die Aufgabe abgeschlossen hat, und die erforderlichen Maßnahmen ergreifen. Dies kann mit Hilfe von Delegierten erreicht werden!
Zur besseren Erklärung zeige ich Ihnen, wie Sie mit Swift in einer einfachen Anwendung einen benutzerdefinierten Delegaten erstellen, der Daten zwischen Klassen weitergibt. Laden Sie zunächst dieses Starterprojekt herunter oder klonen Sie es und führen Sie es aus!
Sie können eine App mit zwei Klassen sehen, ViewController A
und ViewController B
. B hat zwei Ansichten, die beim Tippen die Hintergrundfarbe von ändern ViewController
, nichts zu kompliziert, oder? Nun wollen wir auf einfache Weise darüber nachdenken, auch die Hintergrundfarbe von Klasse A zu ändern, wenn auf die Ansichten von Klasse B getippt wird.
Das Problem ist, dass diese Ansichten Teil der Klasse B sind und keine Ahnung von Klasse A haben. Daher müssen wir einen Weg finden, um zwischen diesen beiden Klassen zu kommunizieren, und hier erstrahlt die Delegation. Ich habe die Implementierung in 6 Schritte unterteilt, damit Sie diese bei Bedarf als Spickzettel verwenden können.
Schritt 1: Suchen Sie in der ClassBVC-Datei nach der Pragma-Markierung Schritt 1 und fügen Sie diese hinzu
//MARK: step 1 Add Protocol here.
protocol ClassBVCDelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}
Der erste Schritt besteht darin, ein protocol
Protokoll zu erstellen . In diesem Fall erstellen wir das Protokoll in Klasse B. Innerhalb des Protokolls können Sie so viele Funktionen erstellen, wie Sie möchten, basierend auf den Anforderungen Ihrer Implementierung. In diesem Fall haben wir nur eine einfache Funktion, die ein optionales UIColor
Argument akzeptiert . delegate
In diesem Fall empfiehlt es sich, Ihre Protokolle zu benennen, indem Sie das Wort am Ende des Klassennamens hinzufügen ClassBVCDelegate
.
Schritt 2: Suchen Sie nach der Pragma-Markierung in Schritt 2 ClassVBC
und fügen Sie diese hinzu
//MARK: step 2 Create a delegate property here.
weak var delegate: ClassBVCDelegate?
Hier erstellen wir nur eine Delegate-Eigenschaft für die Klasse. Diese Eigenschaft muss den protocol
Typ übernehmen und sollte optional sein. Außerdem sollten Sie das schwache Schlüsselwort vor der Eigenschaft hinzufügen, um Speicherzyklen und potenzielle Speicherverluste zu vermeiden. Wenn Sie nicht wissen, was dies bedeutet, machen Sie sich vorerst keine Sorgen. Denken Sie daran, dieses Schlüsselwort hinzuzufügen.
Schritt 3: Suchen Sie nach der Pragma-Markierung Schritt 3 im handleTap method
in ClassBVC
und fügen Sie diese hinzu
//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(tapGesture.view?.backgroundColor)
Eine Sache, die Sie wissen sollten, führen Sie die App aus und tippen Sie auf eine Ansicht. Sie werden kein neues Verhalten sehen und das ist richtig, aber ich möchte darauf hinweisen, dass die App nicht abstürzt, wenn der Delegat aufgerufen wird, und Das liegt daran, dass wir es als optionalen Wert erstellen und es deshalb nicht abstürzt, selbst wenn der Delegierte noch nicht existiert. Gehen wir jetzt zur ClassAVC
Akte und machen es, der Delegierte.
Schritt 4: Suchen Sie in der handleTap-Methode in nach der Pragma-Markierung Schritt 4 ClassAVC
und fügen Sie diese wie neben Ihren Klassentyp ein.
//MARK: step 4 conform the protocol here.
class ClassAVC: UIViewController, ClassBVCDelegate {
}
Jetzt hat ClassAVC die ClassBVCDelegate
Protokoll übernommen hat, können Sie sehen, dass Ihr Compiler Ihnen einen Fehler mit der Meldung "Typ" ClassAVC entspricht nicht dem Protokoll "ClassBVCDelegate" anzeigt. Dies bedeutet nur, dass Sie die Methoden des Protokolls noch nicht verwendet haben. Stellen Sie sich das vor Wenn Klasse A das Protokoll annimmt, ist das wie das Unterzeichnen eines Vertrags mit Klasse B und dieser Vertrag besagt: "Jede Klasse, die mich adoptiert, MUSS meine Funktionen nutzen!"
Kurzer Hinweis: Wenn Sie aus einem Objective-C
Hintergrund stammen, denken Sie wahrscheinlich, dass Sie diesen Fehler auch beenden können, indem Sie diese Methode optional machen. Zu meiner und wahrscheinlich Ihrer Überraschung wird die Swift
Sprache jedoch nicht optional protocols
unterstützt. Wenn Sie dies möchten, können Sie sie erstellen eine Erweiterung für Ihreprotocol
oder verwenden Sie das Schlüsselwort @objc in Ihrer protocol
Implementierung.
Persönlich, wenn ich ein Protokoll mit verschiedenen optionalen Methoden erstellen muss, würde ich es vorziehen, es in verschiedene zu unterteilen protocols
. Auf diese Weise folge ich dem Konzept, meinen Objekten eine einzige Verantwortung zu übertragen, aber es kann je nach spezifischer Implementierung variieren.
hier ist ein guter Artikel über optionale Methoden.
Schritt 5: Suchen Sie nach der Pragma-Markierung Schritt 5 in der Methode zur Vorbereitung auf den Übergang und fügen Sie diese hinzu
//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
}
Hier erstellen wir nur eine Instanz von ClassBVC
und weisen ihren Delegierten dem Selbst zu, aber was ist das Selbst hier? Nun, Selbst ist das, ClassAVC
was delegiert wurde!
Schritt 6: Suchen Sie abschließend nach dem Pragma in Schritt 6 ClassAVC
und verwenden Sie die Funktionen von protocol
, beginnen Sie mit der Eingabe von func changeBackgroundColor, und Sie werden sehen, dass es automatisch für Sie vervollständigt wird. Sie können eine beliebige Implementierung hinzufügen. In diesem Beispiel ändern wir nur die Hintergrundfarbe und fügen diese hinzu.
//MARK: step 6 finally use the method of the contract
func changeBackgroundColor(_ color: UIColor?) {
view.backgroundColor = color
}
Führen Sie jetzt die App aus!
Delegates
sind überall und Sie verwenden sie wahrscheinlich ohne Vorankündigung. Wenn Sie eine tableview
in der Vergangenheit verwendete Delegation erstellen , lösen viele Klassen von UIKIT
Arbeiten um sie herum und viele andere frameworks
diese Hauptprobleme.
Herzlichen Glückwunsch, Sie implementieren gerade einen benutzerdefinierten Delegaten. Ich weiß, dass Sie wahrscheinlich denken, so viel Ärger nur dafür? Nun, Delegation ist ein sehr wichtiges Entwurfsmuster, um zu verstehen, ob Sie ein werden möchteniOS
Entwickler werden , und denken Sie immer daran, dass sie eine Eins-zu-Eins-Beziehung zwischen Objekten haben.
Das Original-Tutorial finden Sie hier
Die Antwort wird tatsächlich beantwortet, aber ich möchte Ihnen einen "Spickzettel" zum Erstellen eines Delegaten geben:
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;
Methode:
-(void)delegateMEthod: (ArgType) arg{
}
Aus meiner Sicht erstellen Sie eine separate Klasse für diese Delegatmethode, und Sie können sie verwenden, wo Sie möchten.
in meiner benutzerdefinierten DropDownClass.h
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;
danach in.m Datei Array mit Objekten erstellen,
- (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];
}
}
}];
}
Hier sind alle für die benutzerdefinierte Delegatenklasse festgelegt. Danach können Sie diese Delegatenmethode verwenden, wo Sie möchten. Zum Beispiel ...
in meinem anderen viewcontroller danach importieren
Erstellen Sie eine Aktion zum Aufrufen dieser Delegatenmethode
- (IBAction)dropDownBtn1Action:(id)sender {
DropDownViewController *vehicleModelDropView = [[DropDownViewController alloc]init];
vehicleModelDropView.dropDownType = DDCITY;
vehicleModelDropView.delegate = self;
[self presentViewController:vehicleModelDropView animated:YES completion:nil];
}
danach Aufruf delegate Methode wie folgt
- (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;
}
}
Delegieren: - Erstellen
@protocol addToCartDelegate <NSObject>
-(void)addToCartAction:(ItemsModel *)itemsModel isAdded:(BOOL)added;
@end
Senden Sie und weisen Sie den Delegierten zu, um anzuzeigen, dass Sie Daten senden
[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. Implementieren Sie die Methode in der Klasse .m - (void) didRemoveCellWithTag: (NSInteger) tag {NSLog @ ("Tag% d", tag);
}}
Beginnen wir mit einem Beispiel: Wenn wir ein Produkt online kaufen, durchläuft es einen Prozess wie Versand / Lieferung, der von verschiedenen Teams abgewickelt wird. Wenn der Versand abgeschlossen ist, sollte das Versandteam das Lieferteam benachrichtigen und es sollte eine Eins-zu-Eins-Kommunikation sein, um diese Informationen zu senden Dies wäre ein Overhead für andere Personen / Anbieter, die diese Informationen möglicherweise nur an erforderliche Personen weitergeben möchten.
Wenn wir also in Bezug auf unsere App denken, kann eine Veranstaltung eine Online-Bestellung sein und verschiedene Teams können wie mehrere Ansichten sein.
Hier ist der Code, der ShippingView als Versandteam und DeliveryView als Lieferteam betrachtet:
//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
//
}
}