Jak utworzyć delegatów w Objective-C?

Odpowiedzi:

888

Delegat Celu-C to obiekt, który został przypisany do delegatewł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 UIWebViewboku 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.)

Dokonywanie delegatów dla swoich klas

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ż UIWebViewDelegatew 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.

Nazewnictwo

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.

Optymalizacje prędkości

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.

Nieformalni delegaci

Przed istnieniem protokołów powszechne było używanie kategorii w NSObjectcelu zadeklarowania metod, które delegat mógłby wdrożyć. Na przykład CALayernadal 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ą delegatewł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.

Jesse Rusak
źródło
Myślę, że musisz rzutować na unsigned inttyp, BOOLponieważ zwracana wartość delegate respondsToSelectorjest typu BOOL.
Roland,
Czy delegata może być używana do polimorfizmu jak w C ++?
@ Dan Tak, jasne. Protokoły są ogólnie stosowane w przypadku polimorfizmu.
Jesse Rusak
@JesseRusak Myślę, że „JSSomethingDelegate” powinno być „SomethingDelegate” dla spójności :)
Hans Knöchel
382

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!");
}
Tibidabo
źródło
4
Świetnie jest użyć tej odpowiedzi jako szybkiego odniesienia. Ale dlaczego właściwość delegata w pliku MyClass.h jest oznaczona jako „IBOutlet”?
Arno van der Meer
4
@ArnovanderMeer Dobry połów! Nie pamiętam dlaczego. Potrzebuję go w moim projekcie, ale nie w tym przykładzie usunąłem go. dzięki
Tibidabo
Dzięki. Mimo że odpowiedź jest miła i dokładna, najlepiej uczę się na podstawie niewielkiego przykładowego kodu. Dobrze jest mieć dwie odpowiedzi.
sudo
@Tibidabo Całkowicie wyjątkowy. Naprawdę chciałbym, żeby każdy mógł wyjaśnić takie koncepcje programowania. Przez lata widziałem setki wyjaśnień na temat „delegatów” i nigdy tak naprawdę nie zrozumiałem tej teorii! Dziękuję bardzo ...
Charles Robertson,
5
Gdzie jest myClasstworzona instancja w MyVC.m?
Lane Rettig,
18

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.

umop
źródło
18

Proszę! zapoznaj się z prostym samouczkiem krok po kroku, aby zrozumieć, jak działają Delegaci w iOS.

Deleguj w iOS

Utworzyłem dwóch ViewControllerów (do przesyłania danych z jednego do drugiego)

  1. FirstViewController implementuje delegata (który zapewnia dane).
  2. SecondViewController deklaruje delegata (który otrzyma dane).
swiftBoy
źródło
17

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 NSWindowchce 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ć NSArray27 obiektów, różnego rodzaju obiekty, tylko 18 z nich ma metodę, -(void)setToBue;a pozostałe 9 nie. Aby wezwać setToBluewszystkich 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 nilw MyClass deallocmetodzie.

Tom Andersen
źródło
15

Zgodnie z dobrą praktyką zalecaną przez Apple, delegatowi (który z definicji jest protokołem) jest zgodne z NSObjectprotokoł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ć @optionaladnotacji 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ć, respondsToSelectorczy widok (zgodny z twoim delegatem) faktycznie zaimplementował twoje opcjonalne metody, czy nie.

Drelich
źródło
11

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.

DrBug
źródło
Mówisz o semantyce, a ja mówiłem o intuicji. Mówisz o funkcji wirtualnej - ale przyzwyczajenie się do nowej terminologii może być trudne. Odpowiedź służy początkującym, którzy chcą pomyśleć o równoległości w C ++ / C
DrBug
To, co mówisz, nie jest dla mnie jasne. Dlaczego nie piszesz nowej odpowiedzi i pozwalasz zobaczyć, czy więcej osób uzna to za przydatne, zagłosują?
DrBug,
9

Wersja szybka

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

  1. Gdy klasa musi przekazać pewne informacje innej klasie
  2. Gdy klasa chce zezwolić innej klasie na jej dostosowanie

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 .

Suragch
źródło
8

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];
}
Tibidabo
źródło
8

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 MyClasspliku 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ść MyClasstype ( 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ą MyClassinstancję do delegata jako parametr, jest to najlepsza praktyka w przypadku, gdy delegat chce wywołać niektóre metody w MyClassinstancji, a także pomaga, gdy delegat deklaruje się jako MyClassDelegatedo wielu MyClassinstancji, na przykład gdy masz wiele UITableView'swystąpień w twoim ViewControlleri deklaruje się jako UITableViewDelegatedla nich wszystkich.

aw swoim wnętrzu MyClasspowiadamiasz 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).

m.eldehairy
źródło
6

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]; 
     }
  }
soumya
źródło
5

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.

Mahesh
źródło
4

Zastrzeżenie: jest to Swiftwersja, 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 Ai 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ą UIColorjako argument. Dobrą praktyką jest nazywanie protokołów przez dodanie słowa delegatena końcu nazwy klasy, w tym przypadku ClassBVCDelegate.

krok 2: Poszukaj pragmy zaznacz krok 2 w ClassVBCi 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 methodw ClassBVCDodaj

//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 ClassAVCakt i dokonajmy tego, delegowani.

krok 4: Poszukaj znaku pragma krok 4 wewnątrz metody handleTap w ClassAVCi dodaj to obok swojego typu klasy w ten sposób.

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

Teraz ClassAVC przyjął ClassBVCDelegateprotokół, 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-Ctł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, Swiftjęzyk nie obsługuje opcjonalnego protocols, jeśli chcesz to zrobić, możesz stworzyć rozszerzenie dla twojego protocollub 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ę ClassBVCi 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 ClassAVCi 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ę!

Delegatessą wszędzie i prawdopodobnie korzystasz z nich bez uprzedzenia, jeśli tworzysz tablevieww przeszłości używane delegacje, wiele klas UIKITprac wokół nich i wiele innych frameworks, rozwiązują one te główne problemy.

  • Unikaj ciasnego łączenia przedmiotów.
  • Zmodyfikuj zachowanie i wygląd bez potrzeby podklasowania obiektów.
  • Zezwalaj na wykonywanie zadań na dowolnym dowolnym obiekcie.

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ć iOSprogramistą, i zawsze pamiętaj, że mają one jeden do jednego związek między obiektami.

Oryginalny tutorial możesz zobaczyć tutaj

James Rochabrun
źródło
4

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
Miras Maratuly
źródło
2

ViewController.h

@protocol NameDelegate <NSObject>

-(void)delegateMEthod: (ArgType) arg;

@end

@property id <NameDelegate> delegate;

ViewController.m

[self.delegate delegateMEthod: argument];

MainViewController.m

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

Metoda:

-(void)delegateMEthod: (ArgType) arg{
}
Lal Krishna
źródło
2

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;
}
}
Użytkownik558
źródło
0

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];
Vaibhav Gaikwad
źródło
0
//1.
//Custom delegate 
@protocol TB_RemovedUserCellTag <NSObject>

-(void)didRemoveCellWithTag:(NSInteger)tag;

@end

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

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

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

@end

// 5. Zaimplementuj metodę w klasie .m - (void) didRemoveCellWithTag: (NSInteger) tag {NSLog @ („Tag% d”, tag);

}

Rohit Kashyap
źródło
0

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
        //
    }
}
Ellen
źródło