2013-08-07 10 views
8

Próbuję zaimplementować przywracanie stanu w aplikacji, która używa systemu iOS 6+ i scenorysów, ale mam problemy ze znalezieniem sposobu, aby zapobiec duplikowaniu wywołań na ciężkich metodach.UIViewController wywołania cyklu życia w połączeniu z przywracaniem stanu

Gdybym po prostu uruchomić aplikację, a potem muszę konfiguracji interfejsu użytkownika w viewDidLoad:

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    [self setupUI]; 
} 

Działa to dobrze w normalnym, non-state-konserwatorskich świata. Teraz Dodałem przywrócenie stanu i po przywróceniu pewne właściwości muszę zaktualizować UI z tych właściwości:

- (void)decodeRestorableStateWithCoder:(NSCoder *)coder { 
    [super decodeRestorableStateWithCoder:coder]; 
    // restore properties and stuff 
    // [...] 
    [self setupUI]; 
} 

Więc co się dzieje teraz jest to, że pierwsza metoda setupUI jest wywoływana z viewDidLoad, a następnie ponownie od decodeRestorableStateWithCoder:. Nie widzę metody, którą mogę przesłonić, która zawsze jest nazywana ostatnią.

Jest to normalny porządek metoda nazywa:

  • awakeFromNib
  • viewDidLoad
  • viewWillAppear
  • viewDidAppear

Podczas korzystania przywrócenie stanu, nazywa się to:

  • awakeFromNib
  • viewDidLoad
  • decodeRestorableStateWithCoder
  • viewWillAppear
  • viewDidAppear

nie mogę umieścić wezwanie do setupUI w viewWillAppear bo wtedy byłoby również wykonywane za każdym razem, gdy z powrotem do ojczystego widok.

Byłoby znacznie wygodniej, gdyby decodeRestorableStateWithCoder było nazywane PRZED viewDidLoad, ponieważ wtedy mógłbyś użyć przywróconych właściwości. Niestety, nie w tym przypadku, więc ... jak mogę zapobiec wykonywaniu pracy w viewDidLoad, gdy wiem, że muszę to zrobić od razu w decodeRestorableStateWithCoder zaraz po?

+2

Ustawiłem wartość boolean na NIE w viewDidLoad, a jeśli NIE, to zrób rzeczy w viewWillAppear. Zakładam, że nie chcesz tego robić w ten sposób? –

+0

Może rzeczywiście być najbardziej pragmatyczne rozwiązanie, tak. Po prostu wydaje się "nie tak" :) –

+0

@NitinAlabur nie ma powodu, aby ustawiać wartość 'NO' w' viewDidLoad' z powodu domyślnej wartości 'BOOL'. – k06a

Odpowiedz

3
@property (nonatomic) BOOL firstLoad; 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    self.firstLoad = YES; 
} 

- (void)viewWillAppear:(BOOL)animated { 
    [super viewWillAppear:animated]; 

    if (self.firstLoad) { 
     [self setupUI]; 
     self.firstLoad = NO; 
    } 
} 

Dzięki @calvinBhai za sugestię.

+3

Lepiej używać wartości odwrotnej, np. 'Self.firstLoaded', jej domyślną wartością jest' NO' i nie masz powodu, aby ustawić ją w 'viewDidLoad'. – k06a

6

Jeśli programujesz przywracanie stanu programowo (tzn. Nie używając scenorysów), możesz użyć + viewControllerWithRestorationIdentifierPath:coder:, zainicjować kontroler widoku i użyć cokolwiek, czego potrzebujesz od kodera, aby dokonać inicjacji pre-viewDidLoad.

+ (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder 
{ 
    if ([[identifierComponents lastObject] isEqualToString:kViewControllerRestorationIdentifier]) { 
     if ([coder containsValueForKey:kIDToRestore]) { 
      // Can only restore if we have an ID, otherwise return nil. 
      int savedId = [coder decodeIntegerForKey:kIDToRestore]; 
      ViewController *vc = [[ViewController alloc] init]; 
      [vc setThingId:savedId]; 
      return vc; 
     } 
    } 

    return nil; 
} 

Odkryłam, że starają się realizować przywrócenie stanu wykazał się złych praktyk programowania w kodzie, jak pakowanie zbytnio do viewDidLoad. Więc podczas gdy to działa (jeśli nie używasz storyboardów), inną opcją jest odświeżenie sposobu konfigurowania kontrolerów widoku. Zamiast używać flagi, przenieś fragmenty kodu do własnych metod i wywołaj te metody z obu miejsc.

+0

To powinna być zaakceptowana odpowiedź. Początkowo nie było to dla mnie oczywiste, ale masz dostęp do tego samego kodera w 'viewControllerWith ...' i 'decodeRestorableState ...'. Dlatego możesz ustawić wszystkie wymagane dane dla viewDidLoad w 'viewControllerWith ...'. – arsenius

-1

Jedna korekta przepływu MixedCase (co było bardzo pomocne, dziękuję), rzeczywisty przepływ Połączenie jest nieco inna:

Jest to normalny porządek metoda nazywa:

awakeFromNib

viewDidLoad

viewWillAppear

viewDidAppear

Podczas korzystania przywrócenie stanu, nazywa się to:

viewControllerWithRestorationIdentifierPath (dekodować wszystkie dane, które są potrzebne do regularnego uruchamiania)

awakeFromNib

viewDidLoad

viewWillAppear

viewDidAppear

decodeRestorableStateWithCoder (dekodowanie remontu dane państwowe i ustawić kontroler UI)

+1

To nie jest poprawne. Prawidłowa kolejność przywracania stanu to: appWillLaunch -> awakeFromNib -> viewDidLoad -> decodeRestorableState -> appDidLaunch -> viewWillAppear' –

5

Funny wystarczająco sekwencja dekodowanie jest jeszcze inna i dokładnie:

+viewControllerWithRestorationIdentifierPath:coder: 
awakeFromNib 
viewDidLoad 
decodeRestorableStateWithCoder: 
viewWillAppear 
viewDidAppear 

i to całkowicie ma sens tak.

0

Tak, rzeczywiście byłoby ładniej, gdyby -decodeRestorableStateWithCoder: zostało wywołane przed -viewDidLoad. Westchnienie.

przeniosłem mój kod konfiguracji widoku (która zależy od stanu przywracającej) do -viewWillAppear: i używane dispatch_once(), zamiast zmiennej logicznej:

private var setupOnce: dispatch_once_t = 0 

override func viewWillAppear(animated: Bool) { 
    dispatch_once(&setupOnce) { 
     // UI setup code moved to here 
    } 

    : 
} 

Dokumentacja stwierdza, że ​​„poglądy nie są już oczyszczone przy słabym pamięci warunki ", więc dispatch_once powinien być poprawny przez cały czas działania kontrolera widoku.

+0

Problem z dispatch_once w tym przypadku polega na tym, że treść bloku dispatch_once będzie wywoływana tylko raz podczas całej inwokacji aplikacji - w ten sposób, gdy kontroler UIViewController zostanie poprawnie zwolniony, na przykład przeniesiony ze stosu UINavigationController, a następnie ponownie zepchnięty na stos, kod konfiguracji nie uruchomi się po raz drugi i widok nie zostanie skonfigurowany. Musisz być BARDZO ostrożny z tym wzorcem i zawierać tylko kod w bloku dispatch_once, który nie musi być uruchamiany, jeśli widok został zdeinitowany i przywrócony. – Troy

0

Dodawanie do odpowiedzi Berbie, w

Rzeczywisty przepływ jest:

initWithCoder 
+viewControllerWithRestorationIdentifierPath:coder: 
awakeFromNib 
viewDidLoad 
decodeRestorableStateWithCoder: 
viewWillAppear 
viewDidAppear 

Należy pamiętać, że wewnątrz initWithCoder, trzeba ustawić self.restorationClass = [self class]; Zmusi to viewControllerWithRestorationIdentifierPath:coder: nazywać.

2

Z książki „Programowanie iOS 9: zanurkować głęboko Wyświetleń Zobacz, kontrolerów i ram” strony 386-387

Znany kolejność wydarzeń podczas odbudowy państwa jest tak:

  1. application:shouldRestoreApplicationState:
  2. application:viewControllerWithRestorationIdentifierPath:coder:
  3. viewControllerWithRestorationIdentifierPath:coder:, aby środkiem łańcucha
  4. viewDidLoad, w ORD er dół łańcucha; ewentualnie przeplatane z powyższym
  5. decodeRestorableStateWithCoder:, aby w dół łańcucha
  6. application:didDecodeRestorableStateWithCoder:
  7. applicationFinishedRestoringState, aby w dół łańcucha

Nadal nie wiem, kiedy viewWillAppear: i viewDidAppear: przybędzie, czy viewDidAppear: przybędą w ogóle. Ale w applicationFinishedRestoringState można niezawodnie zakończyć konfigurowanie kontrolera widoku i interfejsu.

Powiązane problemy