Як створити делегатів у Objective-C?


Відповіді:


888

Делегат Objective-C - це об'єкт, який присвоєно delegateвластивості іншому об'єкту. Щоб створити його, ви визначаєте клас, який реалізує цікавлять вас способи делегування, і позначаєте цей клас як реалізацію протоколу делегата.

Наприклад, припустимо, у вас є UIWebView. Якщо ви хочете реалізувати webViewDidStartLoad:метод його делегата , ви можете створити такий клас:

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

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

Тоді ви можете створити екземпляр MyClass і призначити його як делегата веб-перегляду:

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

На UIWebViewстороні, він , ймовірно , має код , подібний цьому , щоб побачити , якщо делегат відповідає на webViewDidStartLoad:повідомлення , використовуючи respondsToSelector:і відправити його в разі потреби.

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

Саме властивість делегата зазвичай оголошується weak(в ARC) або assign(pre-ARC), щоб уникнути збереження циклів, оскільки делегат об'єкта часто має чітке посилання на цей об'єкт. (Наприклад, контролер перегляду часто є делегатом представлення, яке він містить.)

Створення делегатів для ваших класів

Щоб визначити власних делегатів, вам доведеться десь оголосити їх методи, як це обговорювалося в документах Apple Docs у протоколах . Зазвичай ви декларуєте офіційний протокол. Декларація, перефразована з UIWebView.h, виглядатиме так:

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

Це аналог інтерфейсу або абстрактного базового класу, оскільки створює особливий тип для вашого делегата, UIWebViewDelegateв цьому випадку. Делегат-виконавці повинні прийняти цей протокол:

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

А потім реалізуйте методи в протоколі. Для методів, оголошених у протоколі як @optional(як і більшість методів делегування), вам потрібно перевіритись -respondsToSelector:перед тим, як викликати певний метод на ньому.

Іменування

Методи делегування зазвичай називають, починаючи з імені класу делегування, і беруть об’єкт делегування як перший параметр. Вони також часто використовують заповіт, слід чи формуляр. Отже, webViewDidStartLoad:(перший параметр - це веб-перегляд), а не loadStarted(без параметрів).

Оптимізація швидкості

Замість того, щоб перевіряти, чи відповідає делегат селектору кожного разу, коли ми хочемо надіслати його, ви можете кешувати цю інформацію, коли встановлені делегати. Один з дуже чистих способів зробити це - використовувати бітфілд, наступний:

@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

Тоді в тілі ми можемо перевірити, що наш делегат обробляє повідомлення, отримуючи доступ до нашої delegateRespondsToструктури, а не надсилаючи -respondsToSelector:знову і знову.

Неформальні делегати

До появи протоколів, було прийнято використовувати категорію на NSObjectдекларувати методи делегат міг би здійснити. Наприклад, CALayerдосі це робить:

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

Це повідомляє компілятору, що будь-який об’єкт може реалізувати displayLayer:.

Потім ви використовуєте той самий -respondsToSelector:підхід, як описано вище, для виклику цього методу. Делегати реалізують цей метод і призначають delegateвластивість, і це все (немає декларації про відповідність протоколу). Цей метод є поширеним у бібліотеках Apple, але новий код повинен використовувати сучасний підхід до протоколу, оскільки цей підхід забруднює NSObject(що робить автозаповнення менш корисним) і ускладнює компілятору попередження про помилки друку та подібні помилки.


Я думаю, що вам потрібно привласнити unsigned intтип, BOOLоскільки значення повернення delegate respondsToSelectorмає тип BOOL.
Роланд

Чи можна використовувати делегата для поліморфізму, як у C ++?

@Dan Так, звичайно. Протоколи взагалі використовують для поліморфізму.
Джессі Русак

@JesseRusak Я думаю, що "JSSomethingDelegate" повинен бути "SomethingDelegate" для послідовності :)
Hans Knöchel

382

Затверджена відповідь чудова, але якщо ви шукаєте 1 хвилину відповіді, спробуйте це:

Файл MyClass.h повинен виглядати так (додайте делегатські рядки з коментарями!)

#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

Файл MyClass.m повинен виглядати приблизно так

#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

Щоб використовувати свого делегата в іншому класі (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

Реалізувати метод делегата

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

4
Чудово використовувати цю відповідь як швидку довідку. Але чому властивість делегата у вашому MyClass.h позначена як "IBOutlet"?
Арно ван дер Меер

4
@ArnovanderMeer Хороший улов! Я не пам'ятаю, чому. Мені це потрібно в моєму проекті, але не в цьому прикладі, я його зняв. thx
Тібідабо

Дякую. Як приємна і ретельна, як прийнята відповідь, я найкраще вчусь із компактного зразкового коду. Добре мати дві відповіді.
судо

@Tibidabo Цілком видатний. Я дуже хочу, щоб кожен міг пояснити подібні концепції програмування. Я бачив сотні пояснень про "делегатів" протягом багатьох років і ніколи не розумів цю теорію дотепер! Дякую велике ...
Чарльз Робертсон

5
Де myClassінстанціюється всередині MyVC.m?
Провулок Реттіг

18

Використовуючи метод формального протоколу для створення підтримки делегатів, я виявив, що ви можете забезпечити належну перевірку типів (хоча, час виконання, не час компіляції), додавши щось на зразок:

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

у вашому коді доступу до делегатів (setDelegate). Це допомагає мінімізувати помилки.


18

Будь ласка! перегляньте нижче простий покроковий посібник, щоб зрозуміти, як працюють Делегати в iOS.

Делегуйте в iOS

Я створив два ViewControllers (для надсилання даних від одного до іншого)

  1. FirstViewController реалізує делегат (який надає дані).
  2. SecondViewController оголошує делегата (який отримає дані).

17

Можливо, це більше узгоджується з тим, що вам не вистачає:

Якщо ви приїжджаєте з точки зору на C ++, делегати дещо звикають - але в основному "вони просто працюють".

Як це працює, ви встановлюєте якийсь об'єкт, який ви написали як делегат до NSWindow, але ваш об'єкт має лише реалізацію (методи) для одного або кількох з багатьох можливих методів делегування. Так щось відбувається і NSWindowхоче викликати ваш об’єкт - він просто використовує respondsToSelectorметод Objective-c, щоб визначити, чи хоче ваш об'єкт називати цей метод, а потім викликає його. Ось як працюють об’єктивні-c - методи розглядаються на вимогу.

Це абсолютно тривіально робити це з власними об'єктами, нічого особливого не відбувається, ви можете, наприклад, мати NSArray27 об'єктів, всілякі об'єкти, лише 18 з них мають метод, -(void)setToBue;інші 9 не мають. Тож закликайте setToBlueвсіх 18, які потребують цього, приблизно так:

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

Інша річ, що стосується делегатів - це те, що вони не зберігаються, тому ви завжди повинні встановити делегата nilу своєму MyClass deallocметоді.


15

Як хороша практика, рекомендована Apple, делегат (це протокол, за визначенням), добре відповідати NSObjectпротоколу.

@protocol MyDelegate <NSObject>
    ...
@end

& для створення необов'язкових методів у вашого делегата (тобто методів, які не обов'язково потрібно реалізовувати), ви можете використовувати @optionalпримітку так:

@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

Тож, використовуючи методи, які ви вказали як необов'язкові, вам потрібно (у своєму класі) перевірити, respondsToSelectorчи представлення (яке відповідає вашому делегату) реально реалізувало ваші необов'язкові методи (методи) чи ні.


11

Я думаю, що всі ці відповіді мають багато сенсу, коли ви зрозумієте делегатів. Особисто я приїхав із країни C / C ++, а до цього процедурними мовами, такими як Fortran і т.д.

Якби я пояснював делегатів програмісту на C ++ / Java, я б сказав

Що таке делегати? Це статичні вказівники на класи в іншому класі. Після призначення покажчика ви можете викликати функції / методи в цьому класі. Отже, деякі функції вашого класу "делеговані" (у світі C ++ - покажчик на вказівник об'єкта класу) іншому класу.

Що таке протоколи? Концептуально це служить аналогічною метою, як і для файлу заголовка класу, який ви призначаєте як клас делегата. Протокол є явним способом визначення того, які методи потрібно реалізувати в класі, вказівник якого встановлений як делегат у класі.

Як я можу зробити щось подібне в C ++? Якщо ви спробували це зробити в C ++, вам слід, визначивши покажчики на класи (об'єкти) у визначенні класу, а потім підключити їх до інших класів, які нададуть додаткові функції як делегати базового класу. Але цю проводку потрібно ввести в код, і вона буде незграбною та схильною до помилок. Завдання C просто передбачає, що програмісти не найкраще підтримують цю дисципліну і забезпечує обмеження компілятора для забезпечення чистої реалізації.


Те, про що ви говорите, - це семантика, поки я говорив про інтуїцію. Те, про що ви говорите, це віртуальна функція - але просто звикнути до нової термінології може бути складним завданням. Відповідь подає початківцям, які хочуть придумати паралель на C ++ / C
DrBug

Те, що ви говорите, мені не дуже зрозуміло. Чому ви не пишете нову відповідь і не бачите, якщо більше людей вважають її корисною, вони проголосують за неї?
DrBug

9

Швидка версія

Делегат - це просто клас, який виконує якусь роботу для іншого класу. Прочитайте наступний код для дещо нерозумного (але, сподіваюсь, освічуючого) прикладу дитячої площадки, який показує, як це робиться у 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!"
}

У реальній практиці делегати часто використовуються в наступних ситуаціях

  1. Коли класі потрібно донести певну інформацію до іншого класу
  2. Коли клас хоче дозволити іншому класу налаштувати його

Класам не потрібно заздалегідь нічого знати один про одного, за винятком того, що клас делегата відповідає необхідному протоколу.

Я настійно рекомендую прочитати наступні дві статті. Вони допомогли мені зрозуміти делегатів навіть краще, ніж це робила документація .


8

Гаразд, це насправді не відповідь на питання, але якщо ви шукаєте, як зробити власного делегата, можливо, щось набагато простіше могло б бути кращою відповіддю для вас.

Я навряд чи реалізую своїх делегатів, бо мені це рідко потрібно. Я можу мати ТОЛЬКО ОДИН делегат для об'єкта делегата. Тож, якщо ви хочете, щоб ваш делегат в один спосіб передавав / передавав дані, то вам набагато краще з повідомленнями.

NSNotification може передавати об'єкти більш ніж одному одержувачу, і це дуже просто у використанні. Це працює так:

Файл MyClass.m повинен виглядати приблизно так

#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

Щоб використовувати своє сповіщення в інших класах: Додайте клас як спостерігач:

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

Вкажіть селектор:

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

Не забудьте видалити свій клас як спостерігач, якщо

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

8

скажемо, у вас є клас, який ви розробили, і хочете оголосити властивість делегата, щоб мати змогу сповіщати про нього, коли трапляється якась подія:

@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

тож ви оголошуєте протокол у MyClassфайлі заголовка (або окремий файл заголовка), а також оголошуєте необхідні / необов'язкові обробники подій, які ваш / його делегат повинен / повинен реалізувати, а потім оголошують властивість MyClassтипу ( id< MyClassDelegate>), що означає будь-який об'єктивний клас c, що відповідає в протоколі MyClassDelegateви помітите, що властивість делегата оголошується слабким, це дуже важливо для запобігання циклу збереження (найчастіше делегат зберігає MyClassекземпляр, тому якщо ви оголосили делегата як збережений, вони обидва будуть зберігати один одного, і ні з них коли-небудь вийде).

ви також помітите, що методи протоколу передають MyClassекземпляр делегату як параметр, це найкраща практика, якщо делегат хоче викликати деякі методи, MyClassнаприклад, а також допомагає, коли делегат оголошує себе MyClassDelegateдекількома MyClassекземплярами, наприклад, коли у вас є кілька UITableView'sекземплярів у вашому ViewControllerі заявляє про себе як про UITableViewDelegateвсі.

і всередині свого MyClassви повідомляєте делегату про оголошені події наступним чином:

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

спершу ви перевіряєте, чи відповідає ваш делегат на метод протоколу, який ви збираєтесь викликати, якщо делегат не реалізує його, і додаток потім вийде з ладу (навіть якщо потрібен метод протоколу).


6

Ось простий метод створення делегатів

Створіть протокол у .h-файлі. Переконайтеся, що визначено перед протоколом за допомогою @class, а потім ім'я UIViewController< As the protocol I am going to use is UIViewController class>.

Крок: 1: Створіть новий протокол класу з назвою "YourViewController", який буде підкласом класу UIViewController, і призначте цей клас другому ViewController.

Крок: 2: Перейдіть у файл "YourViewController" і змініть його, як показано нижче:

#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

Методи, визначені в поведінці протоколу, можна керувати за допомогою @optional і @required як частина визначення протоколу.

Крок: 3: Впровадження Делегата

    #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

// перевірити, чи визначено метод перед тим, як викликати його

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

5

Щоб створити власного делегата, спочатку потрібно створити протокол і оголосити необхідні методи, не застосовуючи. А потім реалізуйте цей протокол у своєму класі заголовка, де ви хочете реалізувати делегат або делегатські методи.

Протокол повинен бути оголошений як нижче:

@protocol ServiceResponceDelegate <NSObject>

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

@end

Це клас обслуговування, де слід виконати певне завдання. Він показує, як визначити делегата і як встановити делегата. У класі реалізації після виконання завдання викликаються методи делегата.

@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

Це основний клас перегляду, з якого викликається клас обслуговування, встановивши делегата собі. А також протокол реалізований у класі заголовка.

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

- (void) go;

@end

@implementation viewController

//
//some methods
//

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

Це все, і, застосовуючи делегатні методи в цьому класі, контроль повернеться, як тільки операція / завдання буде виконана.


4

Відмова від відповідальності: це Swiftверсія створення файлу delegate.

Отже, що таке делегати? … У розробці програмного забезпечення існують загальні архітектури рішень для багаторазового використання, які допомагають вирішувати часто зустрічаються проблеми в заданому контексті, ці «шаблони», так би мовити, найбільш відомі як моделі дизайну. Делегати - це схема дизайну, яка дозволяє одному об’єкту надсилати повідомлення іншому об'єкту, коли відбувається певна подія. Уявіть, що об'єкт A викликає об'єкт B для виконання дії. Після завершення дії об’єкт A повинен знати, що B виконав завдання і вжив необхідних дій, це можна досягти за допомогою делегатів!

Для кращого пояснення я збираюся показати вам, як створити спеціальний делегат, який передає дані між класами, зі Swift у простому додатку, почніть із завантаження або клонування цього проекту для початківців та запустіть його!

Ви можете бачити додаток з двома класами ViewController Aта ViewController B. B має два погляди, що при натисканні змінює колір тла ViewController, нічого надто складного, правда? ну тепер давайте подумаємо на простий спосіб також змінити колір тла класу A при натисканні поглядів на клас B.

Проблема полягає в тому, що ці погляди є частиною класу B і не мають поняття про клас A, тому нам потрібно знайти спосіб спілкування між цими двома класами, і саме тут делегує делегація. Я розділив реалізацію на 6 етапів, щоб ви могли використовувати це як шпаргалку, коли вам це потрібно.

крок 1: Шукайте крок 1 позначки прагми у файлі ClassBVC та додайте це

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

Перший крок - це створення protocol, в цьому випадку ми створимо протокол класу В, всередині протоколу ви можете створити стільки функцій, які вам потрібно, виходячи з вимог вашої реалізації. У цьому випадку у нас є лише одна проста функція, яка приймає необов’язковий UIColorаргумент. Це хороша практика , щоб назвати свої протоколи , додавши слово delegateв кінці імені класу, в даному випадку ClassBVCDelegate.

крок 2: Знайдіть прагматичний крок 2 ClassVBCі додайте це

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

Тут ми просто створюємо властивість делегата класу, це властивість повинно приймати protocolтип, і воно повинно бути необов’язковим. Крім того, слід додати слабке ключове слово перед властивістю, щоб уникнути циклів та можливих витоків пам’яті, якщо ви не знаєте, що це означає, не хвилюйтесь зараз, просто не забудьте додати це ключове слово.

крок 3: Подивіться на крок Прагма марки 3 всередині handleTap methodв ClassBVCі додати

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

Одне, що вам слід знати, запустіть додаток і натисніть на будь-який вигляд, ви не побачите жодної нової поведінки, і це правильно, але те, що я хочу зазначити, - це те, що програма не виходить з ладу, коли викликається делегат, і це тому, що ми створюємо його як необов'язкове значення, і тому воно не вийде з ладу, навіть делеговане ще не існує. Тепер перейдемо до ClassAVCфайлу і зробимо його, делегованим.

крок 4: Шукайте крок 4 позначки прагми всередині методу handleTap ClassAVCта додайте це поруч із типом класу, як це.

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

Тепер ClassAVC прийняв ClassBVCDelegateпротокол, ви бачите, що ваш компілятор видає вам помилку, яка говорить "Тип" ClassAVC не відповідає протоколу "ClassBVCDelegate", і це означає лише, що ви ще не використовували методи протоколу, уявіть, що коли клас А приймає протокол, це як підписання контракту з класом В, і в цьому контракті сказано: "Будь-який клас, який приймає мене, ОБОВ'ЯЗКОВО використовувати мої функції!"

Швидка примітка: Якщо ви прийшли з Objective-Cфонового режиму, ви, напевно, думаєте, що можете також закрити цю помилку, зробивши цей метод необов’язковим, але, на мій подив, і, ймовірно, ваш, Swiftмова не підтримує необов'язковий protocols, якщо ви хочете це зробити, ви можете створити розширення для вашого protocolабо використовувати ключове слово @objc у своїй protocolреалізації.

Особисто, якщо мені доведеться створити протокол з різними необов'язковими методами, я вважаю за краще би розбити його на різні protocols, тому я буду дотримуватися концепції покладання однієї єдиної відповідальності на мої об'єкти, але вона може змінюватися залежно від конкретної реалізації.

ось хороша стаття про необов'язкові методи.

крок 5: Шукайте крок 5 позначки прагми всередині методу підготовки до Segue і додайте це

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

Тут ми просто створюємо екземпляр ClassBVCі призначаємо його делегата самому, але що тут я? ну, я - це те, ClassAVCщо було делеговане!

крок 6: Нарешті, знайдіть крок 6 прагми ClassAVCта скористаємось функціямиprotocol прагми та , почніть вводити функцію changeBackgroundColor, і ви побачите, що вона автоматично доповнює її. Ви можете додати будь-яку реалізацію всередині нього, у цьому прикладі ми просто змінимо колір тла, додамо це.

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

Тепер запустіть додаток!

Delegates є скрізь, і ви, ймовірно, використовуєте їх, навіть не помічаючи, якщо ви створюєте tableview делегацію, яку ви раніше використовували, багато класів UIKITробіт навколо них і багато інших frameworks, вони вирішують ці основні проблеми.

  • Уникайте щільного зчеплення предметів.
  • Модифікуйте поведінку та зовнішній вигляд без необхідності підкласу об’єктів.
  • Дозволити обробляти завдання будь-яким довільним об’єктом.

Вітаємо, ви просто впроваджуєте спеціальний делегат, я знаю, що ви, напевно, думаєте, стільки проблем саме для цього? добре, делегування - це дуже важлива модель дизайну, щоб зрозуміти, чи хочете ви статиiOS розробником, і завжди майте на увазі, що вони мають один до одного відносини між об'єктами.

Оригінальний підручник ви можете побачити тут


4

Відповідь насправді відповідає, але я хотів би дати вам "шпаргалку" для створення делегата:

DELEGATE SCRIPT

CLASS A - Where delegate is calling function

@protocol <#Protocol Name#> <NSObject>

-(void)delegateMethod;

@end

@interface <#Some ViewController#> : <#UIViewController#> 

@property (nonatomic, assign) id <<#Protocol Name#>> delegate;

@end


@implementation <#Some ViewController#> 

-(void)someMethod {
    [self.delegate methodName];
}

@end




CLASS B - Where delegate is called 

@interface <#Other ViewController#> (<#Delegate Name#>) {}
@end

@implementation <#Other ViewController#> 

-(void)otherMethod {
    CLASSA *classA = [[CLASSA alloc] init];

    [classA setDelegate:self];
}

-delegateMethod() {

}

@end

2

ViewController.h

@protocol NameDelegate <NSObject>

-(void)delegateMEthod: (ArgType) arg;

@end

@property id <NameDelegate> delegate;

ViewController.m

[self.delegate delegateMEthod: argument];

MainViewController.m

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

Спосіб:

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

2

На мій погляд, створіть окремий клас для цього методу делегації, і ви можете використовувати там, де вам потрібно.

в моєму власному 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;

після цього файл in.m створює масив з об'єктами,

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

Тут все встановлено для користувацького делегата class.after, що ви можете використовувати цей метод делегата там, де ви хочете. Наприклад, ...

в іншому імпорті диспетчера перегляду після цього

створити дію для виклику такого способу делегата

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

після цього виклику делегат метод, як це

- (void)dropDownDidSelectItemWithString:(NSString *)itemString DropDownType:(DropDownType)dropDownType {
switch (dropDownType) {
    case DDCITY:{
        if(itemString.length > 0){
            //Here i am printing the selected row
            [self.dropDownBtn1 setTitle:itemString forState:UIControlStateNormal];
        }
    }
        break;
    case DDSTATE: {
        //Here i am printing the selected row
        [self.dropDownBtn2 setTitle:itemString forState:UIControlStateNormal];
    }

    default:
        break;
}
}

0

Делегат: - Створіть

@protocol addToCartDelegate <NSObject>

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

@end

Надішліть і будь-ласка призначте делегата, щоб побачити надсилання даних

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

0
//1.
//Custom delegate 
@protocol TB_RemovedUserCellTag <NSObject>

-(void)didRemoveCellWithTag:(NSInteger)tag;

@end

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

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

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

@end

// 5. Реалізуйте метод у класі .m - (void) didRemoveCellWithTag: (NSInteger) тег {NSLog @ ("Тег% d", тег);

}


0

Почнемо з прикладу, якщо ми купуємо товар в Інтернеті, він проходить процес, як доставка / доставка, якими займаються різні команди. Отже, якщо доставка завершена, команда доставки повинна повідомити про це групу доставки, і це має бути один на один зв’язок, як трансляція цієї інформації буде накладними для інших людей / постачальник може захотіти передати цю інформацію лише потрібним людям.

Тож якщо ми думаємо з точки зору нашої програми, подією може бути замовлення в Інтернеті, а різні команди можуть бути схожими на кілька переглядів.

Ось код, який розгляне ДоставкаView як команда доставки та ДоставкаView як команда доставки:

//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
        //
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.