Лучший способ передать данные из Child Modal VC в контроллер родительского контроля?

Каков наилучший способ передачи данных из дочернего модального представления в родительский контроллер?

У меня есть экран входа в Mod Mod для моего iPad-приложения, в котором я хочу передать информацию о пользователе родительскому контроллеру Split View.

Я думаю об использовании NSNotification, но я не уверен, что это самый простой/наиболее эффективный способ передачи данных родительскому.

Спасибо! Алан

Ответ 1

Я бы предложил как iPatel, чтобы использовать delegation для решения вашей проблемы. Связь между родительским контроллером представления и контроллером входа в систему делает этот шаблон подходящим. Когда один объект создает другой, чтобы выполнить определенную ответственность, следует рассматривать делегирование как способ связать созданный объект с создателем. Особенно убедительной причиной выбора делегирования было бы, если задача, которая должна быть выполнена, потенциально имеет несколько шагов, требующих высокого уровня взаимодействия между объектами. Вы можете посмотреть на NSURLConnectionDelegate protocol в качестве иллюстрации этого. Подключение к URL-адресу - сложная задача, включающая такие этапы, как обработка ответов, решение проблем аутентификации, сохранение загруженных данных и обработка ошибок, соединение и делегат обрабатывают это вместе в течение всего срока службы соединения.

Как вы, наверное, заметили, в Objective-C протоколы используются для достижения делегирования без жесткой привязки созданного объекта (в данном случае вашего контроллера входа в систему) к объекту, который его создал (родительский контроллер представления). Контроллер просмотра входа может затем взаимодействовать с любым объектом, который может получать сообщения, определенные в его протоколе, а не полагаться на какую-либо конкретную реализацию класса. Завтра, если вы получите требование разрешить любому диспетчеру просмотра отображать окно входа в систему, контроллер входа в систему не потребуется изменять. Ваши другие контроллеры представлений могут реализовать свой протокол делегатов, создать и представить окно входа в систему и назначить себя делегатами без контроллера входа в систему, когда-либо зная о своем существовании.

Некоторые примеры делегирования, которые вы найдете в Stack Overflow, могут быть очень запутанными и очень не похожими на то, что найдено во встроенных фреймворках. Нужно тщательно выбирать имена и интерфейсы протоколов, а также обязанности, назначенные каждому объекту, чтобы максимально использовать повторное использование кода и цель кода.

Сначала вы должны взглянуть на многие протоколы делегатов внутри встроенных фреймворков, чтобы понять, как выглядит отношение при выражении кода. Вот еще один небольшой пример, основанный на вашем случае использования вашего логина. Надеюсь, вы обнаружите, что цель делегации ясна и что роли и обязанности задействованных объектов ясны и выражаются через их имена в коде.

Сначала рассмотрим протокол делегата LoginViewController:

#import <UIKit/UIKit.h>

@protocol LoginViewControllerDelegate;

@interface LoginViewController : UIViewController

// We choose a name here that expresses what object is doing the delegating
@property (nonatomic, weak) id<LoginViewControllerDelegate> delegate;

@end

@protocol LoginViewControllerDelegate <NSObject>

// The methods declared here are all optional
@optional

// We name the methods here in a way that explains what the purpose of each message is
// Each takes a LoginViewController as the first argument, allowing one object to serve
// as the delegate of many LoginViewControllers
- (void)loginViewControllerDidLoginSuccessfully:(LoginViewController *)lvc;
- (void)loginViewController:(LoginViewController *)lvc didFailWithError:(NSError *)error;
- (void)loginViewControllerDidReceivePasswordResetRequest:(LoginViewController *)lvc;
- (void)loginViewControllerDiDReceiveSignupRequest:(LoginViewController *)lvc;
- (BOOL)loginViewControllerShouldAllowAnonymousLogin:(LoginViewController *)lvc;

@end

Контроллер входа может передавать несколько событий своему делегату, а также запрашивать у своего делегата информацию, используемую для настройки его поведения. Он передает события делегату в его реализацию как часть своего ответа на действия пользователя:

#import "LoginViewController.h"

@interface LoginViewController ()

@property (weak, nonatomic) IBOutlet UIButton *anonSigninButton;

@end

@implementation LoginViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    //  Here we ask the delegate for information used to layout the view
    BOOL anonymousLoginAllowed = NO;
    //  All our protocol methods are @optional, so we must check they are actually implemented before calling.
    if ([self.delegate respondsToSelector:@selector(loginViewControllerShouldAllowAnonymousLogin:)]) {
        // self is passed as the LoginViewController argument to the delegate methods
        // in this way our delegate can serve as the delegate of multiple login view controllers, if needed
        anonymousLoginAllowed = [self.delegate loginViewControllerShouldAllowAnonymousLogin:self];
    }
    self.anonSigninButton.hidden = !anonymousLoginAllowed;
}

- (IBAction)loginButtonAction:(UIButton *)sender
{
    // We're preteneding our password is always bad. So we assume login succeeds when allowed anonmously
    BOOL loginSuccess = [self isAnonymousLoginEnabled];
    NSError *loginError = [self isAnonymousLoginEnabled] ? nil : [NSError errorWithDomain:@"domain" code:0 userInfo:nil];

    //  Fake concurrency
    double delayInSeconds = 1.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        //  Notify delegate of failure or success
        if (loginSuccess) {
            if ([self.delegate respondsToSelector:@selector(loginViewControllerDidLoginSuccessfully:)]) {
                [self.delegate loginViewControllerDidLoginSuccessfully:self];
            }
        }
        else {
            if ([self.delegate respondsToSelector:@selector(loginViewController:didFailWithError:)]) {
                [self.delegate loginViewController:self didFailWithError:loginError];
            }
        }
    });
}

- (IBAction)forgotPasswordButtonAction:(id)sender
{
    //  Notify delegate to handle forgotten password request.
    if ([self.delegate respondsToSelector:@selector(loginViewControllerDidReceivePasswordResetRequest:)]) {
        [self.delegate loginViewControllerDidReceivePasswordResetRequest:self];
    }
}

- (IBAction)signupButtonAction:(id)sender
{
    //  Notify delegate to handle signup request.
    if ([self.delegate respondsToSelector:@selector(loginViewControllerDiDReceiveSignupRequest:)]) {
        [self.delegate loginViewControllerDiDReceiveSignupRequest:self];
    }
}

- (BOOL)isAnonymousLoginEnabled
{
    BOOL anonymousLoginAllowed = NO;

    if ([self.delegate respondsToSelector:@selector(loginViewControllerShouldAllowAnonymousLogin:)]) {
        anonymousLoginAllowed = [self.delegate loginViewControllerShouldAllowAnonymousLogin:self];
    }
    return  anonymousLoginAllowed;
}

@end

Главный контроллер представления создает экземпляр и представляет контроллер входа в систему и обрабатывает его сообщения делегатов:

#import "MainViewController.h"
#import "LoginViewController.h"

#define LOGGED_IN NO

@interface MainViewController () <LoginViewControllerDelegate>

@end

@implementation MainViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    //  Fake loading time to show the modal cleanly
    if (!LOGGED_IN) {
        double delayInSeconds = 1.0;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            //  Create a login view controller, assign its delegate, and present it
            LoginViewController *lvc = [[LoginViewController alloc] init];
            lvc.delegate = self;
            [self presentViewController:lvc animated:YES completion:^{
                NSLog(@"modal completion finished.");
            }];
        });
    }
}

#pragma mark - LoginViewControllerDelegate


- (void)loginViewControllerDidLoginSuccessfully:(LoginViewController *)lvc
{
    NSLog(@"Login VC delegate - Login success!");
    [self dismissViewControllerAnimated:YES completion:NULL];
}

- (void)loginViewController:(LoginViewController *)lvc didFailWithError:(NSError *)error
{
    // Maybe show an alert...
    // UIAlertView *alert = ...
}

- (void)loginViewControllerDidReceivePasswordResetRequest:(LoginViewController *)lvc
{
    // Take the user to safari to reset password maybe
     NSLog(@"Login VC delegate - password reset!");
}

- (void)loginViewControllerDiDReceiveSignupRequest:(LoginViewController *)lvc
{
    // Take the user to safari to open signup form maybe
    NSLog(@"Login VC delegate - signup requested!");
}

- (BOOL)loginViewControllerShouldAllowAnonymousLogin:(LoginViewController *)lvc
{
    return YES;
}

@end

Вход в систему может быть сложным, интерактивным процессом в некотором роде, поэтому я рекомендую вам серьезно рассмотреть возможность использования делегирования вместо уведомлений. Однако одна вещь, которая может быть проблематичной, состоит в том, что делегаты - это обязательно только один объект. Если вам нужно, чтобы несколько, разрозненные объекты знали о прогрессе контроллера входа в систему, и вам может понадобиться использовать уведомления. Особенно, если процесс входа в систему может быть очень простым, таким образом, чтобы не требовать какого-либо взаимодействия, кроме передачи односторонних сообщений и данных, уведомления могут стать жизнеспособным вариантом. Вы можете передавать произвольные переменные в уведомлении обратно в свойство userInfo, которое является NSDictionary того, что вы решили использовать в нем. Уведомления могут влиять на производительность, но я понимаю, что это происходит только сейчас, когда число наблюдателей составляет сотни. Тем не менее, это не самый естественный подход в моем сознании, поскольку у вас есть родительский объект (который более или менее контролирует время жизни ребенка), запрашивая у стороннего объекта обновления из дочернего объекта.

Ответ 2

Вы можете получить его с помощью Протокола, это лучший способ.

Я дам вам основную идею о том, как создать протокол

Кроме того, прочитайте этот вопрос: Как создать делегаты в Objective-C?

Следующий код дает вам основную идею протокола, здесь, в нижнем коде, вы можете получить заголовок Button от MasterViewController до DetailViewController.

#DetailViewController.h

#import <UIKit/UIKit.h>

@protocol MasterDelegate <NSObject>
-(void) getButtonTitile:(NSString *)btnTitle;
@end


@interface DetailViewController : MasterViewController

@property (nonatomic, assign) id<MasterDelegate> customDelegate; 

#DetailViewController.m

if([self.customDelegate respondsToSelector:@selector(getButtonTitile:)])
{
          [self.customDelegate getButtonTitile:button.currentTitle];    
}

#MasterViewController.m

create obj of DetailViewController

DetailViewController *obj = [[DetailViewController alloc] init];
obj.customDelegate = self;
[self.navigationController pushViewController:reportTypeVC animated:YES];

and add delegate method in MasterViewController.m for get button title.

#pragma mark -
#pragma mark - Custom Delegate  Method

-(void) getButtonTitile:(NSString *)btnTitle;
{
    NSLog(@"%@", btnTitle);

}