Wiem, jak pracują delegaci i wiem, jak mogę ich używać.
Ale jak je utworzyć?
Wiem, jak pracują delegaci i wiem, jak mogę ich używać.
Ale jak je utworzyć?
Odpowiedzi:
Delegat Celu-C to obiekt, który został przypisany do delegate
właściwości inny obiekt. Aby je utworzyć, zdefiniuj klasę, która implementuje metody delegowania, którymi jesteś zainteresowany, i oznacz tę klasę jako implementującą protokół delegowania.
Załóżmy na przykład, że masz UIWebView
. Jeśli chcesz zaimplementować webViewDidStartLoad:
metodę jego delegata , możesz utworzyć taką klasę:
@interface MyClass<UIWebViewDelegate>
// ...
@end
@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView {
// ...
}
@end
Następnie możesz utworzyć instancję MyClass i przypisać ją jako delegata widoku internetowego:
MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;
Z UIWebView
boku prawdopodobnie ma kod podobny do tego, aby zobaczyć, czy delegat odpowiada nawebViewDidStartLoad:
wiadomość za pomocą respondsToSelector:
i wyślij go w razie potrzeby.
if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.delegate webViewDidStartLoad:self];
}
Sama właściwość delegowania jest zwykle zadeklarowana weak
(w ARC) lub assign
(przed ARC), aby uniknąć zachowywania pętli, ponieważ delegat obiektu często ma silne odniesienie do tego obiektu. (Na przykład kontroler widoku jest często delegatem widoku, który zawiera.)
Aby zdefiniować własnych delegatów, musisz gdzieś zadeklarować ich metody, jak omówiono w Dokumentach Apple na temat protokołów . Zwykle deklarujesz formalny protokół. Deklaracja parafrazowana z UIWebView.h wyglądałaby następująco:
@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end
Jest to analogiczne do interfejsu lub abstrakcyjnej klasy bazowej, ponieważ UIWebViewDelegate
w tym przypadku tworzy specjalny typ dla twojego delegata . Delegujący realizatorzy musieliby przyjąć ten protokół:
@interface MyClass <UIWebViewDelegate>
// ...
@end
A następnie zaimplementuj metody w protokole. Dla metod zadeklarowanych w protokole jako@optional
(jak większość metod delegowania) należy to sprawdzić -respondsToSelector:
przed wywołaniem konkretnej metody.
Metody delegowania są zwykle nazywane zaczynając od nazwy klasy delegującej i przyjmują obiekt delegujący jako pierwszy parametr. Często używają też testamentu, formularza lub formularza. Na przykład webViewDidStartLoad:
(pierwszy parametr to widok strony internetowej) zamiast loadStarted
(bez parametrów) na przykład.
Zamiast sprawdzać, czy delegat odpowiada na selektor za każdym razem, gdy chcemy wysłać wiadomość, możesz buforować te informacje, gdy delegaci są ustawieni. Jednym z bardzo czystych sposobów, aby to zrobić, jest użycie pola bitowego w następujący sposób:
@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
Następnie w ciele możemy sprawdzić, czy nasz delegat obsługuje wiadomości, uzyskując dostęp do naszego delegateRespondsTo
struktury, a nie wysyłając -respondsToSelector:
je w kółko.
Przed istnieniem protokołów powszechne było używanie kategorii w NSObject
celu zadeklarowania metod, które delegat mógłby wdrożyć. Na przykład CALayer
nadal robi to:
@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end
To informuje kompilator, że dowolny obiekt może zaimplementować displayLayer:
.
Następnie użyłbyś tego samego -respondsToSelector:
podejścia, jak opisano powyżej, aby wywołać tę metodę. Delegaci implementują tę metodę i przypisują delegate
właściwość i to wszystko (nie ma deklaracji zgodności z protokołem). Ta metoda jest powszechna w bibliotekach Apple, ale nowy kod powinien używać powyższego bardziej nowoczesnego protokołu, ponieważ to podejście zanieczyszcza NSObject
(co sprawia, że autouzupełnianie jest mniej przydatne) i utrudnia kompilatorowi ostrzeganie o literówkach i podobnych błędach.
unsigned int
typ, BOOL
ponieważ zwracana wartość delegate respondsToSelector
jest typu BOOL
.
Zatwierdzona odpowiedź jest świetna, ale jeśli szukasz odpowiedzi trwającej 1 minutę, spróbuj tego:
Plik MyClass.h powinien wyglądać tak (dodaj delegowane linie z komentarzami!)
#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
Plik MyClass.m powinien wyglądać tak
#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
Aby użyć delegata w innej klasie (w tym przypadku UIViewController o nazwie 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
Wdrożenie metody delegowania
- (void) myClassDelegateMethod: (MyClass *) sender {
NSLog(@"Delegates are great!");
}
myClass
tworzona instancja w MyVC.m?
Korzystając z metody formalnego protokołu do tworzenia obsługi delegatów, zauważyłem, że możesz zapewnić poprawne sprawdzanie typu (aczkolwiek, środowisko wykonawcze, a nie czas kompilacji), dodając coś takiego:
if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
[NSException raise:@"MyDelegate Exception"
format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}
w kodzie delegata (setDelegate). Pomaga to zminimalizować błędy.
Proszę! zapoznaj się z prostym samouczkiem krok po kroku, aby zrozumieć, jak działają Delegaci w iOS.
Utworzyłem dwóch ViewControllerów (do przesyłania danych z jednego do drugiego)
Może jest to bardziej zgodne z tym, czego brakuje:
Jeśli pochodzisz z punktu widzenia podobnego do C ++, delegaci trochę się przyzwyczajają - ale w zasadzie „po prostu działają”.
Działa to tak, że ustawiasz obiekt, który napisałeś jako delegat do NSWindow, ale twój obiekt ma tylko implementacje (metody) dla jednej lub kilku z wielu możliwych metod delegowania. Więc coś się dzieje i NSWindow
chce nazwać twój obiekt - po prostu używa Celu-crespondsToSelector
metody aby ustalić, czy Twój obiekt chce tę metodę, a następnie ją wywołuje. Tak działa cel C - metody są sprawdzane na żądanie.
Robienie tego z własnymi obiektami jest całkowicie trywialne, nie dzieje się nic specjalnego, możesz na przykład mieć NSArray
27 obiektów, różnego rodzaju obiekty, tylko 18 z nich ma metodę, -(void)setToBue;
a pozostałe 9 nie. Aby wezwać setToBlue
wszystkich 18, którzy tego potrzebują, coś takiego:
for (id anObject in myArray)
{
if ([anObject respondsToSelector:@selector(@"setToBlue")])
[anObject setToBlue];
}
Inną rzeczą dotyczącą delegatów jest to, że nie są one przechowywane, więc zawsze musisz ustawić delegata nil
w MyClass dealloc
metodzie.
Zgodnie z dobrą praktyką zalecaną przez Apple, delegatowi (który z definicji jest protokołem) jest zgodne z NSObject
protokołem.
@protocol MyDelegate <NSObject>
...
@end
i aby utworzyć opcjonalne metody w ramach swojego uczestnika (tj. metody, które niekoniecznie muszą zostać zaimplementowane), możesz użyć @optional
adnotacji w następujący sposób:
@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
Tak więc, gdy używasz metod, które określiłeś jako opcjonalne, musisz (w swojej klasie) sprawdzić, respondsToSelector
czy widok (zgodny z twoim delegatem) faktycznie zaimplementował twoje opcjonalne metody, czy nie.
Myślę, że wszystkie te odpowiedzi mają sens, gdy zrozumiesz delegatów. Osobiście pochodzę z krainy C / C ++, a wcześniej języki proceduralne, takie jak Fortran itp., Więc oto moje 2-minutowe podejście do znalezienia podobnych analogów w paradygmacie C ++.
Gdybym miał wyjaśnić delegatów programistom C ++ / Java, powiedziałbym
Kim są delegaci? Są to statyczne wskaźniki do klas w innej klasie. Po przypisaniu wskaźnika można wywoływać funkcje / metody w tej klasie. Dlatego niektóre funkcje twojej klasy są „delegowane” (w świecie C ++ - wskaźnik na obiekt klasy) do innej klasy.
Jakie są protokoły? Pod względem koncepcyjnym służy on do podobnego celu, co plik nagłówkowy klasy, którą przypisujesz jako klasę delegowaną. Protokół jest jawnym sposobem definiowania metod, które należy zaimplementować w klasie, której wskaźnik został ustawiony jako delegat w klasie.
Jak mogę zrobić coś podobnego w C ++? Jeśli spróbujesz to zrobić w C ++, zdefiniuj wskaźniki do klas (obiektów) w definicji klasy, a następnie połącz je z innymi klasami, które zapewnią dodatkowe funkcje jako delegaty do twojej klasy podstawowej. Ale to okablowanie musi być obsługiwane w kodzie i będzie niezdarne i podatne na błędy. Cel C zakłada po prostu, że programiści nie są najlepsi w utrzymywaniu tego deklinacji i zapewnia ograniczenia kompilatora w celu wymuszenia czystej implementacji.
Delegat to po prostu klasa, która wykonuje pewne prace dla innej klasy. Przeczytaj poniższy kod dla nieco głupiego (ale mam nadzieję, że pouczającego) przykładu placu zabaw, który pokazuje, jak to się robi w 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!"
}
W praktyce delegaci są często wykorzystywani w następujących sytuacjach
Klasy nie muszą wcześniej wiedzieć o sobie niczego poza tym, że klasa delegowana jest zgodna z wymaganym protokołem.
Bardzo polecam przeczytanie dwóch poniższych artykułów. Pomogli mi zrozumieć delegatów jeszcze lepiej niż dokumentacja .
Ok, to nie jest tak naprawdę odpowiedź na pytanie, ale jeśli zastanawiasz się, jak stworzyć własnego delegata, być może coś znacznie prostszego może być lepszą odpowiedzią dla Ciebie.
Prawie nie wdrażam moich delegatów, ponieważ rzadko potrzebuję. Mogę mieć TYLKO JEDEN delegata dla obiektu delegowanego. Jeśli więc chcesz, aby Twój delegat komunikował się / przekazywał dane w jedną stronę, to znacznie lepiej niż w przypadku powiadomień.
NSNotification może przekazywać obiekty do więcej niż jednego odbiorcy i jest bardzo łatwy w użyciu. Działa to tak:
Plik MyClass.m powinien wyglądać tak
#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
Aby użyć powiadomienia w innych klasach: Dodaj klasę jako obserwatora:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];
Zaimplementuj 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
}
Nie zapomnij usunąć swojej klasy jako obserwatora, jeśli
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
powiedzmy, że masz klasę, którą opracowałeś i chcesz zadeklarować właściwość delegata, aby móc powiadomić ją o zdarzeniu:
@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
więc deklarujesz protokół w MyClass
pliku nagłówkowym (lub osobnym pliku nagłówkowym) i deklarujesz wymagane / opcjonalne procedury obsługi zdarzeń, które musi / powinien wdrożyć Twój delegat, a następnie deklarujesz właściwość MyClass
type ( id< MyClassDelegate>
), co oznacza dowolną obiektywną klasę c, która jest zgodna z protokół MyClassDelegate
, zauważysz, że właściwość delegata jest zadeklarowana jako słaba, jest to bardzo ważne, aby zapobiec cyklowi przechowywania (najczęściej delegat zachowujeMyClass
instancję, więc jeśli zadeklarujesz delegata jako zachowaj, oba zachowają się nawzajem i żadne z nich zostanie kiedykolwiek wydany).
zauważysz również, że metody protokołu przekazują MyClass
instancję do delegata jako parametr, jest to najlepsza praktyka w przypadku, gdy delegat chce wywołać niektóre metody w MyClass
instancji, a także pomaga, gdy delegat deklaruje się jako MyClassDelegate
do wielu MyClass
instancji, na przykład gdy masz wiele UITableView's
wystąpień w twoim ViewController
i deklaruje się jako UITableViewDelegate
dla nich wszystkich.
aw swoim wnętrzu MyClass
powiadamiasz delegata o zadeklarowanych wydarzeniach w następujący sposób:
if([_delegate respondsToSelector:@selector(myClass: requiredEventHandlerWithParameter:)])
{
[_delegate myClass:self requiredEventHandlerWithParameter:(ParamType*)param];
}
najpierw sprawdzisz, czy delegat odpowiada na metodę protokołu, którą zamierzasz wywołać, na wypadek, gdyby delegat jej nie zaimplementował i aplikacja ulegnie awarii (nawet jeśli wymagana jest metoda protokołu).
Oto prosta metoda tworzenia delegatów
Utwórz protokół w pliku .h. Upewnij się, że jest zdefiniowany przed protokołem za pomocą @class, po którym następuje nazwa UIViewController< As the protocol I am going to use is UIViewController class>.
Krok: 1: Utwórz nowy protokół klasy o nazwie „YourViewController”, który będzie podklasą klasy UIViewController i przypisz tę klasę do drugiego ViewController.
Krok: 2: Przejdź do pliku „YourViewController” i zmodyfikuj go w następujący sposób:
#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
Metody zdefiniowane w zachowaniu protokołu można kontrolować za pomocą @optional i @required w ramach definicji protokołu.
Krok: 3: Wdrożenie delegata
#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
// sprawdź, czy metoda została zdefiniowana przed jej wywołaniem
- (void) someMethodToCallDelegate {
if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
[self.delegate delegateMethodName:self];
}
}
Aby utworzyć własnego delegata, najpierw musisz utworzyć protokół i zadeklarować niezbędne metody, bez implementacji. A następnie zaimplementuj ten protokół w klasie nagłówka, w której chcesz zaimplementować metody delegowania lub delegowania metod.
Protokół należy zadeklarować jak poniżej:
@protocol ServiceResponceDelegate <NSObject>
- (void) serviceDidFailWithRequestType:(NSString*)error;
- (void) serviceDidFinishedSucessfully:(NSString*)success;
@end
Jest to klasa usług, w której należy wykonać pewne zadanie. Pokazuje, jak zdefiniować delegata i jak ustawić delegata. W klasie implementacji po zakończeniu zadania delegowane metody są wywoływane.
@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
Jest to główna klasa widoku, z której wywoływana jest klasa usługi poprzez ustawienie delegata na siebie. A także protokół jest zaimplementowany w klasie nagłówka.
@interface viewController: UIViewController <ServiceResponceDelegate>
{
ServiceClass* _service;
}
- (void) go;
@end
@implementation viewController
//
//some methods
//
- (void) go
{
_service = [[ServiceClass alloc] init];
[_service setDelegate:self];
[_service someTask];
}
To wszystko, a dzięki wdrożeniu metod delegowania w tej klasie kontrola powróci po zakończeniu operacji / zadania.
Zastrzeżenie: jest to Swift
wersja, jak utworzyćdelegate
.
Kim więc są delegaci? … W rozwoju oprogramowania istnieją ogólne architektury rozwiązań wielokrotnego użytku, które pomagają rozwiązać często występujące problemy w danym kontekście, te „szablony”, że tak powiem, są najlepiej znane jako wzorce projektowe. Delegaci to wzorzec projektowy, który pozwala jednemu obiektowi wysyłać wiadomości do innego obiektu, gdy nastąpi określone zdarzenie. Wyobraź sobie, że obiekt A wywołuje obiekt B w celu wykonania akcji. Po zakończeniu akcji obiekt A powinien wiedzieć, że B wykonał zadanie i podjąć niezbędne działania, można to osiągnąć przy pomocy delegatów!
Dla lepszego wyjaśnienia pokażę, jak utworzyć niestandardowego uczestnika, który przesyła dane między klasami, za pomocą Swift w prostej aplikacji, zacznij od pobrania lub klonowania tego projektu startowego i uruchom go!
Możesz zobaczyć aplikację z dwiema klasami ViewController A
i ViewController B
. B ma dwa widoki, które po dotknięciu zmieniają kolor tła ViewController
, nic zbyt skomplikowanego, prawda? Cóż, teraz pomyślmy w prosty sposób, aby zmienić kolor tła klasy A, gdy widoki na klasie B są stuknięte.
Problem polega na tym, że te widoki są częścią klasy B i nie mają pojęcia o klasie A, więc musimy znaleźć sposób na komunikację między tymi dwiema klasami i tam właśnie świeci delegacja. Podzieliłem implementację na 6 kroków, dzięki czemu możesz użyć tego jako ściągawki, gdy jej potrzebujesz.
krok 1: Poszukaj znaku pragma krok 1 w pliku ClassBVC i dodaj go
//MARK: step 1 Add Protocol here.
protocol ClassBVCDelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}
Pierwszym krokiem jest utworzenie protocol
, w tym przypadku utworzymy protokół w klasie B, w ramach protokołu możesz utworzyć dowolną liczbę funkcji w zależności od wymagań twojej implementacji. W tym przypadku mamy tylko jedną prostą funkcję, która przyjmuje opcjonalną UIColor
jako argument. Dobrą praktyką jest nazywanie protokołów przez dodanie słowa delegate
na końcu nazwy klasy, w tym przypadku ClassBVCDelegate
.
krok 2: Poszukaj pragmy zaznacz krok 2 w ClassVBC
i dodaj to
//MARK: step 2 Create a delegate property here.
weak var delegate: ClassBVCDelegate?
Tutaj po prostu tworzymy właściwość delegowania dla klasy, ta właściwość musi przyjąć właściwość protocol
typ i powinna być opcjonalna. Ponadto należy dodać słabe słowo kluczowe przed właściwością, aby uniknąć zachowywania cykli i potencjalnych wycieków pamięci, jeśli nie wiesz, co to oznacza, nie martw się na razie, pamiętaj tylko o dodaniu tego słowa kluczowego.
krok 3: Spójrz na etapie znaku Pragma 3 wewnątrz handleTap method
w ClassBVC
Dodaj
//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(tapGesture.view?.backgroundColor)
Jedną rzeczą, którą powinieneś wiedzieć, uruchomić aplikację i dotknąć dowolnego widoku, nie zobaczysz żadnego nowego zachowania i jest to poprawne, ale chcę podkreślić, że aplikacja nie zawiesza się, gdy wywoływany jest delegat, i to dlatego, że tworzymy go jako wartość opcjonalną i dlatego nie zawiedzie, nawet przekazane dane jeszcze nie istnieją. Przejdźmy teraz do ClassAVC
akt i dokonajmy tego, delegowani.
krok 4: Poszukaj znaku pragma krok 4 wewnątrz metody handleTap w ClassAVC
i dodaj to obok swojego typu klasy w ten sposób.
//MARK: step 4 conform the protocol here.
class ClassAVC: UIViewController, ClassBVCDelegate {
}
Teraz ClassAVC przyjął ClassBVCDelegate
protokół, widać, że twój kompilator wyświetla błąd, który mówi „Typ” ClassAVC nie jest zgodny z protokołem „ClassBVCDelegate”, a to oznacza tylko, że nie użyłeś jeszcze metod protokołu, wyobraź sobie, że kiedy klasa A przyjmuje protokół, jest to jak podpisanie umowy z klasą B, a umowa ta mówi: „Każda klasa adoptująca MUSI korzystać z moich funkcji!”
Szybka uwaga: jeśli pochodzisz z Objective-C
tła, prawdopodobnie myślisz, że możesz również zamknąć ten błąd, czyniąc tę metodę opcjonalną, ale ku mojemu zaskoczeniu i prawdopodobnie twojemu, Swift
język nie obsługuje opcjonalnego protocols
, jeśli chcesz to zrobić, możesz stworzyć rozszerzenie dla twojego protocol
lub użyj słowa kluczowego @objc w swoimprotocol
implementacji.
Osobiście, jeśli muszę stworzyć protokół z różnymi opcjonalnymi metodami, wolałbym podzielić go na różne protocols
, w ten sposób podążę za koncepcją powierzenia jednej odpowiedzialności moim obiektom, ale może się to różnić w zależności od konkretnej implementacji.
tutaj jest dobry artykuł o metodach opcjonalnych.
krok 5: Poszukaj znaku pragma krok 5 wewnątrz metody przygotowania do segue i dodaj to
//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
}
Właśnie tworzymy instancję ClassBVC
i przypisujemy jej delegata do siebie, ale czym jest tutaj ja? Cóż, jaźń jestClassAVC
co zostało delegowane!
krok 6: Na koniec poszukaj pragmy kroku 6 ClassAVC
i skorzystajmy z funkcji protocol
, zacznij pisać func changeBackgroundColor, a zobaczysz, że jest to automatyczne uzupełnianie dla Ciebie. Możesz dodać dowolną implementację, w tym przykładzie po prostu zmienimy kolor tła, dodamy to.
//MARK: step 6 finally use the method of the contract
func changeBackgroundColor(_ color: UIColor?) {
view.backgroundColor = color
}
Teraz uruchom aplikację!
Delegates
są wszędzie i prawdopodobnie korzystasz z nich bez uprzedzenia, jeśli tworzysz tableview
w przeszłości używane delegacje, wiele klas UIKIT
prac wokół nich i wiele innych frameworks
, rozwiązują one te główne problemy.
Gratulacje, właśnie wdrożyłeś niestandardowego delegata, wiem, że prawdopodobnie myślisz, tyle kłopotów tylko z tego powodu? cóż, delegacja jest bardzo ważnym wzorcem projektowym, który należy zrozumieć, jeśli chcesz zostać iOS
programistą, i zawsze pamiętaj, że mają one jeden do jednego związek między obiektami.
Oryginalny tutorial możesz zobaczyć tutaj
Odpowiedź jest faktycznie udzielona, ale chciałbym dać ci „ściągawki” do utworzenia delegata:
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;
Metoda:
-(void)delegateMEthod: (ArgType) arg{
}
Z mojego punktu widzenia utwórz osobną klasę dla tej metody delegowania i możesz jej używać tam, gdzie chcesz.
w moim niestandardowym 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;
po tym pliku in.m utwórz tablicę z obiektami,
- (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];
}
}
}];
}
Tutaj wszystkie są ustawione na niestandardową klasę delegowania. Po tym możesz użyć tej metody delegowania, gdzie chcesz. Na przykład ...
w kolejnym importowaniu kontrolera widoku po tym
utwórz akcję dla wywołania metody delegowanej w ten sposób
- (IBAction)dropDownBtn1Action:(id)sender {
DropDownViewController *vehicleModelDropView = [[DropDownViewController alloc]init];
vehicleModelDropView.dropDownType = DDCITY;
vehicleModelDropView.delegate = self;
[self presentViewController:vehicleModelDropView animated:YES completion:nil];
}
po tym wywołaniu deleguj metodę w ten sposób
- (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;
}
}
Deleguj: - Utwórz
@protocol addToCartDelegate <NSObject>
-(void)addToCartAction:(ItemsModel *)itemsModel isAdded:(BOOL)added;
@end
Wyślij i przypisz uczestnika, aby zobaczył, że wysyłasz dane
[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. Zaimplementuj metodę w klasie .m - (void) didRemoveCellWithTag: (NSInteger) tag {NSLog @ („Tag% d”, tag);
}
Zacznijmy od przykładu, jeśli kupimy produkt online, przejdzie on przez proces taki jak wysyłka / dostawa obsługiwana przez różne zespoły, więc jeśli wysyłka zostanie ukończona, zespół wysyłający powinien powiadomić zespół dostarczający i powinna to być komunikacja jeden do jednego, gdy transmituje te informacje byłoby narzutem na inne osoby / sprzedawca może chcieć przekazać te informacje tylko wymaganym osobom.
Jeśli więc myślimy o naszej aplikacji, wydarzenie może być zamówieniem online, a różne zespoły mogą przypominać wiele widoków.
Oto kod rozważający ShippingView jako zespół wysyłający i DeliveryView jako zespół dostarczający:
//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
//
}
}