[英]UINavigationController State Restoration (without Storyboards)
我一直在四處尋找狀態恢復。 在下面的代碼中,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";
}
因為您要在代碼中創建詳細視圖控制器,而不是從Storyboard實例化它,所以您需要實現一個恢復類,因此系統恢復過程知道如何創建詳細視圖控制器。
恢復類實際上只是一個知道如何從恢復路徑創建特定視圖控制器的工廠。 你實際上不必為此創建一個單獨的類,我們可以在MyOtherViewController中處理它:
在MyOtherViewController.h中,實現協議:UIViewControllerRestoration
然后在MyOtherViewController.m中設置restoreID時,還要設置恢復類:
- (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推送詳細信息視圖時設置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 Delegate中執行了以下操作?
-(BOOL) application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder
{
return YES;
}
- (BOOL) application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder
{
return YES;
}
我在模擬器中運行演示代碼時看到了一些瑕疵,它正確地保留了Scroll偏移。 它似乎偶爾為我工作,但不是所有的時間。 我懷疑這可能是因為MyTableViewController的真正恢復也應該使用recoverClass方法,因為它在代碼中實例化。 但是我還沒試過。
我的測試方法是將應用程序放在后台,返回跳板(需要保存應用程序狀態。),然后從XCode中重新啟動應用程序以模擬干凈的啟動。
您的代碼唯一的問題是導航控制器這兩行
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
既然你這樣做是在代碼,而不是通過故事板,您需要提供的不僅僅是一個restorationIdentifier
也是一個restorationClass
給你詳細視圖控制器。
您可以將其保留為未分配的情況-(UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder
在應用程序委托-(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:
可能會有更多有趣的事情-viewControllerWithRestorationIdentifierPath:coder:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.