¿Cómo creo delegados en Objective-C?

Respuestas:

889

Un delegado de Objective-C es un objeto que ha sido asignado a la delegatepropiedad de otro objeto. Para crear uno, usted define una clase que implementa los métodos de delegado que le interesan, y marca esa clase como la implementación del protocolo de delegado.

Por ejemplo, suponga que tiene un UIWebView. Si desea implementar el webViewDidStartLoad:método de su delegado , puede crear una clase como esta:

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

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

Luego, podría crear una instancia de MyClass y asignarla como delegado de la vista web:

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

Por UIWebViewotro lado, probablemente tenga un código similar a este para ver si el delegado responde al webViewDidStartLoad:mensaje utilizando respondsToSelector:y lo envía si corresponde.

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

La propiedad del delegado en sí se suele declarar weak(en ARC) o assign(pre-ARC) para evitar retener bucles, ya que el delegado de un objeto a menudo tiene una fuerte referencia a ese objeto. (Por ejemplo, un controlador de vista a menudo es el delegado de una vista que contiene).

Hacer delegados para tus clases

Para definir sus propios delegados, tendrá que declarar sus métodos en algún lugar, como se discutió en los Documentos de Apple sobre protocolos . Usualmente declaras un protocolo formal. La declaración, parafraseada de UIWebView.h, se vería así:

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

Esto es análogo a una interfaz o clase base abstracta, ya que crea un tipo especial para su delegado, UIWebViewDelegateen este caso. Los implementadores delegados tendrían que adoptar este protocolo:

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

Y luego implementar los métodos en el protocolo. Para los métodos declarados en el protocolo como @optional(como la mayoría de los métodos delegados), debe consultar -respondsToSelector:antes de llamar a un método en particular.

Nombrar

Los métodos delegados generalmente se nombran comenzando con el nombre de la clase delegante y toman el objeto delegado como primer parámetro. También a menudo usan una forma de voluntad, debería o did. Entonces, webViewDidStartLoad:(el primer parámetro es la vista web) en lugar de loadStarted(sin tomar parámetros), por ejemplo.

Optimizaciones de velocidad

En lugar de verificar si un delegado responde a un selector cada vez que queremos enviarle un mensaje, puede almacenar en caché esa información cuando se configuran los delegados. Una forma muy limpia de hacer esto es usar un campo de bits, de la siguiente manera:

@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

Luego, en el cuerpo, podemos verificar que nuestro delegado maneja los mensajes accediendo a nuestra delegateRespondsToestructura, en lugar de enviarlos -respondsToSelector:una y otra vez.

Delegados informales

Antes de que existieran los protocolos, era común el uso de una categoría en la NSObjectque declare los métodos delegado podría implementar. Por ejemplo, CALayertodavía hace esto:

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

Esto le dice al compilador que cualquier objeto podría implementarse displayLayer:.

A continuación, utilizaría el mismo -respondsToSelector:enfoque que el descrito anteriormente para llamar a este método. Los delegados implementan este método y asignan la delegatepropiedad, y eso es todo (no hay declaración de que cumpla con un protocolo). Este método es común en las bibliotecas de Apple, pero el nuevo código debería usar el enfoque de protocolo más moderno anterior, ya que este enfoque contamina NSObject(lo que hace que el autocompletado sea menos útil) y dificulta que el compilador le advierta sobre errores tipográficos y errores similares.

Jesse Rusak
fuente
Creo que necesita convertir el unsigned inttipo a BOOLcomo el valor de retorno de delegate respondsToSelectores de tipo BOOL.
Roland
¿Se puede usar delegado para polimorfismo como en C ++?
@ Dan Sí, claro. Los protocolos en general se usan para el polimorfismo.
Jesse Rusak
@JesseRusak Creo que "JSSomethingDelegate" debería ser "SomethingDelegate" por coherencia :)
Hans Knöchel
382

La respuesta aprobada es excelente, pero si está buscando una respuesta de 1 minuto intente esto:

El archivo MyClass.h debería verse así (¡agregue líneas de delegado con comentarios!)

#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

El archivo MyClass.m debería verse así

#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 su delegado en otra clase (UIViewController llamado MyVC en este caso) 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
fuente
44
Es genial usar esta respuesta como referencia rápida. Pero, ¿por qué la propiedad delegada en su MyClass.h está marcada como 'IBOutlet'?
Arno van der Meer
44
@ArnovanderMeer ¡Buena captura! No recuerdo por qué. Lo necesito en mi proyecto pero no en este ejemplo, lo eliminé. thx
Tibidabo
Gracias. Tan agradable y exhaustiva como es la respuesta aceptada, aprendo mejor de algún código de muestra compacto. Es bueno tener dos respuestas.
sudo
@Tibidabo Totalmente excepcional. Realmente desearía que todos pudieran explicar conceptos de programación como este. ¡He visto cientos de explicaciones, sobre 'delegados', a lo largo de los años y nunca he entendido esta teoría hasta ahora! Muchas gracias ...
Charles Robertson
55
¿Dónde se myClassinstancia dentro de MyVC.m?
Lane Rettig
18

Al usar el método de protocolo formal para crear soporte de delegado, descubrí que puede garantizar una verificación de tipo adecuada (aunque, tiempo de ejecución, no tiempo de compilación) agregando algo como:

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

en su código de acceso delegado (setDelegate). Esto ayuda a minimizar los errores.

umop
fuente
18

¡Por favor! revise a continuación el sencillo tutorial paso a paso para comprender cómo funciona Delegados en iOS.

Delegar en iOS

He creado dos ViewControllers (para enviar datos de uno a otro)

  1. Delegado de implementación FirstViewController (que proporciona datos).
  2. SecondViewController declara el delegado (que recibirá los datos).
swiftBoy
fuente
17

Tal vez esto sea más parecido a lo que te estás perdiendo:

Si viene desde un punto de vista similar a C ++, los delegados tardan un poco en acostumbrarse, pero básicamente 'simplemente funcionan'.

La forma en que funciona es que establezca algún objeto que escribió como delegado a NSWindow, pero su objeto solo tiene implementaciones (métodos) para uno o algunos de los muchos posibles métodos de delegado. Entonces, sucede algo y NSWindowquiere llamar a su objeto: solo usa el respondsToSelectormétodo Objective-c para determinar si su objeto quiere que se llame a ese método y luego lo llama. Así es como funciona Objective-C: los métodos se buscan a pedido.

Es totalmente trivial hacer esto con sus propios objetos, no está sucediendo nada especial, por ejemplo, puede tener uno NSArrayde 27 objetos, todos los diferentes tipos de objetos, solo 18 algunos de ellos tienen el método -(void)setToBue;Los otros 9 no. Entonces, para llamar setToBluea los 18 que lo necesitan, algo como esto:

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

La otra cosa acerca de los delegados es que no se retienen, por lo que siempre debe establecer el delegado nilen su MyClass deallocmétodo.

Tom Andersen
fuente
15

Como una buena práctica recomendada por Apple, es bueno para el delegado (que es un protocolo, por definición), cumplir con el NSObjectprotocolo.

@protocol MyDelegate <NSObject>
    ...
@end

& para crear métodos opcionales dentro de su delegado (es decir, métodos que no necesariamente deben implementarse), puede usar la @optionalanotación de esta manera:

@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

Por lo tanto, cuando use métodos que haya especificado como opcionales, debe verificar (en su clase) respondsToSelectorsi la vista (que se ajusta a su delegado) realmente ha implementado sus métodos opcionales o no.

Vaquero
fuente
11

Creo que todas estas respuestas tienen mucho sentido una vez que entiendes a los delegados. Personalmente, vine de la tierra de C / C ++ y, antes de eso, lenguajes de procedimientos como Fortran, etc., así que aquí están mis 2 minutos para encontrar análogos similares en el paradigma de C ++.

Si tuviera que explicar delegados a un programador de C ++ / Java, diría

¿Qué son los delegados? Estos son punteros estáticos a clases dentro de otra clase. Una vez que asigne un puntero, puede llamar a funciones / métodos en esa clase. Por lo tanto, algunas funciones de su clase están "delegadas" (en el mundo C ++ - puntero a por un puntero de objeto de clase) a otra clase.

¿Qué son los protocolos? Conceptualmente sirve como un propósito similar al archivo de encabezado de la clase que está asignando como clase delegada. Un protocolo es una forma explícita de definir qué métodos deben implementarse en la clase cuyo puntero se estableció como delegado dentro de una clase.

¿Cómo puedo hacer algo similar en C ++? Si trató de hacer esto en C ++, lo haría definiendo punteros a clases (objetos) en la definición de clase y luego conectándolos a otras clases que proporcionarán funciones adicionales como delegados a su clase base. Pero este cableado debe mantenerse dentro del código y será torpe y propenso a errores. El objetivo C simplemente supone que los programadores no son los mejores para mantener esta declinación y proporciona restricciones del compilador para exigir una implementación limpia.

DrBug
fuente
De lo que estás hablando es de semántica mientras yo hablaba de la intuición. De lo que estás hablando es de la función virtual, pero solo acostumbrarte a la nueva terminología puede ser un desafío. La respuesta sirve a los principiantes que quieran pensar en un paralell en C ++ / C
DrBug
Lo que dices no está muy claro para mí. ¿Por qué no escribe una nueva respuesta y veamos si más personas la encuentran útil, la votarán?
DrBug
9

Versión rápida

Un delegado es solo una clase que hace algo de trabajo para otra clase. Lea el siguiente código para obtener un ejemplo de Playground algo tonto (pero con suerte esclarecedor) que muestra cómo se hace esto en 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!"
}

En la práctica real, los delegados se usan a menudo en las siguientes situaciones

  1. Cuando una clase necesita comunicar alguna información a otra clase
  2. Cuando una clase quiere permitir que otra clase lo personalice

Las clases no necesitan saber de antemano, excepto que la clase delegada se ajusta al protocolo requerido.

Recomiendo leer los siguientes dos artículos. Me ayudaron a entender a los delegados incluso mejor que la documentación .

Suragch
fuente
8

Ok, esta no es realmente una respuesta a la pregunta, pero si está buscando cómo hacer su propio delegado, tal vez algo mucho más simple podría ser una mejor respuesta para usted.

Apenas implemento a mis delegados porque rara vez lo necesito. Solo puedo tener UN delegado para un objeto delegado. Por lo tanto, si desea que su delegado se comunique / pase datos de una manera, es mucho mejor que con las notificaciones.

NSNotification puede pasar objetos a más de un destinatario y es muy fácil de usar. Funciona así:

El archivo MyClass.m debería verse así

#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 su notificación en otras clases: Agregue la clase como observador:

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

Implemente el selector:

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

No olvides eliminar tu clase como observador si

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

supongamos que tiene una clase que desarrolló y desea declarar una propiedad delegada para poder notificarla cuando ocurra algún evento:

@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

así que declara un protocolo en el MyClassarchivo de encabezado (o un archivo de encabezado separado) y declara los controladores de eventos requeridos / opcionales que su delegado debe / debe implementar, luego declara una propiedad MyClassde tipo ( id< MyClassDelegate>) que significa cualquier clase de objetivo c que se ajuste a En el protocolo MyClassDelegate, notará que la propiedad del delegado se declara débil, esto es muy importante para evitar el ciclo de retención (la mayoría de las veces el delegado retiene la MyClassinstancia, por lo que si declara al delegado como retener, ambos se retendrán entre sí y ninguno de ellos serán lanzados alguna vez).

también notará que los métodos de protocolo pasan la MyClassinstancia al delegado como parámetro, esta es la mejor práctica en caso de que el delegado quiera llamar a algunos métodos en la MyClassinstancia y también ayuda cuando el delegado se declara MyClassDelegateen varias MyClassinstancias, como cuando tiene múltiples UITableView'sinstancias en tu ViewControllery se declara como una UITableViewDelegatepara todos ellos.

y dentro de MyClassusted notifica al delegado con los eventos declarados de la siguiente manera:

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

primero verifica si su delegado responde al método de protocolo que está a punto de llamar en caso de que el delegado no lo implemente y la aplicación se bloquee (incluso si se requiere el método de protocolo).

m.eldehairy
fuente
6

Aquí hay un método simple para crear delegados

Crear protocolo en archivo .h. Asegúrese de que esté definido antes del protocolo usando @class seguido del nombre del UIViewController< As the protocol I am going to use is UIViewController class>.

Paso: 1: Cree una nueva clase de protocolo llamada "YourViewController", que será la subclase de la clase UIViewController y asigne esta clase al segundo ViewController.

Paso: 2: vaya al archivo "YourViewController" y modifíquelo de la siguiente manera:

#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

Los métodos definidos en el comportamiento del protocolo se pueden controlar con @optional y @required como parte de la definición del protocolo.

Paso: 3: Implementación del 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

// prueba si el método se ha definido antes de llamarlo

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

Para crear su propio delegado, primero debe crear un protocolo y declarar los métodos necesarios, sin implementar. Y luego implemente este protocolo en su clase de encabezado donde desea implementar el delegado o los métodos de delegado.

Un protocolo debe declararse de la siguiente manera:

@protocol ServiceResponceDelegate <NSObject>

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

@end

Esta es la clase de servicio donde se debe realizar alguna tarea. Muestra cómo definir delegado y cómo configurar el delegado. En la clase de implementación, una vez completada la tarea, se llaman los métodos del delegado.

@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

Esta es la clase de vista principal desde donde se llama a la clase de servicio estableciendo el delegado en sí mismo. Y también el protocolo se implementa en la clase de encabezado.

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

- (void) go;

@end

@implementation viewController

//
//some methods
//

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

Eso es todo, y al implementar métodos de delegado en esta clase, el control volverá una vez que se complete la operación / tarea.

Mahesh
fuente
4

Descargo de responsabilidad: esta es la Swiftversión de cómo crear un delegate.

Entonces, ¿qué son los delegados? … En el desarrollo de software, existen arquitecturas de soluciones reutilizables generales que ayudan a resolver problemas comunes dentro de un contexto dado, estas "plantillas", por así decirlo, son mejor conocidas como patrones de diseño. Los delegados son un patrón de diseño que permite que un objeto envíe mensajes a otro objeto cuando ocurre un evento específico. Imagine que un objeto A llama a un objeto B para realizar una acción. Una vez que se completa la acción, el objeto A debe saber que B ha completado la tarea y tomar las medidas necesarias, ¡esto se puede lograr con la ayuda de los delegados!

Para una mejor explicación, le mostraré cómo crear un delegado personalizado que pase datos entre clases, con Swift en una aplicación simple, comience descargando o clonando este proyecto inicial y ejecútelo.

Puedes ver una aplicación con dos clases, ViewController Ay ViewController B. B tiene dos vistas que al tocar cambian el color de fondo del ViewController, nada demasiado complicado ¿verdad? bueno, ahora pensemos de una manera fácil para cambiar también el color de fondo de la clase A cuando se tocan las vistas de la clase B.

El problema es que estas vistas son parte de la clase B y no tienen idea de la clase A, por lo que necesitamos encontrar una manera de comunicarnos entre estas dos clases, y ahí es donde brilla la delegación. Dividí la implementación en 6 pasos para que pueda usar esto como una hoja de trucos cuando lo necesite.

Paso 1: busque el paso 1 de la marca pragma en el archivo ClassBVC y agregue esto

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

El primer paso es crear un protocol, en este caso, crearemos el protocolo en la clase B, dentro del protocolo puede crear tantas funciones como desee según los requisitos de su implementación. En este caso, solo tenemos una función simple que acepta un opcional UIColorcomo argumento. Es una buena práctica nombrar sus protocolos agregando la palabra delegateal final del nombre de la clase, en este caso ClassBVCDelegate,.

paso 2: busca la marca pragma en el paso 2 ClassVBCy agrega esto

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

Aquí solo creamos una propiedad delegada para la clase, esta propiedad debe adoptar el protocoltipo y debe ser opcional. Además, debe agregar la palabra clave débil antes de la propiedad para evitar ciclos de retención y posibles pérdidas de memoria, si no sabe lo que eso significa, no se preocupe por ahora, solo recuerde agregar esta palabra clave.

Paso 3: Busque la marca de paso pragma 3 dentro de la handleTap methoden ClassBVCy añade este

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

Una cosa que debe saber, ejecute la aplicación y toque cualquier vista, no verá ningún comportamiento nuevo y eso es correcto, pero lo que quiero señalar es que la aplicación no se bloquea cuando se llama al delegado, y es porque lo creamos como un valor opcional y es por eso que no se bloqueará incluso si el delegado aún no existe. Vayamos ahora al ClassAVCarchivo y hagámoslo , el delegado.

paso 4: busca la marca de pragma paso 4 dentro del método handleTap ClassAVCy agrega esto al lado de tu tipo de clase de esta manera.

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

Ahora ClassAVC adoptó el ClassBVCDelegateprotocolo, puede ver que su compilador le está dando un error que dice "Escriba 'ClassAVC no se ajusta al protocolo' ClassBVCDelegate 'y esto solo significa que aún no usó los métodos del protocolo, imagine que cuando la clase A adopta el protocolo es como firmar un contrato con la clase B y este contrato dice "¡Cualquier clase que me adopte DEBE usar mis funciones!"

Nota rápida: si vienes de un Objective-Cfondo probablemente estés pensando que también puedes callar ese error haciendo que ese método sea opcional, pero para mi sorpresa, y probablemente la tuya, el Swiftidioma no es opcional protocols, si quieres hacerlo puedes crear una extensión para usted protocolo use la palabra clave @objc en su protocolimplementación.

Personalmente, si tengo que crear un protocolo con diferentes métodos opcionales, preferiría dividirlo en otro protocols, de esa manera seguiré el concepto de dar una sola responsabilidad a mis objetos, pero puede variar según la implementación específica.

Aquí hay un buen artículo sobre métodos opcionales.

paso 5: busca la marca pragma paso 5 dentro del método de preparación para el seguimiento y agrega esto

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

Aquí solo estamos creando una instancia ClassBVCy asignamos su delegado a self, pero ¿qué es self aquí? bueno, yo es el ClassAVCque ha sido delegado!

paso 6: Finalmente, busque el pragma paso 6 ClassAVCy usemos las funciones de protocol, comience a escribir func changeBackgroundColor y verá que se completa automáticamente por usted. Puede agregar cualquier implementación dentro de él, en este ejemplo, solo cambiaremos el color de fondo, agregue esto.

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

Ahora ejecuta la aplicación!

Delegatesestán en todas partes y probablemente los use sin previo aviso, si crea una tableviewdelegación en el pasado, usó muchas clases de UIKITtrabajos a su alrededor y muchos otros frameworkstambién, resuelven estos problemas principales.

  • Evite el acoplamiento apretado de objetos.
  • Modifique el comportamiento y la apariencia sin la necesidad de subclasificar objetos.
  • Permita que las tareas se manejen a cualquier objeto arbitrario.

Enhorabuena, acabas de implementar un delegado personalizado, sé que probablemente estás pensando, ¿tantos problemas solo por esto? bueno, la delegación es un patrón de diseño muy importante para entender si quieres convertirte en iOSdesarrollador, y siempre ten en cuenta que tienen una relación uno a uno entre los objetos.

Puedes ver el tutorial original aquí.

James Rochabrun
fuente
4

La respuesta es realmente respondida, pero me gustaría darle una "hoja de trucos" para crear un 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
fuente
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
fuente
2

Desde mi punto de vista, crea una clase separada para ese método de delegado y puedes usarla donde quieras.

en mi Custom 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;

después de ese archivo in.m crear matriz con 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];
        }
    }
}];
}

Aquí todos están configurados para la clase de delegado personalizado. Después de eso, puede usar este método de delegado donde lo desee. Por ejemplo ...

en mi otra importación viewcontroller después de eso

crear acción para llamar al 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];
}

después de ese método delegado de llamada 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;
}
}
Usuario558
fuente
0

Delegado: - Crear

@protocol addToCartDelegate <NSObject>

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

@end

Enviar y asignar delegado para ver que está enviando datos

[self.delegate addToCartAction:itemsModel isAdded:YES];
Vaibhav Gaikwad
fuente
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 el método en la clase .m - (void) didRemoveCellWithTag: (NSInteger) tag {NSLog @ ("Tag% d", tag);

}

Rohit Kashyap
fuente
0

Comencemos con un ejemplo, si compramos un producto en línea, pasa por un proceso como el envío / entrega manejado por diferentes equipos. Entonces, si el envío se completa, el equipo de envío debe notificar al equipo de entrega y debe ser una comunicación uno a uno cuando se transmite esta información sería una sobrecarga para otras personas / proveedores que deseen transmitir esta información solo a las personas requeridas.

Entonces, si pensamos en términos de nuestra aplicación, un evento puede ser un pedido en línea y diferentes equipos pueden ser como múltiples vistas.

Aquí hay un código que considera ShippingView como equipo de envío y DeliveryView como equipo 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
fuente