2013-01-17 14 views
17

Ich habe mit staatlicher Restaurierung herumgespielt. Im folgenden Code wird die Bildlaufposition des UITableViewControllers wiederhergestellt. Wenn ich jedoch in die Detailansicht (durch Drücken einer Instanz von MyViewController auf den Navigationsstapel) tippen würde, kehrt die App beim Neustart der App immer zur ersten Ansicht zurück Controller im Navigations-Stack (zB MyTableViewController). Wäre jemand in der Lage, mir zu helfen, den richtigen Ansichtscontroller (d. H. MyOtherViewController) wiederherzustellen?UINavigationController State Restoration (ohne Storyboards)

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

Ich habe etwas, das ich denke, wird Ihnen helfen, aber ich bin über die lange (Australia Day) Wochenende gehen weg hier. Wenn du immer noch keine akzeptable Antwort hast, wenn ich zurück bin, werde ich es posten. – CuberChase

+0

Hallo Andy hast du dein Problem gelöst? – Daniel

Antwort

15

Da Sie Ihren Detailansicht-Controller in Code erstellen und nicht aus einem Storyboard instanziieren, müssen Sie eine Wiederherstellungsklasse implementieren, damit der Systemwiederherstellungsprozess weiß, wie der Detailansicht-Controller erstellt wird.

Eine Wiederherstellungsklasse ist eigentlich nur eine Fabrik, die weiß, wie man einen bestimmten View-Controller aus einem Wiederherstellungspfad erstellt. Sie nicht wirklich haben eine eigene Klasse für diese zu schaffen, können wir damit umgehen nur in MyOtherViewController:

in MyOtherViewController.h, implementieren das Protokoll: UIViewControllerRestoration

Wenn Sie dann den restorationID in MyOtherViewController.m gesetzt , stellen auch die Restauration Klasse:

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

Sie dann diese Methode in der Restauration Klasse (MyOtherViewController.m) benötigen

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

Das bringt Sie soweit, dass das Wiederherstellungssubsystem den Detailansicht-Controller erstellen und auf den Navigationsstapel schieben kann. (Was Sie zunächst gefragt.)

Erweiterung des Beispiels Zustand zu handhaben:

In einer realen Anwendung, die Sie sowohl den Zustand speichern bräuchten und handhaben es die Wiederherstellung ... Ich fügte hinzu, die folgende Eigenschaft Definition MyOtherViewController dies zu simulieren:

@property (nonatomic, strong) NSString *selectedRecordId; 

Und ich auch MyOtherViewContoller des viewDidLoad Methode, um die Details Titel, was auch immer Datensatz-ID des Benutzer ausgewählt geändert:

Um das Beispiel funktioniert, ich selectedRecordId gesetzt, wenn zunächst die Detailansicht von MyTableViewController schieben:

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

Das andere fehlende Stück sind die Methoden in MyOtherViewController, Ihre Detailansicht, die den Zustand der Details Controller zu speichern .

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

Jetzt funktioniert das eigentlich noch nicht ganz. Dies liegt daran, dass die "ViewDidLoad" -Methode bei der Wiederherstellung aufgerufen wird, bevor die decoreRestorablStateWithCoder-Methode verwendet wird. Daher wird der Titel nicht festgelegt, bevor die Ansicht angezeigt wird. Um dies zu beheben, wir behandeln entweder den Titel für die Detailansicht Controller in viewWillAppear eingestellt: oder wir können die viewControllerWithRestorationIdentifierPath Verfahren ändern, um den Zustand wiederherzustellen, wenn sie die Klasse wie folgt erstellt:

+(UIViewController *) viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder 
{ 
    MyOtherViewController *controller = [[self alloc] initWithNibName:nil bundle:nil]; 
    controller.selectedRecordId = [coder decodeObjectForKey:@"selectedRecordId"]; 
    return controller; 

} 

Das ist es.

Einige weitere Hinweise ...

i nehme an, Sie in AppDelegate folgendes tat, obwohl ich den Code nicht sehen?

Ich sah ein wenig Schalkhaftigkeit beim Ausführen des Demo-Codes im Simulator, wobei der Scroll-Offset korrekt beibehalten wurde. Es schien gelegentlich für mich zu funktionieren, aber nicht die ganze Zeit. Ich vermute, dass dies möglicherweise daran liegt, dass die Wiederherstellung von MyTableViewController auch einen RestorationClass-Ansatz verwendet, da dieser im Code instanziiert wird. Ich habe das noch nicht ausprobiert.

Meine Testmethode war, die App in den Hintergrund zu stellen, indem ich zum Sprungbrett zurückkehrte (erforderlich für den zu speichernden App-Status). Anschließend startete ich die App von XCode neu, um einen sauberen Start zu simulieren.

+0

Danke für Ihre Antwort, aber es ist nicht ganz das, wonach ich suche. "Sie müssten sowohl den Zustand speichern als auch die Wiederherstellung übernehmen": Ich möchte diesen Zustand nicht selbst behandeln - das sollte Apples Zustandswiederherstellungslogik tun (siehe "State Preservation" auf http://developer.apple) .com/library/ios/# documentation/uikit/Referenz/UINavigationController_Class/Reference/Reference.html). Außerdem müssen Sie die Wiederherstellungsklasse nicht bereitstellen, UIKit sollte darauf schließen (siehe http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/StatePreservation/StatePreservation.html) – Andy

+0

Von der Link gepostet: „Während Erhaltung, Ihre Anwendung ist verantwortlich für:. Telling UIKit, dass es Staatserhaltung unterstützt UIKit angegeben, die Controller und Ansichten anzeigen sollten beibehalten werden für alle erhaltenen Objekte relevante Daten Encoding Bei der Restaurierung.. Ihre App ist verantwortlich für: UIKit mitteilen, dass es die Wiederherstellung des Status unterstützt Bereitstellung (oder Erstellung) der Objekte, die von UIKit angefordert werden Decodierung des Status Ihrer aufbewahrten Objekte und uns Damit wird das Objekt in seinen vorherigen Zustand zurückversetzt. " -Apple kann nur so viel ableiten. –

+0

Von demselben Link "Ein Navigationscontroller codiert z. B. Informationen zur Reihenfolge der Ansichtscontroller auf seinem Navigationsstapel. Diese Informationen werden dann später verwendet, um diese Ansichtscontroller an ihre vorherigen Positionen im Stapel zurückzusetzen." – Andy

0

Da Sie dies in Code und nicht über Storyboard tun, müssen Sie nicht nur eine restorationIdentifier, sondern auch eine restorationClass zu Ihrem Detailansicht Controller bereitstellen.

Sie könnten es in diesem Fall -(UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder unassigned verlassen wird über die Anwendung delegieren genannt das Objekt (View-Controller mit einem restorationIdentifier aber keine restorationClass) zu machen.

Versuchen Sie Folgendes (bitte beachten Sie das UIViewControllerRestoration-Protokoll):

@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 

Beachten Sie auch, dass dies ein sehr einfaches Beispiel ist, in der Regel können Sie eine Menge interessante Dinge passieren in -viewControllerWithRestorationIdentifierPath:coder:

1

der nur Problem mit Ihrem Code ist Navigation-Controller diese beiden Linien

UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:table]; 
    navCon.restorationIdentifier = @"navigationController"; 

i don Ich weiß, warum der Navigationscontroller niemals seine Wiederherstellungsklasse erhält. Ich hatte das gleiche Problem. Ich habe eine alternative Lösung gefunden, um einen Navigationscontroller alleine im Storyboard zu erstellen und zu verwenden.

hier Code

UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" 
    bundle:nil]; 
UINavigationController* navigationController = [storyboard 
    instantiateViewControllerWithIdentifier: 
    @"UINavigationController"]; 

navigationController.viewControllers = @[myController]; 

es gut funktionieren wird.

folgen Sie einfach diesem http://petr-pavlik.squarespace.com/blog/2013/6/16/stare-restoration-without-storyboard

Verwandte Themen