2013-01-17 93 views
17

我一直在玩弄状态恢复。在下面的代码中,UITableViewController的滚动位置得到恢复,但是,如果我点击进入详细视图(将MyViewController的实例推入导航堆栈),当应用程序重新启动时,它总是返回到第一个视图控制器在导航堆栈中(即MyTableViewController)。有人能够帮助我恢复到正确的视图控制器(即MyOtherViewController)吗?UINavigationController状态恢复(没有故事板)

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"; 
} 
+0

我已经得到的东西,我认为会帮助你,但我即将离开的长(澳大利亚国庆日)周末在这里。如果我回来时还没有可以接受的答案,我会发布它。 – CuberChase

+0

嗨安迪你解决了你的问题? – Daniel

回答

15

由于您正在代码中创建详细视图控制器,而不是通过故事板实例化您需要实现的恢复类,所以系统还原过程知道如何创建详细视图控制器。

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

在MyOtherViewController.h

,实现协议:UIViewControllerRestoration

然后,当你在MyOtherViewController.m设置restorationID ,还设置了恢复类:

- (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]; 
} 

现在,这其实并不相当,但工作。这是因为在decoreRestorablStateWithCoder方法之前,将在还原时调用“ViewDidLoad”方法。因此标题在显示视图之前不会被设置。为了解决这个问题,我们处理任何设置标题在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的恢复还应该使用restoreClass方法,因为它是在代码中实例化的。但是我还没有尝试过。

我的测试方法是通过返回到跳板(应用程序状态被保存所需)来将应用程序置于后台,然后从XCode内重新启动应用程序以模拟干净的启动。

+0

感谢您的回复,但这不是我正在寻找的。 “你需要同时保存状态和恢复状态”:我不想自己处理这个状态 - 这就是苹果的状态恢复逻辑应该做的事情(参见http://developer.apple上的“State Preservation”)。 .com/library/ios/#documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html)。另外,您不需要提供修复类,UIKit应该推断它(请参阅http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/StatePreservation/StatePreservation.html) – Andy

+0

链接您发布: “在保存,您的应用程序负责: 告诉UIKit中,它支持状态保存 告诉UIKit的哪个视图控制器和视图应该保留 编码相关的资料作任何保留的对象 在恢复过程中。 ,您的应用程序负责: 告知UIKit它支持状态恢复 提供(或创建)UIKit请求的对象 解码您保存的对象和我们的状态使它返回到以前的状态。“ - 苹果只能推断这么多。 –

+0

来自同一链接“例如,导航控制器会对其导航堆栈中的视图控制器的顺序信息进行编码,然后稍后使用这些信息将这些视图控制器返回到它们在堆栈上的先前位置。” – Andy

0

由于您是在代码中执行此操作而不是通过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:

1

的事只有你的代码的问题是导航控制器 这两条线

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