Wie richte ich einen einfachen Delegaten für die Kommunikation zwischen zwei Ansichtscontrollern ein?

136

Ich habe zwei UITableViewControllersund muss den Wert vom untergeordneten Ansichtscontroller mithilfe eines Delegaten an das übergeordnete Element übergeben. Ich weiß, was Delegierte sind und wollte nur ein einfach zu befolgendes Beispiel sehen.

Danke

jini
quelle
1
Wenn Sie die Xcode-Vorlage "Utility" ausprobieren, ist bereits ein Delegatenmuster implementiert. Benötigen Sie vielleicht mehr Hilfe?
Phi
Hier ist ein sehr einfaches Tutorial. tutorialspoint.com/ios/ios_delegates.htm
Muhammad_Awaab

Antworten:

304

Einfaches Beispiel ...

Angenommen, der untergeordnete Ansichtscontroller hat eine UISliderund wir möchten den Wert des Schiebereglers über einen Delegaten an das übergeordnete Element zurückgeben.

Deklarieren Sie in der Header-Datei des Controllers der untergeordneten Ansicht den Delegatentyp und seine Methoden:

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

Rufen Sie in der Implementierung des Controllers für untergeordnete Ansichten die Delegatmethoden nach Bedarf auf.

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

Deklarieren Sie in der Header-Datei des übergeordneten View Controllers, dass das ChildViewControllerDelegateProtokoll implementiert wird .

RootViewController.h

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

@interface RootViewController : UITableViewController <ChildViewControllerDelegate>

@end

Implementieren Sie in der Implementierung des übergeordneten Ansichtscontrollers die Delegatmethoden entsprechend.

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

Hoffe das hilft!

Simon Whitaker
quelle
1
Wie registriert sich der Elternteil als Delegierter des Kindes?
Madbreaks
2
Durch einen Anruf detailViewController.delegate = self;(es ist im -tableView:didSelectRowAtIndexPath:obigen Code-Snippet enthalten.
Simon Whitaker
Vielen Dank. Wenn der ChildViewController an UITableView delegiert ist, wo sollten sich die UITableView-Methoden befinden? Im Kind oder im Elternteil?
Dejell
Tolles Beispiel / Erklärung! Leider wird beim Kompilieren die Fehlermeldung "Protokolldeklaration für 'MyProtocol' kann nicht gefunden werden" angezeigt. Es ist jedoch so, wie Sie es beschrieben haben: Der erzeugte Viewcontroller hat die Procotol-Definition in seiner .h-Datei und ruft die Protokollmethode in seiner .m-Datei auf. Der Hosting Viewcontroller hat <MyProtocol> in seiner .h @interface-Deklaration - hier tritt der Fehler auf. Ihre Antwort scheint jedoch dieselbe zu sein ... irgendwelche Ideen?
Danny
Danke dir. Ich habe mindestens ein Dutzend Ressourcen angeschaut und dies ist das erste, dem ich folgen konnte. Ich denke, die nummerierten Codekommentare funktionieren hervorragend, um die Reihenfolge zu erklären.
JaseC
32

Der folgende Code zeigt nur die grundlegende Verwendung des Delegatenkonzepts. Sie benennen die Variable und die Klasse gemäß Ihren Anforderungen.

Zuerst müssen Sie ein Protokoll deklarieren:

Nennen wir es MyFirstControllerDelegate.h

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

Importieren Sie die Datei MyFirstControllerDelegate.h und bestätigen Sie Ihren FirstController mit dem Protokoll MyFirstControllerDelegate

#import "MyFirstControllerDelegate.h"

@interface FirstController : UIViewController<MyFirstControllerDelegate>
{

}

@end

In der Implementierungsdatei müssen Sie beide Funktionen des Protokolls implementieren:

@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

in Ihrem SecondController :

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

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

@end

In der Implementierungsdatei von SecondController .

@implementation SecondController

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

@end

Hier ist der Wiki-Artikel über Delegierte.

Jhaliya
quelle
Hier erfahren Sie, wie Sie ein funktionierendes Delegate-Protokoll einrichten. Ich denke, es gibt ein paar wichtige Punkte. Wenn Sie die Methoden für den Delegaten aufrufen, sollten Sie zunächst überprüfen, ob der Delegat auf diesen Selektor reagiert. Wenn dies nicht der Fall ist, stürzt Ihre App ab. Zweitens müssen Sie das "@protocol MyFirstControllerDelegate" auf @protocol MyFirstControllerDelegate <NSObject>
CW0007007
6

Die folgende Lösung ist ein sehr einfacher und einfacher Ansatz zum Senden von Daten von VC2 an VC1 mithilfe eines Delegaten.

PS: Diese Lösung wurde in Xcode 9.X und Swift 4 erstellt

Deklarierte ein Protokoll und erstellte eine delegierte Variable in 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 bestätigt das Protokoll und erwartet, dass Daten über die Delegatmethode sendData empfangen werden

    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
            }
        }
    }
iOS-Team
quelle