ios - ¿Cómo creo delegados en Objective-C?
cocoa callback (19)
Sé cómo trabajan los delegados y cómo puedo usarlos.
¿Pero cómo los creo?
Versión rápida
Un delegado es solo una clase que hace algo de trabajo para otra clase. Lee el siguiente código para ver un ejemplo de patio de juegos un tanto 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 utilizan a menudo en las siguientes situaciones
- Cuando una clase necesita comunicar alguna información a otra clase.
- Cuando una clase quiere permitir que otra clase la personalice.
Las clases no necesitan saber nada unas de otras de antemano, excepto que la clase delegada cumple con el protocolo requerido.
Recomiendo leer los siguientes dos artículos. Me ayudaron a entender a los delegados incluso mejor que la documentation .
¡Por favor! Consulte a continuación el sencillo tutorial paso a paso para comprender cómo funcionan los delegados en iOS.
He creado dos ViewControllers (para enviar datos de uno a otro)
- FirstViewController implement delegate (que proporciona datos).
- SecondViewController declara el delegado (que recibirá los datos).
Al usar el método de protocolo formal para crear soporte de delegado, descubrí que puede asegurar una verificación de tipo adecuada (aunque sea, 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.
Aquí hay un método simple para crear delegados.
Crear protocolo en el archivo .h. Asegúrese de que esté definido antes del protocolo usando @class seguido del nombre de UIViewController < As the protocol I am going to use is UIViewController class>.
Paso: 1: Cree una nueva clase de Protocolo denominada "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 ha sido definido antes de llamarlo
- (void) someMethodToCallDelegate {
if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
[self.delegate delegateMethodName:self];
}
}
Como buena práctica recomendada por Apple, es bueno que el delegado (que es un protocolo, por definición), se ajuste al protocolo NSObject
.
@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 anotación @optional
la @optional
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 utiliza métodos que ha especificado como opcionales, debe (en su clase) verificar con respondsToSelector
si la vista (que se ajusta a su delegado) ha implementado sus métodos opcionales o no.
Creo que todas estas respuestas tienen mucho sentido una vez que entienden a los delegados. Personalmente vine de la tierra de C / C ++ y antes de eso, los lenguajes de procedimiento como Fortran, etc., así que aquí está mi toma de 2 minutos para encontrar análogos similares en el paradigma C ++.
Si tuviera que explicar los 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 asigna un puntero, puede llamar a funciones / métodos en esa clase. Por lo tanto, algunas funciones de su clase están "delegadas" (en C ++ world - puntero a por un puntero de objeto de clase) a otra clase.
¿Qué son los protocolos? Conceptualmente, tiene un propósito similar al del 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 un delegado dentro de una clase.
¿Cómo puedo hacer algo similar en C ++? Si intentara hacer esto en C ++, definirá los punteros a las clases (objetos) en la definición de la clase y luego los conectará 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 asume que los programadores no son los mejores para mantener esta línea de conducta y proporciona restricciones de compilación para imponer una implementación limpia.
Descargo de responsabilidad: esta es la versión Swift
de cómo crear un delegate
.
Entonces, ¿qué son los delegados? ... en el desarrollo de software, existen arquitecturas de soluciones reutilizables que ayudan a resolver problemas comunes en un contexto dado, estas "plantillas", por así decirlo, se conocen mejor 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. Imagina 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 por descargar o clonar este proyecto inicial y ejecútelo!
Puede ver una aplicación con dos clases, ViewController A
y ViewController B
B tiene dos vistas que, al tocar, cambian el color de fondo del ViewController
, nada demasiado complicado, ¿no? 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 en 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 la necesite.
paso 1: busque la marca de pragma paso 1 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 UIColor
opcional como argumento. Es una buena práctica nombrar sus protocolos agregando la palabra delegate
al final del nombre de la clase, en este caso, ClassBVCDelegate
.
paso 2: busque la marca pragma paso 2 en ClassVBC
y agregue esto
//MARK: step 2 Create a delegate property here.
weak var delegate: ClassBVCDelegate?
Aquí simplemente creamos una propiedad delegada para la clase, esta propiedad debe adoptar el tipo de protocol
y debe ser opcional. Además, debe agregar la palabra clave débil antes de la propiedad para evitar los ciclos de retención y las posibles fugas de memoria. Si no sabe qué significa eso, no se preocupe por el momento, solo recuerde agregar esta palabra clave.
paso 3: busque la marca pragma paso 3 dentro del method
ClassBVC
en ClassBVC
y agregue esto
//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(tapGesture.view?.backgroundColor)
Una cosa que debe saber, ejecutar la aplicación y tocar en 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 no existe todavía. Vayamos ahora al archivo ClassAVC
y ClassAVC
, el delegado.
paso 4: busque la marca pragma paso 4 dentro del método ClassAVC
en ClassAVC
y agregue esto junto a su tipo de clase como este.
//MARK: step 4 conform the protocol here.
class ClassAVC: UIViewController, ClassBVCDelegate {
}
Ahora que ClassAVC adoptó el protocolo ClassBVCDelegate
, puede ver que su compilador le está dando un error que dice "El tipo ''ClassAVC no cumple con el 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 proviene de un fondo Objective-C
, probablemente esté pensando que también puede cerrar ese error haciendo que el método sea opcional, pero para mi sorpresa, y probablemente la suya, el lenguaje Swift
no admite protocols
opcionales, si desea Si lo hace, puede crear una extensión para su protocol
o usar la palabra clave @objc en su implementación de protocol
.
Personalmente, si tengo que crear un protocolo con diferentes métodos opcionales, preferiría dividirlo en diferentes protocols
, así seguiré el concepto de otorgar una única 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: busque la marca de pragma paso 5 dentro del método de preparación para segue y agregue 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 de ClassBVC
y asignamos su delegado a sí mismo, pero ¿qué es yo aquí? Bueno, ¡yo mismo es el ClassAVC
que ha sido delegado!
paso 6: Finalmente, busque el pragma paso 6 en ClassAVC
y utilicemos las funciones del protocol
, comience a escribir func changeBackgroundColor y verá que se está completando automáticamente por usted. Puede agregar cualquier implementación dentro de ella, 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!
Delegates
están en todas partes y es probable que los use sin previo aviso. Si crea una tableview
de tableview
en el pasado que usó delegación, muchas clases de UIKIT
trabajan en torno a ellos y muchos otros frameworks
también, resuelven estos problemas principales.
- Evite el acoplamiento apretado de objetos.
- Modifique el comportamiento y la apariencia sin necesidad de subclasificar objetos.
- Permitir que las tareas sean manejadas fuera de cualquier objeto arbitrario.
Enhorabuena, solo implementas 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 comprender si desea convertirse en un desarrollador de iOS
, y siempre tenga en cuenta que tienen una relación de uno a uno entre los objetos.
Puedes ver el tutorial original here
En mi punto de vista, cree una clase separada para ese método de delegado y puede usar donde desee.
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 una 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 que pueda usar este método de delegado donde desee, por ejemplo ...
en mi otro viewcontroller de importación 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 esa llamada método delegado 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;
}
}
La respuesta aprobada es excelente, pero si está buscando una respuesta de 1 minuto, intente esto:
El archivo MyClass.h debería tener este aspecto (¡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 el método de delegado.
- (void) myClassDelegateMethod: (MyClass *) sender {
NSLog(@"Delegates are great!");
}
Ok, esto no es realmente una respuesta a la pregunta, pero si está buscando cómo hacer que su propio delegado quizás algo mucho más simple podría ser una mejor respuesta para usted.
Apenas implemento a mis delegados porque rara vez necesito. Puedo tener SOLO UN delegado para un objeto delegado. Por lo tanto, si desea que su delegado de una forma de comunicación / transmisión de datos sea 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 clase como observador:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];
Implementar 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 a tu clase como observador si
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
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 los métodos de delegado o delegado.
Un protocolo debe ser declarado como abajo:
@protocol ServiceResponceDelegate <NSObject>
- (void) serviceDidFailWithRequestType:(NSString*)error;
- (void) serviceDidFinishedSucessfully:(NSString*)success;
@end
Esta es la clase de servicio donde se debe hacer alguna tarea. Muestra cómo definir delegado y cómo establecer 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 configurando el delegado a sí mismo. Y también el protocolo se implementa en la clase de cabecera.
@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 los métodos de delegado en esta clase, el control regresará una vez que se realice la operación / tarea.
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 establece un objeto que escribió como delegado en NSWindow, pero su objeto solo tiene implementaciones (métodos) para uno o algunos de los muchos métodos de delegado posibles. Entonces, algo sucede, y NSWindow
quiere llamar a su objeto: solo usa el método respondsToSelector
Objective-c para determinar si su objeto quiere que se llame a ese método, y luego lo llama. Así es como funciona object-c: los métodos se buscan a pedido.
Es totalmente trivial hacer esto con sus propios objetos, no hay nada especial en marcha, por ejemplo, podría tener una NSArray
de 27 objetos, todos los diferentes tipos de objetos, solo 18 de ellos con el método -(void)setToBue;
Los otros 9 no lo hacen. Entonces, para llamar a setToBlue
en todos 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 conservan, por lo que siempre debe establecer el delegado en nil
en su método de MyClass dealloc
.
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{
}
digamos que tiene una clase que desarrolló y desea declarar una propiedad de delegado 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 archivo de encabezado de MyClass
(o un archivo de encabezado separado), y declara los controladores de eventos requeridos / opcionales que su delegado debe / debe implementar, luego declare una propiedad en MyClass
of type ( id< MyClassDelegate>
) lo que significa cualquier clase c objetiva que se ajusta al 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 instancia de MyClass
, por lo tanto, si declaró el delegado como retenido, ambos de ellos se retendrán y ninguno de ellos será liberado).
también notará que los métodos de protocolo pasan la instancia de MyClass
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 instancia de MyClass
y también ayuda cuando el delegado se declara a sí mismo como MyClassDelegate
a varias instancias de MyClass
, como cuando tiene varias instancias UITableView''s
en su ViewController
y se declara como un UITableViewDelegate
para todas ellas.
y dentro de su MyClass
usted 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 tu delegado responde al método de protocolo que estás 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).
Comencemos con un ejemplo: si compramos un producto en línea, pasa por un proceso como el envío / entrega a cargo de diferentes equipos. Por lo tanto, si el envío se completa, el equipo de envío debe notificar al equipo de entrega y debe ser una comunicación individual para transmitir esta información. sería una sobrecarga para otras personas / el proveedor podría querer pasar 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í está el código, considere 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
//
}
}
Delegado: - Crear
@protocol addToCartDelegate <NSObject>
-(void)addToCartAction:(ItemsModel *)itemsModel isAdded:(BOOL)added;
@end
Envía y por favor, asigna delegado para ver que estás enviando datos
[self.delegate addToCartAction:itemsModel isAdded:YES];
La respuesta es en realidad una respuesta, 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
Un delegado de Objective-C es un objeto que se ha asignado a la propiedad delegate
otro objeto. Para crear una, simplemente defina una clase que implemente los métodos de delegado en los que está interesado y marque esa clase como implementando el protocolo de delegado.
Por ejemplo, suponga que tiene un UIWebView
. Si desea implementar el método webViewDidStartLoad:
su delegado, puede crear una clase como esta:
@interface MyClass<UIWebViewDelegate>
// ...
@end
@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView {
// ...
}
@end
Luego, puede crear una instancia de MyClass y asignarla como delegado de la vista web:
MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;
En el lado de UIWebView
, probablemente tiene un código similar a este para ver si el delegado responde al mensaje webViewDidStartLoad:
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 suele ser el delegado de una vista que contiene).
Haciendo delegados para tus clases
Para definir sus propios delegados, deberá declarar sus métodos en algún lugar, como se explica en los Documentos de Apple en los protocolos . Por lo general se declara 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, UIWebViewDelegate
en 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 verificar con -respondsToSelector:
antes de llamar a un método en particular.
Nombrar
Los métodos de delegación normalmente se nombran comenzando con el nombre de la clase de delegación, y toman el objeto de delegación como el primer parámetro. También a menudo usan una forma de voluntad, de deber o de hacer. 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 deseamos enviar un mensaje, puede almacenar esa información en caché 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 comprobar que nuestro delegado maneja los mensajes accediendo a nuestra estructura delegateRespondsTo
, en lugar de enviar -respondsToSelector:
una y otra vez.
Delegados informales
Antes de que existieran los protocolos, era común usar una category en NSObject
para declarar los métodos que un delegado podría implementar. Por ejemplo, CALayer
todavía hace esto:
@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end
Básicamente, esto le dice al compilador que cualquier objeto podría implementar displayLayer:
Luego usaría el mismo enfoque -respondsToSelector:
como se describe anteriormente para llamar a este método. Los delegados simplemente implementan este método y asignan la propiedad del delegate
, y eso es todo (no hay ninguna declaración de que cumpla con un protocolo). Este método es común en las bibliotecas de Apple, pero el código nuevo debe usar el enfoque más moderno del protocolo anterior, ya que este método contamina NSObject
(lo que hace que el autocompletar sea menos útil) y hace que sea difícil para el compilador advertirle sobre errores tipográficos y errores similares.
//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: etiqueta (NSInteger) {NSLog @ ("Tag% d", etiqueta);
}