¿Cómo configuro un delegado simple para comunicarse entre dos controladores de vista?

136

Tengo dos UITableViewControllersy necesito pasar el valor del controlador de vista hijo al padre usando un delegado. Sé lo que son los delegados y solo quería ver un ejemplo simple de seguir.

Gracias

jini
fuente
1
Si prueba la plantilla Xcode "Utilidad", ya hay un patrón de delegado implementado. ¿Necesitas más ayuda que esa quizás?
phi
Aquí hay un tutorial muy simple. tutorialspoint.com/ios/ios_delegates.htm
Muhammad_Awaab

Respuestas:

304

Ejemplo simple ...

Digamos que el controlador de vista secundario tiene un UISlidery queremos pasar el valor del control deslizante al padre a través de un delegado.

En el archivo de encabezado del controlador de vista secundaria, declare el tipo de delegado y sus métodos:

ChildViewController.h

#import <UIKit/UIKit.h>

// 1. Forward declaration of ChildViewControllerDelegate - this just declares
// that a ChildViewControllerDelegate type exists so that we can use it
// later.
@protocol ChildViewControllerDelegate;

// 2. Declaration of the view controller class, as usual
@interface ChildViewController : UIViewController

// Delegate properties should always be weak references
// See http://stackoverflow.com/a/4796131/263871 for the rationale
// (Tip: If you're not using ARC, use `assign` instead of `weak`)
@property (nonatomic, weak) id<ChildViewControllerDelegate> delegate;

// A simple IBAction method that I'll associate with a close button in
// the UI. We'll call the delegate's childViewController:didChooseValue: 
// method inside this handler.
- (IBAction)handleCloseButton:(id)sender;

@end

// 3. Definition of the delegate's interface
@protocol ChildViewControllerDelegate <NSObject>

- (void)childViewController:(ChildViewController*)viewController 
             didChooseValue:(CGFloat)value;

@end

En la implementación del controlador de vista secundaria, llame a los métodos delegados según sea necesario.

ChildViewController.m

#import "ChildViewController.h"

@implementation ChildViewController

- (void)handleCloseButton:(id)sender {
    // Xcode will complain if we access a weak property more than 
    // once here, since it could in theory be nilled between accesses
    // leading to unpredictable results. So we'll start by taking
    // a local, strong reference to the delegate.
    id<ChildViewControllerDelegate> strongDelegate = self.delegate;

    // Our delegate method is optional, so we should 
    // check that the delegate implements it
    if ([strongDelegate respondsToSelector:@selector(childViewController:didChooseValue:)]) {
        [strongDelegate childViewController:self didChooseValue:self.slider.value];
    }
}

@end

En el archivo de encabezado del controlador de vista principal, declare que implementa el ChildViewControllerDelegateprotocolo.

RootViewController.h

#import <UIKit/UIKit.h>
#import "ChildViewController.h"

@interface RootViewController : UITableViewController <ChildViewControllerDelegate>

@end

En la implementación del controlador de vista principal, implemente los métodos delegados adecuadamente.

RootViewController.m

#import "RootViewController.h"

@implementation RootViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    ChildViewController *detailViewController = [[ChildViewController alloc] init];
    // Assign self as the delegate for the child view controller
    detailViewController.delegate = self;
    [self.navigationController pushViewController:detailViewController animated:YES];
}

// Implement the delegate methods for ChildViewControllerDelegate
- (void)childViewController:(ChildViewController *)viewController didChooseValue:(CGFloat)value {

    // Do something with value...

    // ...then dismiss the child view controller
    [self.navigationController popViewControllerAnimated:YES];
}

@end

¡Espero que esto ayude!

Simon Whitaker
fuente
1
Sin embargo, ¿cómo se registra el padre como delegado del niño?
Madbreaks
2
Al llamar detailViewController.delegate = self;(está en -tableView:didSelectRowAtIndexPath:el fragmento de código anterior.
Simon Whitaker
Gracias. Si el ChildViewController se delega a UITableView, ¿dónde deberían estar los métodos UITableView? ¿En el niño o el padre?
Dejell
Gran ejemplo / explicación! Desafortunadamente, recibo un error "No se puede encontrar la declaración de protocolo para 'MyProtocol'" cuando intento compilar. Sin embargo, es como lo describió: el viewcontroller generado tiene la definición de protocolo en su archivo .h e invoca el método de protocolo en su archivo .m. El controlador de vista de alojamiento tiene <MyProtocol> en su declaración .h @interface, que es donde ocurre el error. Sin embargo, su respuesta parece ser la misma ... ¿alguna idea?
Danny
Gracias. He buscado al menos una docena de recursos y este es el primero que he podido seguir. Creo que los comentarios del código numerado funcionan muy bien para ayudar a explicar la secuencia del mismo.
JaseC
32

El siguiente código solo muestra el uso muy básico del concepto de delegado ... usted nombra la variable y la clase según sus requisitos.

Primero necesitas declarar un protocolo:

Llamémoslo MyFirstControllerDelegate.h

@protocol MyFirstControllerDelegate
- (void) FunctionOne: (MyDataOne*) dataOne;
- (void) FunctionTwo: (MyDatatwo*) dataTwo;
@end

Importe el archivo MyFirstControllerDelegate.h y confirme su FirstController con el protocolo MyFirstControllerDelegate

#import "MyFirstControllerDelegate.h"

@interface FirstController : UIViewController<MyFirstControllerDelegate>
{

}

@end

En el archivo de implementación, debe implementar ambas funciones de protocolo:

@implementation FirstController 


    - (void) FunctionOne: (MyDataOne*) dataOne
      {
          //Put your finction code here
      }
    - (void) FunctionTwo: (MyDatatwo*) dataTwo
      {
          //Put your finction code here
      }

     //Call below function from your code
    -(void) CreateSecondController
     {
             SecondController *mySecondController = [SecondController alloc] initWithSomeData:.];
           //..... push second controller into navigation stack 
            mySecondController.delegate = self ;
            [mySecondController release];
     }

@end

en su SecondController :

@interface SecondController:<UIViewController>
{
   id <MyFirstControllerDelegate> delegate;
}

@property (nonatomic,assign)  id <MyFirstControllerDelegate> delegate;

@end

En el archivo de implementación de SecondController .

@implementation SecondController

@synthesize delegate;
//Call below two function on self.
-(void) SendOneDataToFirstController
{
   [delegate FunctionOne:myDataOne];
}
-(void) SendSecondDataToFirstController
{
   [delegate FunctionTwo:myDataSecond];
}

@end

Aquí está el artículo wiki sobre delegado.

Jhaliya
fuente
Si bien esto cubre cómo configurar un protocolo Delegado que funcione. Creo que está omitiendo algunos puntos clave. En primer lugar, al llamar a los métodos en el delegado, primero debe verificar que el delegado responda a ese selector. Si no lo hace, su aplicación se bloqueará. En segundo lugar, debe configurar "@protocol MyFirstControllerDelegate" en @protocol MyFirstControllerDelegate <NSObject>
CW0007007
6

La siguiente solución es un enfoque muy básico y simple para enviar datos desde VC2 a VC1 mediante delegado.

PD: esta solución está hecha en Xcode 9.X y Swift 4

Declaró un protocolo y creó un delegado var en ViewControllerB

    import UIKit

    //Declare the Protocol into your SecondVC
    protocol DataDelegate {
        func sendData(data : String)
    }

    class ViewControllerB : UIViewController {

    //Declare the delegate property in your SecondVC
        var delegate : DataDelegate?
        var data : String = "Send data to ViewControllerA."
        override func viewDidLoad() {
            super.viewDidLoad()
        }

        @IBAction func btnSendDataPushed(_ sender: UIButton) {
                // Call the delegate method from SecondVC
                self.delegate?.sendData(data:self.data)
                dismiss(animated: true, completion: nil)
            }
        }

ViewControllerA confirma el protocolo y espera recibir datos a través del método de delegado sendData

    import UIKit
        // Conform the  DataDelegate protocol in ViewControllerA
        class ViewControllerA : UIViewController , DataDelegate {
        @IBOutlet weak var dataLabel: UILabel!

        override func viewDidLoad() {
            super.viewDidLoad()
        }

        @IBAction func presentToChild(_ sender: UIButton) {
            let childVC =  UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier:"ViewControllerB") as! ViewControllerB
            //Registered delegate
            childVC.delegate = self
            self.present(childVC, animated: true, completion: nil)
        }

        // Implement the delegate method in ViewControllerA
        func sendData(data : String) {
            if data != "" {
                self.dataLabel.text = data
            }
        }
    }
equipo iOS
fuente