18

私は状態の復元をいじっています。以下のコードでは、UITableViewController のスクロール位置が復元されますが、詳細ビューをタップすると (MyViewController のインスタンスをナビゲーション スタックにプッシュします)、アプリを再起動すると、常に最初のビューに戻ります。ナビゲーション スタックのコントローラー (つまり、MyTableViewController)。誰かが正しいView Controller(つまり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";
}
4

3 に答える 3

18

コードで詳細ビュー コントローラーを作成していて、ストーリーボードからインスタンス化していないため、復元クラスを実装する必要があります。これにより、システム復元プロセスが詳細ビュー コントローラーの作成方法を認識できるようになります。

復元クラスは、実際には、復元パスから特定のビュー コントローラーを作成する方法を知っている単なるファクトリです。このために別のクラスを実際に作成する必要はありません。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];
}

これで、復元サブシステムが詳細ビュー コントローラーを作成してナビゲーション スタックにプッシュできるようになります。(あなたが最初に尋ねたもの。)

例を拡張して状態を処理する:

実際のアプリでは、状態を保存し、復元を処理する必要があります...これをシミュレートするために、次のプロパティ定義を 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";
}

この例を機能させるために、最初に 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 のメソッドです。

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

それでおしまい。

他にもいくつかメモ...

コードが表示されていなくても、App Delegate で次のことを行ったと思いますか?

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

シミュレーターでデモ コードを実行すると、スクロール オフセットが正しく保持されているため、多少の不安定さが見られました。私にとっては時々うまくいくように見えましたが、いつもではありませんでした。これは、コードでインスタンス化されているため、 MyTableViewController の実際の復元でもrestoreClassアプローチを使用する必要があるためだと思われます。しかし、私はまだそれを試していません。

私のテスト方法は、Springboard (アプリの状態を保存するために必要) に戻ってアプリをバックグラウンドに置き、XCode 内からアプリを再起動してクリーン スタートをシミュレートすることでした。

于 2013-01-23T01:35:15.770 に答える
1

コードの唯一の問題は、これらの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

于 2013-06-28T11:20:47.147 に答える
0

ストーリーボードではなくコードでこれを行っているため、詳細ビュー コントローラーだけで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:

于 2013-05-05T21:44:17.250 に答える