2013-01-17 18 views
17

Byłem bawiąc się z przywrócenie stanu. W poniższym kodzie pozycja przewijania kontrolera UITableViewController zostanie przywrócona, jednak gdy przejdę do widoku szczegółów (przesuwając instancję MyViewController na stos nawigacyjny), po ponownym uruchomieniu aplikacji zawsze powraca do pierwszego widoku kontroler w stosie nawigacji (np. MyTableViewController). Czy ktoś mógłby mi pomóc przywrócić prawidłowy kontroler widoku (np. MyOtherViewController)?UINavigationController State Restoration (bez scenariuszy)

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

Mam coś, że myślę, że będzie pomóc, ale mam zamiar wyjechać na długi (Australia Day) weekendu tutaj. Jeśli nadal nie masz akceptowalnej odpowiedzi, gdy wrócę, opublikuję ją. – CuberChase

+0

Cześć Andy, czy rozwiązałeś swój problem? – Daniel

Odpowiedz

15

Ponieważ tworzysz swój kontroler widoku szczegółów w kodzie i nie tworzysz go w Storyboard, musisz zaimplementować klasę przywracania, więc proces przywracania systemu wie, jak utworzyć kontroler widoku szczegółów.

Klasa przywracania to tak naprawdę tylko fabryka, która wie, jak utworzyć określony kontroler widoku ze ścieżki przywracania. W rzeczywistości nie trzeba tworzyć oddzielną klasę dla tego, możemy po prostu poradzić w MyOtherViewController:

w MyOtherViewController.h, wdrożenia protokołu: UIViewControllerRestoration

Następnie po ustawieniu restorationID w MyOtherViewController.m również ustawić klasę przywracania:

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

następnie należy wdrożyć tę metodę w klasie konserwatorskich (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]; 
} 

Dzięki temu podsystem przywracania jest w stanie utworzyć i wcisnąć kontrolkę widoku szczegółowego na stos nawigacyjny. (Co początkowo poprosił.)

Rozszerzanie przykładzie do obsługi Stan:

W prawdziwej aplikacji, trzeba by zarówno zapisać stan i przywrócenie go obsługiwać ... I dodaje następującą właściwość definicja do MyOtherViewController symulować to:

@property (nonatomic, strong) NSString *selectedRecordId; 

I również zmodyfikowane viewDidLoad metodę MyOtherViewContoller by ustawić tytuł szczegółach cokolwiek rekord wybrany identyfikator użytkownika:

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

Aby przykładową pracę, mogę ustawić selectedRecordId gdy początkowo popychając szczegółowy widok z 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]; 
} 

Drugim brakującym elementem są metody MyOtherViewController, Twój widok szczegółowy, aby zapisać stan kontrolera 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]; 
} 

Teraz to jeszcze nie działa. Dzieje się tak dlatego, że metoda "ViewDidLoad" jest wywoływana przy przywracaniu przed metodą decoreRestorablStateWithCoder. Dlatego tytuł nie zostanie ustawiony przed wyświetleniem widoku. Aby rozwiązać ten problem, załatwiamy albo ustawić tytuł szczegółach kontrolera widoku w viewWillAppear: czy możemy zmodyfikować metodę viewControllerWithRestorationIdentifierPath przywrócić stan, gdy tworzy klasę takiego:

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

} 

to wszystko.

Kilka innych notatek ...

przypuszczam zrobiłeś następujące w App Delegata chociaż nie widzę kodu?

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

Widziałem trochę niestabilności podczas uruchamiania kodu demonstracyjnego w symulatorze z zachowaniem poprawnego przesunięcia Przesunięcie. Wydawało mi się, że działa od czasu do czasu, ale nie cały czas. Podejrzewam, że może tak być, ponieważ prawdziwa renowacja MyTableViewController powinna również wykorzystywać podejście restorationClass, ponieważ jest ono tworzone w kodzie. Jednak jeszcze tego nie próbowałem.

Moja metoda testowania polegała na umieszczeniu aplikacji w tle poprzez powrót do trampoliny (wymagana do zapisania stanu aplikacji), a następnie ponowne uruchomienie aplikacji z poziomu XCode w celu symulacji czystego startu.

+0

Dziękuję za odpowiedź, ale nie jest to dokładnie to, czego szukam. "musisz zapisać stan i poradzić sobie z jego przywróceniem": nie chcę obsługiwać tego stanu osobiście - tak powinna działać logika przywracania stanu Apple (zobacz "Ochrona stanu" na http://developer.apple .com/biblioteka/ios/# documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html). Ponadto, nie musisz podawać klasy przywracania, dlatego UIKit powinien ją wywnioskować (patrz http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/StatePreservation/StatePreservation.html). – Andy

+0

Od link ty pisał: „Podczas konserwacji, aplikacja jest odpowiedzialna za:. powiem UIKit że popiera zachowanie stanu powiem UIKit które oglądać kontrolery i widoki powinny być zachowane Kodowanie odpowiednich danych dla wszelkich zachowanych obiektów Podczas renowacji.. , twoja aplikacja jest odpowiedzialna za: Powiadamianie UIKit, że obsługuje przywracanie stanu Dostarczanie (lub tworzenie) obiektów, które są żądane przez UIKit Dekodowanie stanu twoich zakonserwowanych obiektów i nas w celu przywrócenia obiektu do poprzedniego stanu. " -Apple może tylko tyle wnioskować. –

+0

Z tego samego łącza "Na przykład kontroler nawigacyjny koduje informacje o kolejności kontrolerów widoku na stosie nawigacji, a następnie wykorzystuje te informacje później, aby przywrócić kontrolery widoku do swoich poprzednich pozycji na stosie." – Andy

0

Ponieważ robisz to w kodzie, a nie za pomocą Storyboard, musisz dostarczyć nie tylko restorationIdentifier, ale także restorationClass do kontrolera widoku szczegółów.

Możesz zostawić to ustawienie nieprzypisane, w takim przypadku -(UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder zostanie wywołane na delegacie aplikacji, aby utworzyć obiekt (zobacz kontroler z restorationIdentifier, ale nie restorationClass).

Spróbuj wykonać następujące czynności (proszę zwrócić uwagę na protokół 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 

Należy również pamiętać, że jest to bardzo prosty przykład, zwykle może mieć o wiele więcej ciekawych rzeczy dzieje się w -viewControllerWithRestorationIdentifierPath:coder:

1

na jedyny problem z kodem jest kontroler nawigacyjny te dwie linie

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

i don wiesz dlaczego kontroler nawigacyjny nigdy nie uzyskał klasy przywracania. Miałem ten sam problem. znalazłem alternatywne rozwiązanie, aby stworzyć kontroler nawigacyjny samodzielnie w storybooku i użyć go.

tutaj jest kod

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

navigationController.viewControllers = @[myController]; 

będzie działać prawidłowo.

tylko obserwować tą http://petr-pavlik.squarespace.com/blog/2013/6/16/stare-restoration-without-storyboard