Як налаштувати простий делегат для зв'язку між двома контролерами перегляду?


136

У мене є два UITableViewControllersі потрібно передати значення з контролера дочірнього виду батькові за допомогою делегата. Я знаю, що таке делегати, і просто хотів побачити простий наслідок прикладу.

Спасибі


1
Якщо ви спробуєте Xcode-шаблон «Утиліта», вже є реалізований шаблон делегата. Вам потрібна більше допомоги, ніж це, можливо?
phi

Ось дуже простий підручник. tutorialspoint.com/ios/ios_delegates.htm
Muhammad_Awaab

Відповіді:


304

Простий приклад ...

Скажімо, у контролері дочірнього перегляду є а, UISliderі ми хочемо передати значення повзунка батькові через делегат.

У файлі заголовка контролера дочірнього пристрою оголосити тип делегата та його методи:

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

У реалізації контролера дочірнього перегляду викличте методи делегування відповідно до необхідності.

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

У файлі заголовка контролера батьківського виду заявіть, що він реалізує ChildViewControllerDelegateпротокол.

RootViewController.h

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

@interface RootViewController : UITableViewController <ChildViewControllerDelegate>

@end

У реалізації контролера батьківського перегляду належним чином реалізуйте методи делегування.

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

Сподіваюся, це допомагає!


1
Як же батько реєструється як делегат дитини?
Madbreaks

2
Зателефонувавши detailViewController.delegate = self;(це в -tableView:didSelectRowAtIndexPath:наведеному вище фрагменті коду.
Simon Whitaker

Дякую. Якщо ChildViewController делегований UITableView, де повинні бути методи UITableView? У дитини чи батька?
Dejell

Чудовий приклад / пояснення! На жаль, під час спроби компілювати я отримую декларацію протоколу для помилки "MyProtocol". Це так, як ви описали, хоча: породжений контролер перегляду має визначення прокотолу у своєму .h файлі та викликає метод протоколу у своєму .m файлі. У хостинг-контролері перегляду є <MyProtocol> у своїй .h @ інтерфейсній декларації - саме там відбувається помилка. Здається, ваша відповідь однакова, хоча ... якісь ідеї?
Денні

Дякую. Я переглянув щонайменше десяток ресурсів, і це перший, який я зміг слідкувати. Я думаю, що пронумеровані коментарі до коду чудово допомагають пояснити послідовність його.
JaseC

32

Цей нижче код просто показує саме основне використання концепції делегата .. Ви називаєте змінну та клас відповідно до вашої вимоги.

Спочатку потрібно оголосити протокол:

Назвемо це MyFirstControllerDelegate.h

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

Імпортуйте файл MyFirstControllerDelegate.h та підтвердьте свій FirstController протоколом MyFirstControllerDelegate

#import "MyFirstControllerDelegate.h"

@interface FirstController : UIViewController<MyFirstControllerDelegate>
{

}

@end

У файлі реалізації вам потрібно реалізувати обидві функції протоколу:

@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

у вашому SecondController :

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

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

@end

У файлі реалізації SecondController .

@implementation SecondController

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

@end

Ось стаття wiki про делегата.


Хоча це стосується того, як налаштувати робочий протокол Delegate. Я думаю, що це пропускає кілька ключових моментів. По-перше, при виклику методів делегата слід спершу перевірити, чи відповідає делегат на цей селектор. Якщо це не буде, ваш додаток вийде з ладу. По-друге, потрібно встановити "@protocol MyFirstControllerDelegate" на @protocol MyFirstControllerDelegate <NSObject>
CW0007007

6

Наступне рішення - дуже базовий і простий підхід для передачі даних з VC2 в VC1 за допомогою делегата.

PS: Це рішення зроблено в Xcode 9.X і Swift 4

Заявлений протокол і створив делегат вар в 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 підтверджує протокол і очікується на отримання даних через делегатний метод 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
            }
        }
    }

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.