it-swarm.com.de

Wie kann ich eine Ansicht aus einem UINavigationController herausnehmen und in einem Vorgang durch eine andere ersetzen?

Ich habe eine Anwendung, bei der ich eine Ansicht vom Stapel eines UINavigationControllers entfernen und durch eine andere ersetzen muss. Die Situation ist so, dass die erste Ansicht ein bearbeitbares Element erstellt und sich dann durch einen Editor für das Element ersetzt. Wenn ich die offensichtliche Lösung innerhalb der ersten Ansicht mache:

MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

[self retain];
[self.navigationController popViewControllerAnimated: NO];
[self.navigationController pushViewController: mevc animated: YES];
[self release];

Ich bekomme sehr seltsames Verhalten. Normalerweise wird die Editoransicht angezeigt, aber wenn ich versuche, die Zurück-Schaltfläche in der Navigationsleiste zu verwenden, erhalte ich zusätzliche Bildschirme, einige leer und einige nur vermasselt. Der Titel wird auch zufällig. Es ist, als wäre der Nav-Stack vollständig abgespritzt.

Was wäre ein besserer Ansatz für dieses Problem?

Danke, Matt

83
Matt Brandt

Ich habe festgestellt, dass Sie die viewControllers-Eigenschaft überhaupt nicht manuell durcheinander bringen müssen. Grundsätzlich gibt es zwei knifflige Dinge dazu.

  1. self.navigationController gibt nil zurück, wenn sich self derzeit nicht im Stack des Navigationscontrollers befindet. Speichern Sie es also in einer lokalen Variablen, bevor Sie den Zugriff darauf verlieren.
  2. Sie müssen retain (und korrekt release) self oder das Objekt, dem die Methode gehört, in der Sie sich befinden, wird freigegeben, wodurch Fremdheit verursacht wird.

Sobald Sie diese Vorbereitungen getroffen haben, drücken Sie einfach wie gewohnt. Dieser Code ersetzt sofort den obersten Controller durch einen anderen.

// locally store the navigation controller since
// self.navigationController will be nil once we are popped
UINavigationController *navController = self.navigationController;

// retain ourselves so that the controller will still exist once it's popped off
[[self retain] autorelease];

// Pop this controller and replace with another
[navController popViewControllerAnimated:NO];
[navController pushViewController:someViewController animated:NO];

Wenn Sie in dieser letzten Zeile animated in YES ändern, wird der neue Bildschirm tatsächlich animiert, und der Controller, den Sie gerade geöffnet haben, wird animiert. Sieht ganz nett aus!

137
Alex Wayne

Der folgende Ansatz scheint mir schöner zu sein und funktioniert auch gut mit ARC:

UIViewController *newVC = [[UIViewController alloc] init];
// Replace the current view controller
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]];
[viewControllers removeLastObject];
[viewControllers addObject:newVC];
[[self navigationController] setViewControllers:viewControllers animated:YES];
56
Luke Rogers

Erfahrungsgemäß müssen Sie direkt mit der viewControllers -Eigenschaft des UINavigationControllers arbeiten. So etwas sollte funktionieren:

MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

[[self retain] autorelease];
NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];
[controllers removeLastObject];
self.navigationController.viewControllers = controllers;
[self.navigationController pushViewController:mevc animated: YES];

Hinweis: Ich habe das Retain/Release in ein Retain/Autorelease geändert, da dies im Allgemeinen robuster ist. Wenn zwischen dem Retain/Release eine Ausnahme auftritt, können Sie sich selbst überlassen, aber dafür sorgt Autorelease.

9
Lily Ballard

Nach viel Mühe (und der Anpassung des Codes von Kevin) habe ich schließlich herausgefunden, wie dies in dem View-Controller geschieht, der aus dem Stapel gezogen wird. Das Problem, das ich hatte, war, dass self.navigationController null zurückgab, nachdem ich das letzte Objekt aus dem Controller-Array entfernt hatte. Ich denke, es lag an dieser Zeile in der Dokumentation für UIViewController für die Instanzmethode navigationController "Gibt nur einen Navigationscontroller zurück, wenn sich der Viewcontroller in seinem Stack befindet."

Ich denke, wenn der aktuelle View-Controller aus dem Stack entfernt wird, gibt seine navigationController-Methode null zurück.

Hier ist der angepasste Code, der funktioniert:

UINavigationController *navController = self.navigationController;
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];
[controllers removeLastObject];
navController.viewControllers = controllers;
[navController pushViewController:mevc animated: YES];
7
Michael

Danke, das war genau das, was ich brauchte. Ich habe dies auch in eine Animation eingefügt, um die Seitenrotation zu erhalten:

        MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

    UINavigationController *navController = self.navigationController;      
    [[self retain] autorelease];

    [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration: 0.7];
    [UIView setAnimationTransition:<#UIViewAnimationTransitionCurlDown#> forView:navController.view cache:NO];

    [navController popViewControllerAnimated:NO];
    [navController pushViewController:mevc animated:NO];

    [UIView commitAnimations];

Die Dauer von 0,6 ist schnell, gut für 3GS und neuer, 0,8 ist für 3G immer noch etwas zu schnell.

Johan

4
Johan

Wenn Sie einen anderen View-Controller von popToRootViewController anzeigen möchten, müssen Sie Folgendes tun:

         UIViewController *newVC = [[WelcomeScreenVC alloc] initWithNibName:@"WelcomeScreenVC" bundle:[NSBundle mainBundle]];
            NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]];
            [viewControllers removeAllObjects];
            [viewControllers addObject:newVC];
            [[self navigationController] setViewControllers:viewControllers animated:NO];

Jetzt wird der gesamte vorherige Stack entfernt und ein neuer Stack wird mit dem erforderlichen rootViewController erstellt.

3
msmq

Am liebsten mache ich das mit einer Kategorie auf UINavigationController. Folgendes sollte funktionieren:

UINavigationController + Helpers.h #einführen 

@interface UINavigationController (Helpers)

- (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller;

@end

UINavigationController + Helpers.m
#import "UINavigationController + Helpers.h"

@implementation UINavigationController (Helpers)

- (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller {
    UIViewController* topController = self.viewControllers.lastObject;
    [[topController retain] autorelease];
    UIViewController* poppedViewController = [self popViewControllerAnimated:NO];
    [self pushViewController:controller animated:NO];
    return poppedViewController;
}

@end

Dann können Sie von Ihrem View Controller aus die Draufsicht durch eine neue Ansicht wie folgt ersetzen:

[self.navigationController replaceTopViewControllerWithViewController: newController];
1
bbrame

Diese UINavigationController-Instanzmethode könnte funktionieren ...

Öffnet View-Controller, bis der angegebene View-Controller der Draufsicht-Controller ist, und aktualisiert die Anzeige.

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
1
diclophis
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy];
    for(int i=0;i<controllers.count;i++){
       [controllers removeLastObject];
    }
 self.navigationController.viewControllers = controllers;
1
Ravi

Hier ist ein weiterer Ansatz, bei dem das viewControllers-Array nicht direkt betroffen ist. Prüfen Sie, ob der Controller bereits geknackt wurde. Wenn ja, drücken Sie ihn.

TasksViewController *taskViewController = [[TasksViewController alloc] initWithNibName:nil bundle:nil];

if ([navigationController.viewControllers indexOfObject:taskViewController] == NSNotFound)
{
    [navigationController pushViewController:taskViewController animated:animated];
}
else
{
    [navigationController popToViewController:taskViewController animated:animated];
}
1
ezekielDFM

Ich musste in letzter Zeit etwas Ähnliches tun und meine Lösung auf Michaels Antwort gründen. In meinem Fall musste ich zwei View Controller aus dem Navigationsstapel entfernen und dann einen neuen View Controller hinzufügen. Berufung

[controller removeLastObject];
UINavigationController *navController = self.navigationController;

// retain ourselves so that the controller will still exist once it's popped off
[[self retain] autorelease];

searchViewController = [[SearchViewController alloc] init];    
NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];

[controllers removeLastObject];
// In my case I want to go up two, then Push one..
[controllers removeLastObject];
navController.viewControllers = controllers;

NSLog(@"controllers: %@",controllers);
controllers = nil;

[navController pushViewController:searchViewController animated: NO];
 </ code>
1
mattlangtree

Für Monotouch/Xamarin-IOS:

in der UISplitViewController-Klasse;

UINavigationController mainNav = this._navController; 
//List<UIViewController> controllers = mainNav.ViewControllers.ToList();
mainNav.ViewControllers = new UIViewController[] { }; 
mainNav.PushViewController(detail, true);//to have the animation
0
Nabil.A

Nicht genau die Antwort, kann aber in einigen Szenarien hilfreich sein (z. B. meines):

Wenn Sie den Viewcontroller C öffnen und zu B (out of stack) anstatt zu A (dem unten stehenden C) wechseln müssen, können Sie B vor C drücken und alle 3 auf dem Stack haben. Indem Sie den B-Push unsichtbar halten und wählen, ob Sie nur C oder C und B zusammenpoppten, können Sie denselben Effekt erzielen.

anfängliches Problem A -> C (Ich möchte C platzen lassen und B aus dem Stapel zeigen)

mögliche Lösung A -> B (unsichtbar gedrückt) -> C (wenn ich C drücke, wähle ich B oder auch Pop)

0
alasker

Sie können das Array für Navigations-View-Controller überprüfen, das Sie allen View-Controllern zur Verfügung stellen, die Sie im Navigations-Stack hinzugefügt haben. Wenn Sie dieses Array verwenden, können Sie zu einem bestimmten View-Controller zurück navigieren.

0
jignesh

Ich verwende diese Lösung, um die Animation beizubehalten.

[self.navigationController pushViewController:controller animated:YES];
NSMutableArray *newControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[newControllers removeObject:newControllers[newControllers.count - 2]];
[self.navigationController setViewControllers:newControllers];
0
code4j

Alternative,

Sie können category verwenden, um zu vermeiden, dass self.navigationController nach nilpopViewControllerAnimated ist.

einfach Pop und Push, es ist leicht zu verstehen, muss nicht auf viewControllers.... zugegriffen werden.

// UINavigationController+Helper.h
@interface UINavigationController (Helper)

- (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated;

@end


// UINavigationController+Helper.m
@implementation UINavigationController (Helper)

- (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    UIViewController *v =[self popViewControllerAnimated:NO];

    [self pushViewController:viewController animated:animated];

    return v;
}
@end

In Ihrem ViewController

// #import "UINavigationController+Helper.h"
// invoke in your code
UIViewController *v= [[MyNewViewController alloc] init];

[self.navigationController popThenPushViewController:v animated:YES];

RELEASE_SAFELY(v);
0
payliu