Подтвердить что ты не робот

Восстановление состояния UINavigationController (без раскадровки)

Я собирался с государственной реставрацией. В приведенном ниже коде позиция прокрутки UITableViewController восстанавливается, однако, если я должен был перейти к подробному представлению (нажав экземпляр MyViewController в стек навигации), когда приложение перезагрузится, оно всегда возвращается к первому виду контроллер в стеке навигации (т.е. MyTableViewController). Кто-нибудь сможет мне помочь восстановить правильный контроллер (т.е. MyOtherViewController)?

AppDelegate.m

- (BOOL)launchWithOptions:(NSDictionary *)launchOptions
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        // Override point for customization after application launch.


        MyTableViewController *table = [[MyTableViewController alloc] initWithStyle:UITableViewStylePlain];
        table.depth = 0;
        UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:table];
        navCon.restorationIdentifier = @"navigationController";

        self.window.rootViewController = navCon;

        self.window.backgroundColor = [UIColor whiteColor];
        [self.window makeKeyAndVisible];

    });

    return YES;
}

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    return [self launchWithOptions:launchOptions];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    return [self launchWithOptions:launchOptions];
}

MyTableViewController.m

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if(self)
    {
        self.restorationIdentifier = @"master";
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"Master";
    self.tableView.restorationIdentifier = @"masterView";
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 5;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 10;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    return [NSString stringWithFormat:@"Section %d", section];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(!cell)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    cell.textLabel.text = [NSString stringWithFormat:@"%d", indexPath.row];

    return cell;
}

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyOtherViewController *vc = [[MyOtherViewController alloc] initWithNibName:nil bundle:nil];
    [self.navigationController pushViewController:vc animated:YES];
}

MyOtherViewController.m

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        self.restorationIdentifier = @"detail";
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"Detail";
    self.view.backgroundColor = [UIColor redColor];
    self.view.restorationIdentifier = @"detailView";
}
4b9b3361

Ответ 1

Поскольку вы создаете свой контроллер подробных представлений в коде, а не создаете его из раскадровки, вам нужно реализовать класс восстановления, поэтому процесс восстановления системы знает, как создать контроллер подробных представлений.

Класс восстановления - это просто factory, который знает, как создать определенный контроллер просмотра с пути восстановления. На самом деле вам не нужно создавать отдельный класс для этого, мы можем просто обработать его в MyOtherViewController:

в MyOtherViewController.h, выполните протокол: UIViewControllerRestoration

Затем, когда вы устанавливаете идентификатор восстановления в MyOtherViewController.m, также задайте класс восстановления:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        self.restorationIdentifier = @"detail";
        self.restorationClass = [self class]; //SET THE RESTORATION CLASS
    }
    return self;
}

Затем вам необходимо реализовать этот метод в классе восстановления (MyOtherViewController.m)

    +(UIViewController *) viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder
{
   //At a minimum, just create an instance of the correct class. 
    return [[MyOtherViewController alloc] initWithNibName:nil bundle:nil];
}

Это доставляет вам до тех пор, пока подсистема восстановления может создавать и нажимать контроллер Detail View на стек навигационной системы. (Что вы изначально задали.)

Расширение примера для обработки состояния:

В реальном приложении вам нужно будет сохранить состояние и обработать его восстановление... Я добавил следующее определение свойства MyOtherViewController для имитации этого:

@property (nonatomic, strong) NSString *selectedRecordId;

И я также изменил метод MyDotherViewContoller viewDidLoad, чтобы установить заголовок Detail на любой идентификатор записи, выбранный пользователем:

- (void)viewDidLoad
{
    [super viewDidLoad];
  //  self.title = @"Detail";
    self.title = self.selectedRecordId;
    self.view.backgroundColor = [UIColor redColor];
    self.view.restorationIdentifier = @"detailView";
}

Чтобы сделать этот пример работы, я устанавливаю выбранныйRecordId, когда сначала нажимает Detail View из MyTableViewController:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyOtherViewController *vc = [[MyOtherViewController alloc] initWithNibName:nil bundle:nil];
    vc.selectedRecordId = [NSString stringWithFormat:@"Section:%d, Row:%d", indexPath.section,  indexPath.row];
    [self.navigationController pushViewController:vc animated:YES];
}

Другой недостающий элемент - это методы в MyOtherViewController, ваше подробное представление, чтобы сохранить состояние контроллера детали.

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

-(void)decodeRestorableStateWithCoder:(NSCoder *)coder
{
     self.selectedRecordId = [coder decodeObjectForKey:@"selectedRecordId"];
    [super decodeRestorableStateWithCoder:coder];
}

Теперь это на самом деле еще не совсем работает. Это связано с тем, что метод "ViewDidLoad" вызывается при восстановлении до метода decoreRestorablStateWithCoder. Следовательно, заголовок не будет установлен до отображения представления. Чтобы исправить это, мы обрабатываем либо задание заголовка для контроллера вида Detail в viewWillAppear:, либо мы можем изменить метод viewControllerWithRestorationIdentifierPath для восстановления состояния, когда он создает класс следующим образом:

+(UIViewController *) viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder
{
    MyOtherViewController *controller =  [[self alloc] initWithNibName:nil bundle:nil];
    controller.selectedRecordId = [coder decodeObjectForKey:@"selectedRecordId"];
    return controller;

}

Что это.

Несколько других заметок...

Я предполагаю, что вы сделали следующее в своем делете приложения, хотя я не видел код?

-(BOOL) application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder
{
    return YES;
}
- (BOOL) application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder 
{
    return YES;
}

Я видел немного flakiness при запуске демонстрационного кода в симуляторе, при этом он правильно сохранял смещение прокрутки. Казалось, что это время от времени для меня, но не все время. Я подозреваю, что это может быть связано с тем, что на самом деле восстановление MyTableViewController также должно использовать метод restoreClass, поскольку он создан в коде. Однако я еще не пробовал это.

Мой метод тестирования заключался в том, чтобы поместить приложение в фоновый режим, вернувшись на трамплин (требуется для сохранения состояния приложения), а затем перезапустив приложение из XCode, чтобы имитировать чистый старт.

Ответ 2

Единственная проблема с вашим кодом - контроллер навигации эти две строки

UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:table];
    navCon.restorationIdentifier = @"navigationController";

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

вот код

UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" 
    bundle:nil];
UINavigationController* navigationController = [storyboard
    instantiateViewControllerWithIdentifier:
    @"UINavigationController"];

navigationController.viewControllers = @[myController];

он будет работать нормально.

просто следуйте за этим http://petr-pavlik.squarespace.com/blog/2013/6/16/stare-restoration-without-storyboard

Ответ 3

Поскольку вы делаете это в коде, а не через Storyboard, вам нужно будет предоставить не только restorationIdentifier, но также restorationClass для контроллера подробных представлений.

Вы можете оставить его неназначенным, и в этом случае вызов -(UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder вызывается в делетете приложения для создания объекта (контроллер просмотра с restorationIdentifier, но не restorationClass).

Попробуйте следующее (обратите внимание на протокол UIViewControllerRestoration):

@interface MyViewController ()<UIViewControllerRestoration>
@end

@implementation

- (id)init
{
    self = [super init];
    if (self) {
        // Custom initialization
    }

    if( [self respondsToSelector:@selector(restorationIdentifier)] ){
        self.restorationIdentifier = @"DetailViewController";
        self.restorationClass = [self class];
    }
    return self;
}

#pragma mark - State restoration

+ (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder
{
    UIViewController *viewController = [[self alloc] init];

    return viewController;
}

@end

Также обратите внимание, что это очень простой пример, обычно у вас может быть намного больше интересного в -viewControllerWithRestorationIdentifierPath:coder: