UINavigationController的状态恢复(不包括故事板)(UINavigationCo

2019-08-07 04:37发布

我一直在玩弄周围的状态恢复。 在下面的代码,所述的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";
}

Answer 1:

因为你在代码中创建您的详细视图控制器,而不是从一个故事板实例,你需要实现一个恢复类,所以系统恢复过程中知道如何创建详细信息视图控制器。

恢复类是真的只是一个工厂,知道如何创建从恢复路径特定视图控制器。 你实际上并没有为此创造一个单独的类,我们就可以处理它在MyOtherViewController:

在MyOtherViewController.h,实现协议:UIViewControllerRestoration

然后,当你设置restorationID在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];
}

这让你尽可能的恢复子系统能够创建和推详细视图控制器到导航堆栈。 (什么你最初要求。)

延伸的实施例来处理状态:

在实际的应用程序,你需要既保存状态和处理恢复它...我添加了以下属性定义MyOtherViewController模拟这个:

@property (nonatomic, strong) NSString *selectedRecordId;

而且我还修改MyOtherViewContoller的viewDidLoad方法设置详细信息标题ID的用户选择的任何记录:

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

为了让这个例子的工作,我设置selectedRecordId在最初推动从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方法。 因此在显示视图之前标题不被设置。 为了解决这个问题,我们处理任何设置标题在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;
}

运行在它正确保存滚动偏移模拟器演示代码时,我看到了一个有点片状的。 这似乎是偶尔为我工作,但不是所有的时间。 我怀疑这可能是因为真的MyTableViewController的恢复也应该使用restorationClass方法,因为它在代码中被实例。 不过,我并没有说出来呢。

我的测试方法,是通过返回到跳板(需要要保存的应用程序状态),然后在Xcode中重新推出的应用程序来模拟一个干净的开始把应用程序在后台运行。



Answer 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



Answer 3:

既然你这样做是在代码,而不是通过故事板,您需要提供的不仅仅是一个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:



文章来源: UINavigationController State Restoration (without Storyboards)