Сохранение состояния для контроллеров представления с пользовательскими делегатами или источниками данных

Я пытаюсь использовать iOS 6+ (мое приложение 7.0+), сохранение состояния, чтобы сохранить представление, которое представлено модально из другого View Controller. Как таковой, он имеет типичный образец отклонения контроллера модального представления:

TNTLoginViewController.h содержит

@protocol TNTLoginViewControllerDelegate <NSObject>

- (void)TNTLoginViewControllerDismiss:(TNTLoginViewController *)controller;

@end

@interface TNTLoginViewControllerDelegate : NSObject

@interface TNTLoginViewController : UIViewController

@property (weak, nonatomic) IBOutlet id <TNTLoginViewControllerDelegate> delegate;

- (IBAction)getStarted:(id)sender;

@end

getStarted: реализация

- (IBAction)getStarted:(id)sender
{
    // Perform login
    ...

    // Dismiss me
    [self.delegate TNTLoginViewControllerDismiss:self];
}

TNTLoginViewControllerDismiss: метод на делегате, который представил модальное

- (void)TNTLoginViewControllerDismiss:(TNTLoginViewController *)controller
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

И все это работает как шарм! До сохранения государства. Проще говоря, я не знаю, как TNTLoginViewController сохранит свой делегат. Я понимаю, почему это не может: это просто указатель! Поэтому я попробовал различные способы получения делегата:

  1. Восстановление класса: к сожалению, как метод класса, viewControllerWithRestorationIdentifierPath:coder: не помогает мне указать на мое конкретное представление View Controller.
  2. Установите мое представление VC в качестве делегата моего модального VC в Storyboard: Xcode не позволил бы мне нарисовать это соединение, даже когда мой класс VC публично принял TNTLogingViewControllerDelegate> Протокол в его заголовке. Это может быть отдельной проблемой, или это может быть запрещено.
  3. Используйте уровень приложения-делегата application:viewControllerWithRestorationIdentifierPath:coder: вернуть модальный контроллер представления с его делегатом, установленным для моего представления View Controller. Я должен быть в состоянии получить это представление VC от делегата приложения, но это может сработать.

Сейчас я иду с № 3, но если есть лучшее решение, которое кто-то может порекомендовать, я был бы в восторге.

Установки, которые привели бы к подобным проблемам:

  1. Настройка источника данных, скажем, для табличного представления.

1 ответ

Решение

Вы правы, это можно сделать на уровне приложения-делегата с application:viewControllerWithRestorationIdentifierPath:coder:, но вы должны быть осторожны / умны в том, как вы это делаете!

Цель здесь - вернуть TNTLoginViewController во время процесса восстановления состояния с его делегатом, установленным для его родителя.

Сначала вы должны создать объект TNTLoginViewController. Вы упомянули раскадровку, поэтому я буду загружать ее оттуда. Я предполагаю, что у вас есть довольно стандартная установка с файлом Main.storyboard, и личность правильно установлена ​​в Identity Inspector.

TNTLoginViewController * loginViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"loginViewController"];

Далее вам нужно установить его делегат для родителя. Я собираюсь предположить, что есть UINavigationController, соединяющий эту модель. Чтобы найти это в объекте application-делегата, вам нужно покопаться в его свойстве window.

Свойство окна является объектом UIWindow, у которого есть другое свойство, называемое rootViewController. Это объект UIViewController. Так как я предполагаю, что есть UINavigationController, соединяющий вашу модель, вам нужно будет вписать этот UIViewController в UINavigationViewController (я бы разместил ссылку, которую я не могу, на моем текущем уровне репутации).

Теперь вы можете использовать свойство topViewController контроллера в верхней части стека навигации, которое вы хотите установить в качестве своего делегата! Если нет, то вы можете перемещаться по объекту UINavigationController, для которого вы хотите использовать его в качестве делегата.

И помните, поскольку вы устанавливаете делегата с уровня приложения-делегата, вам может потребоваться указать свой протокол здесь, чтобы избежать неопределенности.

Чтобы реализовать эти последние четыре шага в коде будет выглядеть примерно так.

loginViewController.delegate = (id <TNTLoginViewControllerDelegate>)((UINavigationController *) self.window.rootViewController).topViewController;

И тогда вы можете вернуть свой TNTLoginViewController с правильно установленным делегатом!

Убедитесь, что не забыли последствия использования application:viewControllerWithRestorationIdentifierPath:coder:, Вы хотите сделать это только в случае восстановления вашего TNTLoginViewController. К счастью, вы можете проверить это с помощью переданного аргумента identifierComponents. Сравните это с вашим именем в Identity Inspector и верните nil, если они не совпадают.

Ваш последний метод в файле AppDelegate.m будет выглядеть примерно так.

- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder
{
if ([[identifierComponents lastObject] isEqualToString:@"loginViewController"]) {
    TNTLoginViewController * loginViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"loginViewController"];

    loginViewController.delegate = (id <TNTLoginViewControllerDelegate>)((UINavigationController *) self.window.rootViewController).topViewController;

    return loginViewController;
}

return nil;
}

Надеюсь, это поможет!

Другие вопросы по тегам