Jak skonfigurować prostego delegata do komunikacji między dwoma kontrolerami widoku?


136

Mam dwa UITableViewControllersi muszę przekazać wartość z kontrolera widoku podrzędnego do nadrzędnego za pomocą delegata. Wiem, kim są delegaci i chciałem po prostu zobaczyć prosty przykład do naśladowania.

Dziękuję Ci


1
Jeśli wypróbujesz szablon Xcode „Utility”, istnieje już zaimplementowany wzorzec delegata. Czy potrzebujesz więcej pomocy niż to?
phi

Odpowiedzi:


304

Prosty przykład ...

Powiedzmy, że podrzędny kontroler widoku ma a UISlideri chcemy przekazać wartość suwaka z powrotem do rodzica za pośrednictwem delegata.

W pliku nagłówkowym kontrolera widoku podrzędnego zadeklaruj typ delegata i jego metody:

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

W implementacji kontrolera widoku podrzędnego wywołaj metody delegata zgodnie z wymaganiami.

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

W pliku nagłówkowym kontrolera widoku nadrzędnego zadeklaruj, że implementuje on ChildViewControllerDelegateprotokół.

RootViewController.h

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

@interface RootViewController : UITableViewController <ChildViewControllerDelegate>

@end

W implementacji kontrolera widoku nadrzędnego odpowiednio zaimplementuj metody delegata.

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

Mam nadzieję że to pomoże!


1
Jak jednak rodzic rejestruje się jako delegat dziecka?
Madbreaks

2
Dzwoniąc detailViewController.delegate = self;(znajduje się w -tableView:didSelectRowAtIndexPath:powyższym fragmencie kodu.
Simon Whitaker

Dzięki. Jeśli ChildViewController jest delegowany do UITableView, gdzie powinny znajdować się metody UITableView? W dziecku czy rodzicu?
Dejell

Świetny przykład / wyjaśnienie! Niestety, podczas próby kompilacji pojawia się błąd „Nie można znaleźć deklaracji protokołu dla„ MyProtocol ””. Jest jednak tak, jak opisałeś: spawnowany kontroler widoku ma definicję procotol w swoim pliku .h i wywołuje metodę protokołu w swoim pliku .m. Hosting viewcontroller ma <MyProtocol> w swojej deklaracji .h @interface - czyli tam, gdzie występuje błąd. Twoja odpowiedź wydaje się być taka sama, chociaż ... jakieś pomysły?
Danny

Dziękuję Ci. Przejrzałem co najmniej tuzin zasobów i jest to pierwsza, jaką udało mi się zastosować. Myślę, że ponumerowane komentarze do kodu świetnie sprawdzają się, pomagając wyjaśnić jego kolejność.
JaseC

32

Poniższy kod pokazuje tylko bardzo podstawowe użycie koncepcji delegata ... nazywasz zmienną i klasę zgodnie z wymaganiami.

Najpierw musisz zadeklarować protokół:

Nazwijmy to MyFirstControllerDelegate.h

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

Importuj plik MyFirstControllerDelegate.h i potwierdź swój FirstController za pomocą protokołu MyFirstControllerDelegate

#import "MyFirstControllerDelegate.h"

@interface FirstController : UIViewController<MyFirstControllerDelegate>
{

}

@end

W pliku implementacji należy zaimplementować obie funkcje protokołu:

@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

w SecondController :

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

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

@end

W pliku implementacji SecondController .

@implementation SecondController

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

@end

Oto artykuł wiki dotyczący delegata.


Chociaż obejmuje to, jak skonfigurować działający protokół delegata. Myślę, że pomija kilka kluczowych punktów. Po pierwsze, podczas wywoływania metod na delegacie należy najpierw sprawdzić, czy delegat odpowiada na ten selektor. Jeśli tak się nie stanie, aplikacja ulegnie awarii. Po drugie, musisz ustawić „@protocol MyFirstControllerDelegate” na @protocol MyFirstControllerDelegate <NSObject>
CW0007007

6

Poniższe rozwiązanie jest bardzo podstawowym i prostym podejściem do przesyłania danych z VC2 do VC1 za pomocą delegata.

PS: To rozwiązanie jest wykonane w Xcode 9.X i Swift 4

Zadeklarował protokół i utworzył delegata var w 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 potwierdza protokół i oczekuje, że otrzyma dane za pośrednictwem metody delegata 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
            }
        }
    }

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.