2013-08-07 3 views
8

Ich versuche die Statuswiederherstellung in einer App zu implementieren, die iOS 6+ und Storyboards verwendet, aber ich habe Probleme, einen Weg zu finden, doppelte Aufrufe schwerer Methoden zu verhindern.UIViewController Lebenszyklusaufrufe in Kombination mit Statuswiederherstellung

Wenn ich einfach die App starten, dann muss ich Setup die Benutzeroberfläche in viewDidLoad:

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

Dies funktioniert gut in einem normalen, nicht-staatlich Restaurierung Welt. Jetzt habe ich Zustand Restaurierung gegeben, und nach ein paar Eigenschaften der Wiederherstellung Ich brauche die Benutzeroberfläche mit diesen Eigenschaften zu aktualisieren:

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

So was passiert nun, dass zuerst die setupUI Methode von viewDidLoad genannt wird, und dann wieder von decodeRestorableStateWithCoder:. Ich sehe keine Methode, die ich überschreiben kann, die immer zuletzt genannt wird.

Dies ist die normale Reihenfolge der Methode aufruft:

  • awakeFromNib
  • viewDidLoad
  • viewWillAppear
  • viewDidAppear

Wenn Zustandswiederherstellung verwendet wird, dies wird als:

  • awakeFromNib
  • viewDidLoad
  • decodeRestorableStateWithCoder
  • viewWillAppear
  • viewDidAppear

ich den Anruf nicht setupUI in viewWillAppear platzieren können, denn dann wäre es auch jedes Mal, wenn nativer wieder ausgeführt werden, um eine Sicht.

Es wäre viel handlicher, wenn decodeRestorableStateWithCoder BEFORE viewDidLoad genannt wurde, denn dann könnten Sie wiederhergestellte Eigenschaften verwenden. Leider ist das nicht der Fall, also ... wie kann ich verhindern, die Arbeit in viewDidLoad zu tun, wenn ich weiß, dass ich es wieder in decodeRestorableStateWithCoder direkt danach machen muss?

+2

Ich würde einen Boolean auf NO in ViewDidLoad setzen, und wenn NEIN, dann Sachen in ViewWillAppear tun. Ich nehme an, du willst es nicht so machen? –

+0

Könnte eigentlich die pragmatischste Lösung sein, ja. Es fühlt sich einfach "falsch" an :) –

+0

@NitinAlabur Es gibt keinen Grund, den Wert für 'NO' in' viewDidLoad' zu setzen, da der 'BOOL'-Wert voreingestellt ist. – k06a

Antwort

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

Dank @calvinBhai für den Vorschlag.

+3

Besser, inversen Wert zu verwenden, wie 'self.firstLoaded', der Standardwert ist' NO' und Sie haben keinen Grund, es in 'viewDidLoad' zu setzen. – k06a

6

Wenn Sie die Statuswiederherstellung programmatisch durchführen (d. H. Keine Storyboards verwenden), können Sie + viewControllerWithRestorationIdentifierPath:coder: verwenden, den View-Controller dort initalisieren und alles verwenden, was Sie vom Coder benötigen, um Ihre Pre-ViewDidLoad-Initialisierung durchzuführen. angezeigt hat schlechte Programmierpraktiken in meinem Code, wie die Verpackung zu viel in 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; 
} 

Ich habe festgestellt, dass staatliche Restauration umzusetzen versuchen. Auch wenn dies funktioniert (wenn Sie keine Storyboards verwenden), besteht die andere Möglichkeit darin, die Einrichtung der View-Controller neu zu gestalten. Anstatt ein Flag zu verwenden, verschieben Sie Codeteile in ihre eigenen Methoden und rufen Sie diese Methoden von beiden Orten auf.

+0

Dies sollte die akzeptierte Antwort sein. Es war für mich zunächst nicht offensichtlich, aber Sie haben Zugriff auf den gleichen Coder in 'viewControllerWith ...' und 'decodeRestorableState ...'. Daher können Sie alle erforderlichen Daten für viewDidLoad in 'viewControllerWith ...' einrichten. – arsenius

-1

Eine Korrektur Mixedcase-Flow (was sehr hilfreich war, danke), die tatsächliche Rufablauf ist ein bisschen anders:

Dies ist die normale Reihenfolge der Methode ruft:

awakeFromNib

viewDidLoad

viewWillAppear

viewDidAppear

Wenn Zustandswiederherstellung verwendet wird, heißt dies:

viewControllerWithRestorationIdentifierPath (alle Daten dekodieren, die für normale Inbetriebnahme benötigt wird)

awakeFromNib

viewDidLoad

viewWillAppear

viewDidAppear

decodeRestorableStateWithCoder (decode restorable Zustandsdaten, und stellen Sie Ihre Controller UI)

+1

Dies ist nicht korrekt. Die richtige Reihenfolge für die Zustandswiederherstellung lautet: 'appWillLaunch -> watchFromNib -> viewDidLoad -> decodeRestorableState -> appDidLaunch -> viewWillAppear' –

5

Lustig genug die Dekodierungsfolge auch anders und genau:

+viewControllerWithRestorationIdentifierPath:coder: 
awakeFromNib 
viewDidLoad 
decodeRestorableStateWithCoder: 
viewWillAppear 
viewDidAppear 

und es macht total Sinn so.

0

Ja, es wäre in der Tat schöner, wenn -decodeRestorableStateWithCoder: vor -viewDidLoad aufgerufen würden. Seufzer.

ich meine Ansicht Setup-Code bewegt (was auf wiederherstellbare Zustand abhängt) zu -viewWillAppear: und verwendet dispatch_once() anstelle einer boolean Variable:

private var setupOnce: dispatch_once_t = 0 

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

    : 
} 

Die Dokumentation besagt, dass „Ansichten nicht mehr unter Low-Speicher gelöscht sind Bedingungen "so dispatch_once sollte für die Lebensdauer des View-Controllers korrekt sein.

+0

Das Problem mit dispatch_once ist in diesem Fall, dass der Rumpf des dispatch_once Blocks nur einmal während des gesamten Aufrufs aufgerufen wird der App - wenn der UIViewController korrekt freigegeben wird, z. B. wenn er aus einem UINavigationController-Stack entfernt und dann wieder auf den Stack geschoben wird, wird der Setup-Code nicht erneut ausgeführt und die Ansicht wird nicht eingerichtet. Sie müssen mit diesem Muster SEHR vorsichtig sein und nur Code in den Block dispatch_once einschließen, der nicht ausgeführt werden muss, wenn die Ansicht gelöscht und wieder aktiviert wird. – Troy

0

Hinzufügen zu Berbie Antwort,

Der eigentliche Fluss ist:

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

Beachten Sie, dass innerhalb initWithCoder, müssen Sie self.restorationClass = [self class]; einstellen Dies wird viewControllerWithRestorationIdentifierPath:coder: dann aufgerufen werden, erzwingen.

2

Aus dem Buch "Programming iOS 9: Tauche tief in Views, View-Controller und Frameworks" Seiten 386-387

Die bekannte Reihenfolge der Ereignisse während des Zustands Restauration ist wie folgt:

  1. application:shouldRestoreApplicationState:
  2. application:viewControllerWithRestorationIdentifierPath:coder:
  3. viewControllerWithRestorationIdentifierPath:coder:, unten in der Kette, um in ord
  4. viewDidLoad, über die Kette; möglicherweise verschachtelt mit den vorstehenden
  5. decodeRestorableStateWithCoder:, um unten in der Kette
  6. application:didDecodeRestorableStateWithCoder:
  7. applicationFinishedRestoringState, um unten in der Kette

Sie noch nicht wissen, wann viewWillAppear: und viewDidAppear: ankommen, oder ob wird überhaupt ankommen. Aber in applicationFinishedRestoringState können Sie Ihren View-Controller und Ihre Schnittstelle zuverlässig konfigurieren.