Flexible Navigation zwischen Szenen

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Flexible Navigation zwischen Szenen

    Hallo,

    ich denke gerade über ein Arbeitsspeicher-Problem nach, welches sich für mich aufgrund einer bestimmten Art der Navigation ergeben hat:

    Wie programmiert man am besten eine Navigation mit benutzerdefinierten Transitionen, in der man per Code flexibel zwischen verschiedenen Szenen mit eigenen Klassen herumspringen kann? Also keine fest vordefinierte Szenen-Abfolge A->B->C->D, sondern vom Anwender bedingt steuerbar(bei richtigen Antworten können weitere zusätzliche Seiten angesteuert werden) und teilweise mit Loops (bei falschen Antworten wiederholen sich eine oder mehrere Szenen, z.B. A->B->C->B->C->B->C->D). Die Szenen werden mit dem Interface Builder erstellt.

    Bei Segues zwischen den Szenen dürfte doch der Arbeitsspeicher der vorhergehenden Szenen nicht so ohne weiteres wieder frei gegeben werden, richtig?
    Ist instantiateViewControllerWithIdentifier + presentViewController der richtige Ansatz, oder bekommt man da prinzipiell die gleichen Probleme?
    So was richtig Eindeutiges über flexible Navigationen und dem Haushalten von Arbeitsspeicher habe ich nicht gefunden. Ich habe zwar Erklärungen über Navigation Controller gefunden, aber die Beispiele schienen mir vom Aufbau her allesamt komplett anders zu sein (eher vordefinierte Navigations-Hierarchie und eine Main Szene als zentraler Rücksprungs-Punkt) als das was ich gerne hätte. Wie ließe sich so eine flexible Navigation rein vom Ansatz her am besten umsetzen?

    Viele Grüße,
    Profiwrestler
  • Ob Du das mit Übergängen machst oder die Viewcontroller direkt aus dem Storyboard erzeugst, ist nicht so entscheidend. Wichtiger ist, wie Du zwischen den Controllern navigierst. Wenn Du beispielsweise einen Navigationstack verwendest solltest Du darauf achten, dass der durch eine Schleife nicht beliebig groß wird. Hier kann es sinnvoller sein, zurückzuspringen und dadurch vorhandene Controller wiederzuverwenden.
    „Meine Komplikation hatte eine Komplikation.“
  • Hallo macmoonshine,

    vielen Dank für Deine Hilfe. Die von mir geplanten ViewControlller werden ziemlich viel Arbeitsspeicher benötigen. Daher wollte ich - wenn das möglich ist - die inaktiven ViewController so schnell wie möglich entfernen. Ich möchte also nicht nur Schleifen verhindern, sondern bereits Ketten!

    Dein Satz-Anfang „Wenn Du beispielsweise einen Navigationstack verwendest...“ lässt mich vermuten, dass man zwischen ViewControllern irgendwie auch ohne Stack herumspringen kann? Leider finde ich dazu aber nichts, sondern nur Push-/Pop-Beispiele für die NavigationController-Klasse oder aber presentViewController/dismissViewController für hierarchielose ViewController.

    Ich habe jetzt mal versucht, dass mit dem Zurückspringen in einem Minimal-Beispiel zu implementieren:

    Startseite.h:

    Quellcode

    1. #import <UIKit/UIKit.h>
    2. #import "Szene1.h"
    3. #import "Szene2.h"
    4. #import "Szene3.h"
    5. @protocol StartseiteDelegate <NSObject>
    6. @optional
    7. -(void)goto_Szene1;
    8. -(void)goto_Szene2;
    9. -(void)goto_Szene3;
    10. @end
    11. @interface Startseite : UIViewController
    12. @end
    Alles anzeigen


    Startseite.m:

    Quellcode

    1. #import "Startseite.h"
    2. @interface Startseite ()
    3. @end
    4. @implementation Startseite
    5. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    6. {
    7. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    8. if (self) {
    9. }
    10. return self;
    11. }
    12. - (void)viewDidLoad
    13. {
    14. [super viewDidLoad];
    15. }
    16. - (void)viewDidAppear:(BOOL)animated
    17. {
    18. static dispatch_once_t pred;
    19. dispatch_once(&pred, ^{
    20. [self goto_Szene1];
    21. });
    22. }
    23. - (void)didReceiveMemoryWarning
    24. {
    25. [super didReceiveMemoryWarning];
    26. }
    27. - (void)goto_Szene1 {
    28. Szene1 *viewController = [[self storyboard] instantiateViewControllerWithIdentifier:@"Szene1"];
    29. viewController.delegate = self;
    30. [self presentViewController:viewController animated:YES completion:^{
    31. NSLog(@"%@", viewController);
    32. }];
    33. }
    34. - (void)goto_Szene2 {
    35. Szene2 *viewController = [[self storyboard] instantiateViewControllerWithIdentifier:@"Szene2"];
    36. viewController.delegate = self;
    37. [self presentViewController:viewController animated:YES completion:^{
    38. NSLog(@"%@", viewController);
    39. }];
    40. }
    41. - (void)goto_Szene3 {
    42. Szene3 *viewController = [[self storyboard] instantiateViewControllerWithIdentifier:@"Szene3"];
    43. viewController.delegate = self;
    44. [self presentViewController:viewController animated:YES completion:^{
    45. NSLog(@"%@", viewController);
    46. }];
    47. }
    48. @end
    Alles anzeigen


    Szene1.h:

    Quellcode

    1. #import <UIKit/UIKit.h>
    2. #import "Startseite.h"
    3. @interface Szene1 : UIViewController {
    4. }
    5. @property (nonatomic,weak) id delegate;
    6. - (IBAction)goto_Szene2:(id)sender;
    7. @end
    Alles anzeigen


    Szene1.m:

    Quellcode

    1. #import "Szene1.h"
    2. @interface Szene1 ()
    3. @end
    4. @implementation Szene1
    5. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    6. {
    7. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    8. if (self) {
    9. }
    10. return self;
    11. }
    12. - (void)viewDidLoad
    13. {
    14. [super viewDidLoad];
    15. }
    16. - (void)didReceiveMemoryWarning
    17. {
    18. [super didReceiveMemoryWarning];
    19. }
    20. - (IBAction)goto_Szene2:(id)sender {
    21. [self dismissViewControllerAnimated:YES completion:^{
    22. [self.delegate goto_Szene2];
    23. }];
    24. }
    25. @end
    Alles anzeigen


    Szene2.h:

    Quellcode

    1. #import <UIKit/UIKit.h>
    2. #import "Startseite.h"
    3. @interface Szene2 : UIViewController {
    4. }
    5. @property (nonatomic,weak) id delegate;
    6. - (IBAction)goto_Szene3:(id)sender;
    7. @end
    Alles anzeigen


    Szene2.m:

    Quellcode

    1. #import "Szene2.h"
    2. @interface Szene2 ()
    3. @end
    4. @implementation Szene2
    5. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    6. {
    7. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    8. if (self) {
    9. }
    10. return self;
    11. }
    12. - (void)viewDidLoad
    13. {
    14. [super viewDidLoad];
    15. }
    16. - (void)didReceiveMemoryWarning
    17. {
    18. [super didReceiveMemoryWarning];
    19. }
    20. - (IBAction)goto_Szene3:(id)sender {
    21. [self dismissViewControllerAnimated:YES completion:^{
    22. [self.delegate goto_Szene3];
    23. }];
    24. }
    25. @end
    Alles anzeigen


    Szene3.h:

    Quellcode

    1. #import <UIKit/UIKit.h>
    2. #import "Startseite.h"
    3. @interface Szene3 : UIViewController {
    4. }
    5. @property (nonatomic,weak) id delegate;
    6. - (IBAction)goto_Szene1:(id)sender;null
    7. @end
    Alles anzeigen


    Szene3.m:

    Quellcode

    1. #import "Szene3.h"
    2. @interface Szene3 ()
    3. @end
    4. @implementation Szene3
    5. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    6. {
    7. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    8. if (self) {
    9. }
    10. return self;
    11. }
    12. - (void)viewDidLoad
    13. {
    14. [super viewDidLoad];
    15. }
    16. - (void)didReceiveMemoryWarning
    17. {
    18. [super didReceiveMemoryWarning];
    19. }
    20. - (IBAction)goto_Szene1:(id)sender {
    21. [self dismissViewControllerAnimated:YES completion:^{
    22. [self.delegate goto_Szene1];
    23. }];
    24. }
    25. @end
    Alles anzeigen


    Die Navigation mittels Protocol/Delegate funktioniert scheinbar, der Arbeitsspeicher wird aber erst freigegeben, wenn ich einen Speicherwarnhinweis simuliere.
    Ich frage mich nur, ob ich wirklich für jede Klasse eine eigene goto_Szene-Methode deklarieren muss(wegen dem Variablen-Typ, wenn ich UIViewController nehme, findet er ja die Delegate-Eigenschaft nicht)?
    Eleganter wäre es natürlich, wenn ich vom aktuellen ViewController aus einfach nur die Ziel-ID als String übergebe und eine generische Goto-Funktion steuert dann den ViewController mit der entsprechenden ID an.

    Dann ist mir auch nicht ganz klar, wie ich einen selbst animierten Übergang zwischen den Szenen hinbekomme, wenn da stets der Rücksprung zur Startseite dazwischen ist…?

    Viele Grüße,
    Profiwrestler
  • Mit presentViewController:animated: zeigst Du die Viewcontroller modal an, was für Deine Zwecke suboptimal ist, da dabei immer die Vorgänger erhalten bleiben. Da ist ein Navigationcontroller flexibler, weil Du damit zurückspringen kannst. Du kannst unter Umständen einen UIPageViewController verwenden. Da kannst Du die inaktiven Viewcontroller jederzeit freigeben. Eine weitere Möglichkeit wäre die Verwendung eines eigenen Containerviewcontrollers oder den Rootviewcontroller des Fensters direkt auszutauschen.

    Woher rührt die große Datenmenge?
    „Meine Komplikation hatte eine Komplikation.“