2013-01-17 12 views
17

Ho gironzolato con il ripristino dello stato. Nel codice qui sotto, la posizione di scorrimento di UITableViewController viene ripristinata, tuttavia, se dovessi toccare la vista di dettaglio (spingendo un'istanza di MyViewController sullo stack di navigazione), quando l'app si riavvia, torna sempre alla prima vista controller nello stack di navigazione (cioè MyTableViewController). Qualcuno potrebbe aiutarmi a ripristinare il controller di visualizzazione corretto (ad esempio MyOtherViewController)?UINavigationController Stato Restauro (senza Storyboard)

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

ho che penso vi aiuterà, ma io sono sul punto di andare via per il weekend lungo (Australia Day) qui. Se non hai ancora una risposta accettabile quando torno, lo posterò. – CuberChase

+0

Ciao Andy hai risolto il tuo problema? – Daniel

risposta

15

Poiché si sta creando il proprio controller della vista dettagli in codice e non si crea un'istanza da uno Storyboard, è necessario implementare una classe di ripristino, in modo che il processo di ripristino del sistema sappia come creare il controller della vista dettagli.

Una classe di ripristino è in realtà solo una fabbrica che sa come creare un controller di visualizzazione specifico da un percorso di ripristino. In realtà non è necessario creare una classe separata per questo, possiamo solo gestire la cosa in MyOtherViewController:

in MyOtherViewController.h, implementare il protocollo: UIViewControllerRestoration

Poi, quando si imposta la restorationID in MyOtherViewController.m , impostare anche la classe di restauro:

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

È quindi necessario implementare questo metodo nella classe di restauro (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]; 
} 

Ciò consente di ottenere fino a quando il sottosistema di ripristino è in grado di creare e spingere il controller Vista di dettaglio nello Stack di navigazione. (Quello che inizialmente chiesto.)

estendere l'esempio per gestire lo stato:

In una vera e propria applicazione, avresti bisogno di entrambe salvare lo stato e gestire il ripristino di esso ... Ho aggiunto la seguente proprietà definizione MyOtherViewController per simulare questo:

@property (nonatomic, strong) NSString *selectedRecordId; 

E ho anche modificato il metodo di viewDidLoad MyOtherViewContoller per impostare il titolo dettaglio a qualunque numero di registro l'utente ha selezionato:

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    // self.title = @"Detail"; 
    self.title = self.selectedRecordId; 
    self.view.backgroundColor = [UIColor redColor]; 
    self.view.restorationIdentifier = @"detailView"; 
} 

per far funzionare esempio, ho impostato selectedRecordId quando inizialmente spingendo il dettaglio Vista da 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]; 
} 

L'altro pezzo mancante sono i metodi in MyOtherViewController, la vostra vista di dettaglio, per salvare lo stato del regolatore Dettaglio .

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

Ora, questo in realtà non del tutto funziona ancora. Questo perché il metodo "ViewDidLoad" viene richiamato sul ripristino prima che il metodo decoreRestorablStateWithCoder sia. Quindi il titolo non viene impostato prima che venga visualizzata la vista. Per risolvere questo problema, gestiamo sia impostato il titolo per la vista del regolatore Dettaglio in viewWillAppear: o siamo in grado di modificare il metodo viewControllerWithRestorationIdentifierPath per ripristinare lo stato in cui si crea la classe in questo modo:

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

} 

Questo è tutto.

Poche altre note ...

Presumo che hai fatto il seguente nella vostra App delegato, anche se non ho visto il codice?

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

ho visto un po 'di desquamazione quando si esegue il codice demo nel simulatore con correttamente preservare l'offset di scorrimento. Sembrava funzionare occasionalmente per me, ma non sempre. Sospetto che ciò possa essere dovuto al fatto che il ripristino di MyTableViewController dovrebbe anche utilizzare un approccio di ripristinoClass, poiché viene istanziato nel codice. Tuttavia non l'ho ancora provato.

Il mio metodo di prova consisteva nel mettere l'app in background tornando al trampolino (necessario per lo stato dell'app da salvare), quindi riavviare l'app da XCode per simulare un avvio pulito.

+0

Grazie per la tua risposta, ma non è proprio quello che sto cercando. "avresti bisogno di salvare entrambi lo stato e gestirlo ripristinandolo": non voglio gestire questo stato da solo - questo è ciò che la logica di ripristino dello stato di Apple dovrebbe fare (vedi "State Preservation" su http://developer.apple .com/library/ios/# documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html). Inoltre, non è necessario fornire la classe di ripristino, UIKit deve dedurlo (vedere http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/StatePreservation/StatePreservation.html) – Andy

+0

Da il link che hai postato: "Durante la conservazione, la vostra applicazione è responsabile di:. Telling UIKit che supporta la conservazione dello stato Raccontare UIKit che vedono i controller e le viste dovrebbero essere conservati codifica dei dati rilevanti per tutti gli oggetti conservati Durante il restauro.. , la tua app è responsabile di: Dire a UIKit che supporta il ripristino dello stato Fornire (o creare) gli oggetti richiesti da UIKit. Decodificare lo stato degli oggetti conservati e noi per restituire l'oggetto al suo stato precedente. " -Apple può solo dedurre così tanto. –

+0

Dallo stesso link "Ad esempio, un controller di navigazione codifica informazioni sull'ordine dei controller di visualizzazione sul suo stack di navigazione, quindi utilizza queste informazioni in un secondo momento per riportare quei controller di visualizzazione alle loro precedenti posizioni nello stack." – Andy

0

Dal momento che si sta facendo questo in codice e non tramite Storyboard, è necessario fornire non solo un dettaglio View Controller restorationIdentifier ma anche un restorationClass a voi.

Si può lasciare non assegnato, nel qual caso -(UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder viene chiamato il delegato dell'applicazione per creare l'oggetto (visualizza controller con un restorationIdentifier ma non restorationClass).

provare il seguente (si prega di notare il protocollo 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 

Si noti inoltre che questo è un esempio molto semplice, di solito si potrebbe avere le cose molto più interessanti in corso in -viewControllerWithRestorationIdentifierPath:coder:

1

la unico problema con il tuo codice è controller di navigazione queste due linee

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

i don so perché il controller di navigazione non ottiene mai la sua classe di ripristino. Ho avuto lo stesso problema. Ho trovato una soluzione alternativa per creare un controller di navigazione stand alone nello storyboard e usarlo.

qui è il codice

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

navigationController.viewControllers = @[myController]; 

che funzionerà bene.

basta seguire questo qualcosa http://petr-pavlik.squarespace.com/blog/2013/6/16/stare-restoration-without-storyboard