Восстановление состояния iOS с помощью пользовательского контроллера представления контейнера (MMDrawerController)

Настройка

У меня есть настраиваемый контроллер представления контейнера с скользящим меню (MMDrawerController), управляющий контроллером центрального представления и выдвижным левым меню. /выдвижной ящик. Так же, как приложение Facebook и сотни других.

Открытие ящика слева и нажатие пункта меню заменяет контроллер центрального вида.

Что работает

Если я открою приложение, нажму кнопку на первом контроллере центрального вида (который изменит цвет фона), я смогу закрыть приложение и успешно восстановить цвет фона. Идеально.

Что не работает

Если я выберу другой центр vc для загрузки (открыв меню/ящик и выбрав пункт меню), а затем уничтожу приложение, приложение не будет восстановлено на этот контроллер представления.

Что я делаю

В моем родительском контроллере представления я кодирую контроллеры левого и центрального представления, чтобы я мог воссоздать их при восстановлении.

- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
    [coder encodeObject:self.centerViewController forKey:@"centerVC"];
    [coder encodeObject:self.leftDrawerViewController forKey:@"leftDrawerVC"];
    [super encodeRestorableStateWithCoder:coder];
}

- (void)decodeRestorableStateWithCoder:(NSCoder *)coder
{
    // if I don't change the center view controller, these values
    // get logged out as expected
    NSLog(@"leftDrawer: %@", [coder decodeObjectForKey:@"leftDrawerVC"]);
    NSLog(@"center: %@", [coder decodeObjectForKey:@"centerVC"]);
    [super decodeRestorableStateWithCoder:coder];
}

В потоке, где я меняю цвет на первом контроллере центрального представления, во время декодирования я могу успешно вернуть центральный и левый контроллеры представления. Однако в потоке, где я выбираю новый центр vc для загрузки, во время декодирования эти объекты равны нулю.

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

ОБНОВЛЕНИЕ 1

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

ОБНОВЛЕНИЕ 2 Если вы посмотрите на раздел комментариев к этой сути, вы можно видеть, что все правильные вызовы кодирования/декодирования, похоже, происходят при сохранении и восстановлении. Мне интересно, если в моем делегате приложения, когда я изначально настраиваю свой корневой контроллер представления (экземпляр mmdrawercontroller), если я каким-то образом стираю восстановление состояния? Вот что я делаю:

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UINavController *centerNav = [[UINavController alloc] initWithRootViewController:[FeaturedViewController new]];
    centerNav.restorationIdentifier = @"centerNav";
    UINavigationController *leftDrawerNavController = [[UINavigationController alloc] initWithRootViewController:[LeftDrawerViewController new]];
    leftDrawerNavController.restorationIdentifier = @"leftDrawerNav";

    MMDrawerController *drawerViewController = [[MMDrawerController alloc] initWithCenterViewController:centerNav leftDrawerViewController:leftDrawerNavController];

    // no restoration class, since this will always be created before state restoration resumes, and therefore will be found implicitly
    [drawerViewController setRestorationIdentifier:@"mmDrawer"];

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.rootViewController = drawerViewController;
    self.window.restorationIdentifier = NSStringFromClass([UIWindow class]);
    self.window.backgroundColor = [UIColor whiteColor];

    [self.window makeKeyAndVisible];
    return true;
}

person djibouti33    schedule 29.07.2013    source источник


Ответы (1)


Я сделал то же самое с MMDrawerController и считаю, что проблема в том, что вы не предоставляете UIKit способ создания всех возможных контроллеров центрального представления, когда он восстанавливает состояние. Обратите внимание, что при кодировании контроллера сохраняется только идентификатор восстановления для этого контроллера. Для восстановления при следующем запуске приложения UIKit должен иметь возможность получить экземпляр контроллера — он не будет создавать его сам. Он попытается получить указанный экземпляр различными способами, перечисленными здесь:

http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/StatePreservation/StatePreservation.html#//apple_ref/doc/uid/TP40007072-CH11-SW10

Ваше приложение может каждый раз создавать центральный контроллер по умолчанию (либо с помощью раскадровки, либо вручную), чтобы он работал. Для других вам, вероятно, потребуется либо реализовать класс восстановления, либо реализовать application:viewControllerWithRestorationIdentifierPath:coder: в своем делегате и заставить его возвращать новый экземпляр правильного типа.

person Jason Sankey    schedule 01.08.2013
comment
Если вы посмотрите раздел комментариев этого списка (gist.github.com/djibouti33/6124370 ) вы можете видеть, что все правильные вызовы кодирования/декодирования, похоже, происходят с соответствующими контроллерами представления. А именно, мой SearchVC кодируется, и после восстановления ему отправляется +viewControllerWithRestorationIdentiferPath, а также -decodeRestoratbleStateWithCoder. Проблема здесь в том, что когда мой MMDrawerController восстанавливается, ранее закодированные контроллеры представления (левый и центральный) становятся нулевыми. Технически это оба контроллера NavController, но у них есть идентификаторы восстановления, так что со мной все будет в порядке. - person djibouti33; 01.08.2013
comment
Это очень похоже на мое приложение. Иногда при реализации этого я также получал нули при декодировании центрального контроллера, и это было потому, что UIKit не знал, как создать/найти центральный навигационный контроллер. Есть ли в вашем приложении метод предоставления экземпляра контроллера навигации поиска при восстановлении? Я предполагаю, что ваше приложение: viewControllerWithRestorationIdentifierPath:coder: запрашивается и возвращает nil. В моем приложении я всегда создаю оба альтернативных контроллера (навигаторы и их дочерние элементы) в willFinishLaunching, и мой делегат будет предоставлять их по запросу при восстановлении. - person Jason Sankey; 01.08.2013
comment
Насколько я понимаю, будучи встроенным контроллером контейнера, мне не нужно ничего делать с NavController, кроме как установить его идентификатор восстановления, чтобы зарегистрировать его в цепочке восстанавливаемых объектов. Пока его корневой контроллер представления (мой FeatureVC или searchVC) имеет идентификатор и класс восстановления, он должен знать, как перестраиваться. Мой метод application:viewControllerFor... на самом деле пуст, потому что, как я понимаю, я все настроил так, чтобы вещи можно было либо найти неявно, либо воссоздать по мере необходимости. Я добавил обновление выше, показывающее мой appWillFinishLaunching. Что-нибудь бросающееся в глаза? - person djibouti33; 01.08.2013
comment
И вы спросили: есть ли в вашем приложении метод предоставления экземпляра контроллера навигации поиска при восстановлении? Исходному CenterNavController, созданному в appWillFinish, присваивается идентификатор centerNav (и он загружает FeaturedVC. Когда вы открываете ящик и выбираете пункт меню (поиск), я заключаю SearchController в NavController, которому я даю тот же идентификатор восстановления (centerNav) , Мое приложение не создает ничего, связанного с поиском в appWillFinish. Я думал, что мне просто нужно дождаться восстановления, чтобы позаботиться об этом. - person djibouti33; 01.08.2013
comment
Достаточно просто добавить application:viewControllerWithRestorationIdentifierPath:coder: и посмотреть, просит ли UIKit создать навигационный контроллер. Исходя из своего опыта, я верю, что так и будет. Обратите внимание, что вам не нужно восстанавливать его навигационный стек, все это действительно обрабатывается UINavigationController, вы просто предоставляете ненастроенный экземпляр. (Еще одно отличие в моем случае заключается в том, что я не даю двум навигационным контроллерам один и тот же идентификатор, но это потому, что я всегда создаю их оба, и в вашем случае это может подойти.) - person Jason Sankey; 01.08.2013
comment
Если вы не возражаете, не могли бы вы бросить общие части вашего appWillFinish и три метода восстановления состояния делегата приложения в суть? Я думаю, что у меня неправильно настроен делегат приложения. Но, следуя вашему совету, я регистрирую идентификаторPath из app:viewControllerWithRestoration... и получаю три вызова: (mmDrawer), (mmDrawer,leftDrawerNav) и (mmDrawer,centerNav). Итак, вы правы, он запрашивает centerNav, однако, поскольку он уже создан в appWillFinishLaunching, я не думал, что мне нужно создавать его явно. - person djibouti33; 01.08.2013
comment
На самом деле я не использую application:willEncodeRestorableStateWithCoder: (или декодирую). Они не нужны, если иерархия контроллера правильно кодирует себя (что должно быть с вашими изменениями в MMDrawerController). Насколько мне известно, создания навигационного контроллера с правильным идентификатором недостаточно. Вам все равно нужно вернуть его, когда его спросят в viewControllerWithRestoration. Вы пробовали это? (Боюсь, мне больно дезинфицировать суть.) - person Jason Sankey; 01.08.2013
comment
Хорошо, так что я ближе!!!! Вы правы, я должен вернуть и левый, и центральный навигационные контроллеры, чтобы их дети тоже были восстановлены. gist.github.com/djibouti33/6129555. Теперь единственная проблема, которую я вижу, заключается в том, что я получаю несбалансированные вызовы для начала/конца переходов внешнего вида как для левого, так и для центрального навигационного контроллера. Когда мой восстановленный контроллер поиска загружается в центре, и я снова открываю ящик, кажется, что leftDrawerNavController подпрыгивает на место. Но я могу успешно восстановить стек навигации. Невероятный! - person djibouti33; 01.08.2013