Como crio delegados no Objective-C?

Respostas:

889

Um delegado do Objective-C é um objeto que foi atribuído à delegatepropriedade outro objeto. Para criar um, defina uma classe que implemente os métodos de delegação de seu interesse e marque essa classe como implementando o protocolo de delegação.

Por exemplo, suponha que você tenha um UIWebView. Se você deseja implementar o webViewDidStartLoad:método de seu representante , você pode criar uma classe como esta:

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

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

Em seguida, você pode criar uma instância do MyClass e atribuí-la como delegada da visualização da web:

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

Por UIWebViewoutro lado, provavelmente possui um código semelhante a este para verificar se o delegado responde à webViewDidStartLoad:mensagem usando respondsToSelector:e enviá-lo, se apropriado.

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

A propriedade delegada em si normalmente é declarada weak(no ARC) ouassign (pré-ARC) para evitar loops de retenção, uma vez que o delegado de um objeto geralmente mantém uma forte referência a esse objeto. (Por exemplo, um controlador de exibição geralmente é o delegado de uma exibição que contém.)

Como fazer delegados para suas aulas

Para definir seus próprios delegados, você precisará declarar seus métodos em algum lugar, conforme discutido no Apple Docs sobre protocolos . Você geralmente declara um protocolo formal. A declaração, parafraseada em UIWebView.h, ficaria assim:

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

Isso é análogo a uma interface ou classe base abstrata, pois cria um tipo especial para o seu delegado, UIWebViewDelegateneste caso. Os implementadores delegados teriam que adotar este protocolo:

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

E, em seguida, implemente os métodos no protocolo. Para métodos declarados no protocolo como @optional(como a maioria dos métodos delegados), é necessário verificar -respondsToSelector:antes de chamar um método específico.

Nomeação

Os métodos de delegação geralmente são nomeados começando com o nome da classe de delegação e tomam o objeto de delegação como o primeiro parâmetro. Eles também costumam usar uma forma de vontade, deveria ou didática. Portanto, webViewDidStartLoad:(o primeiro parâmetro é a visualização da web) e não loadStarted(sem parâmetros), por exemplo.

Otimizações de velocidade

Em vez de verificar se um delegado responde a um seletor toda vez que desejamos enviá-lo por mensagem, você pode armazenar em cache essas informações quando os delegados estiverem definidos. Uma maneira muito limpa de fazer isso é usar um campo de bits, da seguinte maneira:

@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

Então, no corpo, podemos verificar se nosso delegado lida com mensagens acessando nossa delegateRespondsToestrutura, em vez de enviar -respondsToSelector:repetidas vezes.

Delegados informais

Antes de protocolos existia, era comum usar uma categoria de NSObjectdeclarar os métodos de um delegado poderia implementar. Por exemplo, CALayerainda faz isso:

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

Isso informa ao compilador que qualquer objeto pode implementar displayLayer:.

Você usaria a mesma -respondsToSelector:abordagem descrita acima para chamar esse método. Os delegados implementam esse método e atribuem a delegatepropriedade, e é isso (não há como declarar que você está em conformidade com um protocolo). Esse método é comum nas bibliotecas da Apple, mas o novo código deve usar a abordagem de protocolo mais moderna acima, uma vez que essa abordagem polui NSObject(o que torna o preenchimento automático menos útil) e dificulta o compilador avisá-lo sobre erros de digitação e erros semelhantes.

Jesse Rusak
fonte
Acho que você precisa para lançar o unsigned inttipo de BOOLcomo o valor de retorno delegate respondsToSelectoré do tipo BOOL.
Roland
O delegado pode ser usado para polimorfismo, como em C ++?
@ Dan Sim, com certeza. Protocolos em geral são usados ​​para polimorfismo.
Jesse Rusak
@JesseRusak eu penso "JSSomethingDelegate" deve ser "SomethingDelegate" para a consistência :)
Hans Knöchel
382

A resposta aprovada é ótima, mas se você estiver procurando por uma resposta de 1 minuto, tente o seguinte:

O arquivo MyClass.h deve ficar assim (adicione linhas de delegado com comentários!)

#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

O arquivo MyClass.m deve ficar assim

#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

Para usar seu representante em outra classe (neste caso, UIViewController chamado 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

Implementar método delegado

- (void) myClassDelegateMethod: (MyClass *) sender {
    NSLog(@"Delegates are great!");
}
Tibidabo
fonte
4
É ótimo usar essa resposta como uma referência rápida. Mas por que a propriedade delegate no seu MyClass.h está marcada como 'IBOutlet'?
Arno van der Meer
4
@ArnovanderMeer Good catch! Não me lembro por quê. Eu preciso dele no meu projeto, mas não neste exemplo, eu o removi. thx
Tibidabo
Obrigado. Por mais agradável e completa que seja a resposta aceita, aprendo melhor com alguns exemplos de códigos compactos. É bom ter duas respostas.
Sudo
@Tibidabo Totalmente excelente. Eu realmente gostaria que todos pudessem explicar conceitos de programação como este. Eu tenho visto centenas de explicações, sobre 'delegados', ao longo dos anos e nunca realmente compreendi essa teoria até agora! Muito obrigado ...
Charles Robertson
5
Onde é myClassinstanciado dentro do MyVC.m?
Lane Rettig
18

Ao usar o método formal de protocolo para criar suporte ao delegado, descobri que você pode garantir a verificação adequada do tipo (embora, tempo de execução, sem tempo de compilação) adicionando algo como:

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

no código do acessador delegado (setDelegate). Isso ajuda a minimizar erros.

hum
fonte
18

Por favor! confira abaixo o tutorial passo a passo simples para entender como os delegados funcionam no iOS.

Delegar no iOS

Eu criei dois ViewControllers (para enviar dados de um para outro)

  1. O FirstViewController implementa o delegado (que fornece dados).
  2. SecondViewController declara o delegado (que receberá dados).
swiftBoy
fonte
17

Talvez isso seja mais do que você está perdendo:

Se você vem de um ponto de vista semelhante ao C ++, os delegados demoram um pouco para se acostumar - mas basicamente 'eles simplesmente funcionam'.

O modo como funciona é que você defina algum objeto que você escreveu como delegado para o NSWindow, mas seu objeto possui apenas implementações (métodos) para um ou alguns dos muitos métodos possíveis de delegação. Então, algo acontece e NSWindowdeseja chamar seu objeto - ele apenas usa o respondsToSelectormétodo de Objective-c para determinar se seu objeto deseja que esse método seja chamado e depois o chama. É assim que o objetivo-c funciona - os métodos são pesquisados ​​sob demanda.

É totalmente trivial fazer isso com seus próprios objetos; não há nada de especial; você pode, por exemplo, ter um NSArrayde 27 objetos, todos os tipos de objetos, apenas 18 deles com o método -(void)setToBue;Os outros 9 não. Então, para chamar setToBluetodos os 18 que precisam, algo assim:

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

A outra coisa sobre os delegados é que eles não são retidos; portanto, você sempre deve definir o delegado nilno seu MyClass deallocmétodo.

Tom Andersen
fonte
15

Como boa prática recomendada pela Apple, é bom que o delegado (que é um protocolo, por definição), esteja em conformidade com o NSObjectprotocolo.

@protocol MyDelegate <NSObject>
    ...
@end

& para criar métodos opcionais dentro do seu delegado (ou seja, métodos que não precisam necessariamente ser implementados), você pode usar a @optionalanotação da seguinte maneira:

@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

Portanto, ao usar métodos que você especificou como opcionais, é necessário (em sua classe) verificar respondsToSelectorse a exibição (que está em conformidade com seu delegado) realmente implementou seus métodos opcionais ou não.

Jean
fonte
11

Acho que todas essas respostas fazem muito sentido depois que você entende os delegados. Pessoalmente, eu vim da terra do C / C ++ e, antes disso, linguagens procedurais como Fortran, etc.

Se eu explicasse os delegados a um programador C ++ / Java, eu diria

O que são delegados? Esses são indicadores estáticos para classes dentro de outra classe. Depois de atribuir um ponteiro, você pode chamar funções / métodos nessa classe. Portanto, algumas funções da sua classe são "delegadas" (no mundo C ++ - ponteiro para por um ponteiro de objeto de classe) para outra classe.

O que são protocolos? Conceitualmente, serve para uma finalidade semelhante ao arquivo de cabeçalho da classe que você está atribuindo como uma classe delegada. Um protocolo é uma maneira explícita de definir quais métodos precisam ser implementados na classe cujo ponteiro foi definido como delegado dentro de uma classe.

Como posso fazer algo semelhante em C ++? Se você tentou fazer isso em C ++, definiu ponteiros para classes (objetos) na definição de classe e os conectou a outras classes que fornecerão funções adicionais como delegadas à sua classe base. Mas essa fiação precisa ser mantida dentro do código e será desajeitada e propensa a erros. O objetivo C apenas supõe que os programadores não são os melhores em manter essa deciplina e fornece restrições ao compilador para impor uma implementação limpa.

DrBug
fonte
O que você está falando é semântica enquanto eu estava falando sobre a intuição. O que você está falando é sobre função virtual - mas apenas para se acostumar com a nova terminologia pode ser um desafio. A resposta serve beginers que querem pensar em um paralelo em C ++ / C
DrBug
O que você está dizendo não é realmente claro para mim. Por que você não escreve uma nova resposta e deixa ver se mais pessoas a acham útil, elas votarão nela?
DrBug
9

Versão Swift

Um delegado é apenas uma classe que trabalha para outra classe. Leia o código a seguir para obter um exemplo parvo (mas esperançosamente esclarecedor) do Playground, que mostra como isso é feito no 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!"
}

Na prática, os delegados são frequentemente usados ​​nas seguintes situações

  1. Quando uma classe precisa comunicar algumas informações para outra classe
  2. Quando uma classe deseja permitir que outra classe a personalize

As classes não precisam saber nada uma sobre a outra, exceto que a classe delegada está em conformidade com o protocolo necessário.

Eu recomendo a leitura dos dois artigos a seguir. Eles me ajudaram a entender os delegados ainda melhor do que a documentação .

Suragch
fonte
8

Ok, isso não é realmente uma resposta para a pergunta, mas se você está procurando saber como fazer seu próprio delegado, talvez algo muito mais simples possa ser uma resposta melhor para você.

Mal implemento meus delegados porque raramente preciso. Eu posso ter APENAS UM delegado para um objeto delegado. Portanto, se você deseja que seu delegado receba uma comunicação unidirecional / transmissão de dados, é melhor que ele receba notificações.

O NSNotification pode passar objetos para mais de um destinatário e é muito fácil de usar. Funciona assim:

O arquivo MyClass.m deve ficar assim

#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

Para usar sua notificação em outras classes: Adicione a classe como um observador:

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

Implemente o seletor:

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

Não se esqueça de remover sua classe como observador se

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

digamos que você tenha uma classe que você desenvolveu e deseja declarar uma propriedade delegada para poder notificá-la quando algum evento acontecer:

@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

para que você declare um protocolo no MyClassarquivo de cabeçalho (ou um arquivo de cabeçalho separado) e declare os manipuladores de eventos opcionais / obrigatórios que seu delegado deve / deve implementar e, em seguida, declare uma propriedade no MyClasstipo ( id< MyClassDelegate>) que significa qualquer classe c objetiva que esteja em conformidade com o protocolo MyClassDelegate, você notará que a propriedade delegate é declarada fraca, isso é muito importante para evitar o ciclo de retenção (na maioria das vezes o delegado retém a MyClassinstância, portanto, se você declarou o delegado como retido, os dois reterão um ao outro e nenhum deles será lançado).

você notará também que os métodos de protocolo transmitem a MyClassinstância ao delegado como parâmetro, essa é uma prática recomendada caso o delegado queira chamar alguns métodos na MyClassinstância e também ajuda quando o delegado se declara MyClassDelegateem várias MyClassinstâncias, como quando você tem várias UITableView'sinstâncias no seu ViewControllere se declara como um UITableViewDelegatepara todos eles.

e dentro do seu, MyClassvocê notifica o delegado com eventos declarados da seguinte maneira:

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

verifique primeiro se o delegado responde ao método de protocolo que você está prestes a chamar, caso o delegado não o implemente e o aplicativo trava (mesmo se o método de protocolo for necessário).

m.eldehairy
fonte
6

Aqui está um método simples para criar delegados

Crie o protocolo no arquivo .h. Certifique-se de que esteja definido antes do protocolo usando @class seguido pelo nome do UIViewController< As the protocol I am going to use is UIViewController class>.

Etapa: 1: Crie um novo protocolo de classe chamado "YourViewController", que será a subclasse da classe UIViewController e atribua essa classe ao segundo ViewController.

Etapa: 2: Vá para o arquivo "YourViewController" e modifique-o conforme abaixo:

#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

Os métodos definidos no comportamento do protocolo podem ser controlados com @optional e @required como parte da definição do protocolo.

Etapa: 3: Implementação do Delegado

    #import "delegate.h"

   @interface YourDelegateUser ()
     <YourViewControllerDelegate>
   @end

   @implementation YourDelegateUser

   - (void) variousFoo {
      YourViewController *controller = [[YourViewController alloc] init];
      controller.delegate = self;
   }

   -(void)defineDelegateMethodName: (YourViewController *) controller {
      // handle the delegate being called here
   }

   -(BOOL)delegateMethodReturningBool: (YourViewController *) controller {
      // handle the delegate being called here
      return YES;
   }

   @end

// teste se o método foi definido antes de chamá-lo

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

Para criar seu próprio delegado, primeiro você precisa criar um protocolo e declarar os métodos necessários, sem implementar. E, em seguida, implemente esse protocolo na classe de cabeçalho em que deseja implementar os métodos delegar ou delegar.

Um protocolo deve ser declarado como abaixo:

@protocol ServiceResponceDelegate <NSObject>

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

@end

Esta é a classe de serviço em que alguma tarefa deve ser realizada. Ele mostra como definir delegado e como definir o delegado. Na classe de implementação, após a conclusão da tarefa, os métodos do delegado são chamados.

@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

Essa é a classe de exibição principal de onde a classe de serviço é chamada, configurando o delegado para si mesmo. E também o protocolo é implementado na classe de cabeçalho.

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

- (void) go;

@end

@implementation viewController

//
//some methods
//

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

É isso e, ao implementar métodos delegados nesta classe, o controle retornará assim que a operação / tarefa for concluída.

Mahesh
fonte
4

Isenção de responsabilidade: esta é a Swiftversão de como criar um delegate.

Então, o que são delegados? … No desenvolvimento de software, existem arquiteturas de soluções reutilizáveis ​​gerais que ajudam a resolver problemas comuns em um determinado contexto, esses “modelos”, por assim dizer, são mais conhecidos como padrões de design. Delegados são um padrão de design que permite que um objeto envie mensagens para outro objeto quando um evento específico acontece. Imagine um objeto A chama um objeto B para executar uma ação. Depois que a ação estiver concluída, o objeto A deve saber que B concluiu a tarefa e tomar as medidas necessárias, isso pode ser alcançado com a ajuda dos delegados!

Para uma explicação melhor, mostrarei como criar um delegado personalizado que transmita dados entre classes, com o Swift em um aplicativo simples, comece baixando ou clonando este projeto inicial e executando-o!

Você pode ver um aplicativo com duas classes ViewController Ae ViewController B. B tem duas visualizações que na torneira mudam a cor de fundo do ViewController, nada muito complicado, certo? bem, agora vamos pensar de uma maneira fácil de alterar também a cor de fundo da classe A quando as visualizações na classe B são tocadas.

O problema é que essas visualizações fazem parte da classe B e não têm idéia da classe A, portanto, precisamos encontrar uma maneira de se comunicar entre essas duas classes, e é aí que brilha a delegação. Dividi a implementação em 6 etapas para que você possa usar isso como uma folha de dicas quando precisar.

etapa 1: procure a marca de pragma etapa 1 no arquivo ClassBVC e adicione este

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

O primeiro passo é criar um protocol, neste caso, criaremos o protocolo na classe B, dentro do protocolo, você poderá criar quantas funções desejar com base nos requisitos de sua implementação. Nesse caso, temos apenas uma função simples que aceita um opcional UIColorcomo argumento. É uma boa prática nomear seus protocolos adicionando a palavra delegateno final do nome da classe, neste caso ClassBVCDelegate,.

etapa 2: procure a marca de pragma etapa 2 ClassVBCe adicione este

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

Aqui, apenas criamos uma propriedade delegada para a classe, essa propriedade deve adotar o protocoltipo e deve ser opcional. Além disso, você deve adicionar a palavra-chave fraca antes da propriedade para evitar reter ciclos e possíveis vazamentos de memória. Se você não sabe o que isso significa, não se preocupe, lembre-se de adicionar essa palavra-chave.

etapa 3: procure a marca de pragma etapa 3 dentro da alçaToque methodem ClassBVCe adicione este

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

Uma coisa que você deve saber, executar o aplicativo e tocar em qualquer visualização, não verá nenhum comportamento novo e isso é correto, mas o que quero salientar é que o aplicativo não está travando quando o delegado é chamado e é porque nós o criamos como um valor opcional e é por isso que ele não falha mesmo que o delegado ainda não exista. Vamos agora ClassAVCarquivar e torná-lo, o delegado.

etapa 4: procure a marca de pragma etapa 4 dentro do método handleTap ClassAVCe adicione-a ao lado do seu tipo de classe como este.

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

Agora o ClassAVC adotou o ClassBVCDelegateprotocolo, você pode ver que seu compilador está fornecendo um erro que diz “Tipo 'ClassAVC não está em conformidade com o protocolo' ClassBVCDelegate 'e isso significa apenas que você ainda não usou os métodos do protocolo, imagine que quando a classe A adota o protocolo é como assinar um contrato com a classe B e este contrato diz "Qualquer classe que me adote DEVE usar minhas funções!"

Nota rápida: Se você é de Objective-Corigem, provavelmente pensa que também pode calar esse erro, tornando esse método opcional, mas, para minha surpresa e provavelmente a sua, o Swiftidioma não suporta opcional protocols, se você quiser, pode criar uma extensão para você protocolou use a palavra-chave @objc em sua protocolimplementação.

Pessoalmente, se eu tiver que criar um protocolo com diferentes métodos opcionais, prefiro dividi-lo em diferentes protocols, dessa maneira seguirei o conceito de atribuir uma única responsabilidade aos meus objetos, mas isso pode variar com base na implementação específica.

Aqui está um bom artigo sobre métodos opcionais.

etapa 5: procure a marca de pragma etapa 5 dentro do método prepare for segue e adicione este

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

Aqui estamos apenas criando uma instância ClassBVCe atribuindo seu delegado a si mesmo, mas o que é eu aqui? bem, o eu é o ClassAVCque foi delegado!

passo 6: Finalmente, procure o pragma do passo 6 ClassAVCe vamos usar as funções do protocol, comece a digitar func changeBackgroundColor e você verá que ele é preenchido automaticamente para você. Você pode adicionar qualquer implementação dentro dela, neste exemplo, apenas mudaremos a cor do plano de fundo, adicione isso.

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

Agora execute o aplicativo!

Delegatesestão em toda parte e você provavelmente as usa sem aviso prévio; se você criou uma tableviewdelegação no passado, usou muitas classes de UIKITtrabalhos ao seu redor e muitas outras frameworkstambém, elas resolvem esses problemas principais.

  • Evite acoplamentos apertados de objetos.
  • Modifique o comportamento e a aparência sem a necessidade de subclassificar objetos.
  • Permitir que tarefas sejam manipuladas para qualquer objeto arbitrário.

Parabéns, você acabou de implementar um delegado personalizado, eu sei que você provavelmente está pensando, tantos problemas só por isso? bem, delegação é um padrão de design muito importante para entender se você deseja se tornar um iOSdesenvolvedor e sempre lembre-se de que eles têm um relacionamento individual entre os objetos.

Você pode ver o tutorial original aqui

James Rochabrun
fonte
4

A resposta é realmente respondida, mas eu gostaria de lhe dar uma "folha de dicas" para criar um delegado:

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
fonte
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;

Método:

-(void)delegateMEthod: (ArgType) arg{
}
Lal Krishna
fonte
2

No meu ponto de vista, crie uma classe separada para esse método delegado e você pode usar onde quiser.

no meu DropDownClass.h personalizado

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;

depois disso, o arquivo in.m cria uma matriz com objetos,

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

Aqui estão todos definidos para a classe delegada personalizada. Depois que você pode usar esse método delegado onde desejar. Por exemplo ...

na minha outra importação viewcontroller depois disso

crie uma ação para chamar o método delegado como este

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

depois dessa chamada delegar método como este

- (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;
}
}
Usuário558
fonte
0

Delegar: - Criar

@protocol addToCartDelegate <NSObject>

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

@end

Envie e atribua delegado para ver se você está enviando dados

[self.delegate addToCartAction:itemsModel isAdded:YES];
Vaibhav Gaikwad
fonte
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. Implemente o método na classe .m - (void) didRemoveCellWithTag: (NSInteger) tag {NSLog @ ("Tag% d", tag);

}

Rohit Kashyap
fonte
0

Vamos começar com um exemplo: se comprarmos um produto on-line, ele passará por processos como remessa / entrega manipulados por equipes diferentes. seria uma sobrecarga para outras pessoas / fornecedores que desejam passar essas informações apenas para as pessoas necessárias.

Portanto, se pensarmos em termos de nosso aplicativo, um evento pode ser um pedido on-line e equipes diferentes podem ter várias visualizações.

Aqui está o código considere o ShippingView como equipe de envio e DeliveryView como equipe de entrega:

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