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"; }
解决方法
恢复类实际上只是一个工厂,它知道如何从恢复路径创建一个特定的视图控制器.你实际上不必为此创建一个单独的类,我们可以在MyOtherViewController中处理它:
在MyOtherViewController.h中,实现协议:UIViewControllerRestoration
然后在MyOtherViewController.m中设置recoverID时,还要设置还原类:
- (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;
而且我还修改了MyOtherViewContoller的viewDidLoad方法,将Detail标题设置为用户选择的任何记录ID:
- (void)viewDidLoad { [super viewDidLoad]; // self.title = @"Detail"; self.title = self.selectedRecordId; self.view.backgroundColor = [UIColor redColor]; self.view.restorationIdentifier = @"detailView"; }
为了使该示例工作,在最初从MyTableViewController中推送Detail View时,设置selectedRecordId:
- (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中的方法,您的详细信息视图,用于保存Detail控制器的状态.
-(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中设置Detail视图控制器的标题:或者我们可以修改viewControllerWithRestorationIdentifierPath方法来恢复创建类的状态:
+(UIViewController *) viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder { MyOtherViewController *controller = [[self alloc] initWithNibName:nil bundle:nil]; controller.selectedRecordId = [coder decodeObjectForKey:@"selectedRecordId"]; return controller; }
而已.
一些其他注意事项…
我假设你在App代表中做了以下,即使我没有看到代码?
-(BOOL) application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder { return YES; } - (BOOL) application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder { return YES; }
在模拟器中运行演示代码时,我正确地保留了滚动偏移量,我看到有点薄弱.这似乎偶尔为我工作,但并不是所有的时间.我怀疑这可能是因为MyTableViewController的真正恢复也应该使用一个restoreClass方法,因为它是在代码中实例化的.但是我还没有尝试过.
我的测试方法是将应用程序置于后台,返回跳板(需要保存应用程序状态),然后从XCode中重新启动应用程序以模拟干净的开始.