Frage

habe ich eine Hierarchie lange View-Controller;

in dem ersten View-Controller Ich benutze diesen Code:

SecondViewController *svc = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
[self presentModalViewController:svc animated:YES];    
[svc release];

In der zweiten View Controller Ich benutze diesen Code:

ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self presentModalViewController:tvc animated:YES];    
[tvc release];

und so weiter.

So gibt es einen Moment, in dem ich viele View-Controller haben und ich muss den ersten View-Controller zurück zu kommen. Wenn ich auf einmal einen Schritt zurück kommen, verwende ich in jedem View-Controller diesen Code:

[self dismissModalViewControllerAnimated:YES];

Wenn ich wieder direkt von der, sagen wir, sechsten View Controller zum ersten gehen wollen, was ich tun, alle Controller entlassen auf einmal?

Danke

War es hilfreich?

Lösung 2

ich die Lösung gefunden.

Natürlich kann man die Lösung in dem naheliegendste Ort finden, so für die dismissModalViewControllerAnimated Methode aus der UIViewController Referenz Lese ...

Wenn Sie präsentieren mehrere modale Ansicht Controller in Aufeinanderfolge und damit einen Stapel von modaler Ansicht bauen Controller, ruft diese Methode auf ein View-Controller niedriger in dem Stapel entlässt seinen unmittelbares Kind Blick Controller und alle Ansicht-Controller über dem Kind auf dem Stapel. Wann dies geschieht, nur die meisten Top-Ansicht wird in einer animierten Art und Weise entlassen; beliebige Zwischensicht Controller sind einfach aus dem Stapel entfernt. Das meist Top-Ansicht wird zurückgewiesen seine Verwendung modal Übergangsstil, die gegebenen unterscheiden sich von den von anderen verwendeten Arten View-Controller senken im Stapel.

so ist es genug, um die dismissModalViewControllerAnimated auf dem Ziel-Ansicht aufzurufen. Ich habe den folgenden Code:

[[[[[self parentViewController] parentViewController] parentViewController] parentViewController] dismissModalViewControllerAnimated:YES];

zu mir nach Hause gehen.

Andere Tipps

Ja. gibt es bereits eine Reihe von Antworten, aber ich bin nur ein ohnehin zum Ende der Liste geht hinzuzufügen. Das Problem ist, dass wir einen Verweis auf den View-Controller an der Basis der Hierarchie erhalten müssen. Wie in @Juan Munhoes Junior Antwort, können Sie die Hierarchie zu Fuß, aber es können verschiedene Wege geben, die Benutzer nehmen könnte, so dass eine ziemlich fragile Antwort ist. Es ist nicht schwer, diese einfache Lösung zu erweitern, wenn auch einfach die Hierarchie geht der Suche nach dem Boden des Stapels. Der Aufruf auf der Unterseite ein entlassen werden alle anderen bekommen, auch.

-(void)dismissModalStack {
    UIViewController *vc = self.presentingViewController;
    while (vc.presentingViewController) {
        vc = vc.presentingViewController;
    }
    [vc dismissViewControllerAnimated:YES completion:NULL];
}

Das ist einfach und flexibel: Wenn Sie für eine bestimmte Art von View-Controller in dem Stapel suchen möchten, können Sie Logik auf [vc isKindOfClass:[DesiredViewControllerClass class]] basierend hinzufügen könnte.

iOS 8+ universelle Methode für Vollbild Entlassung ohne falsche Animation Kontext. In Objective-C und Swift

Objective-C

- (void)dismissModalStackAnimated:(bool)animated completion:(void (^)(void))completion {
    UIView *fullscreenSnapshot = [[UIApplication sharedApplication].delegate.window snapshotViewAfterScreenUpdates:false];
    [self.presentedViewController.view insertSubview:fullscreenSnapshot atIndex:NSIntegerMax];
    [self dismissViewControllerAnimated:animated completion:completion];
}

Swift

func dismissModalStack(animated: Bool, completion: (() -> Void)?) {
    if let fullscreenSnapshot = UIApplication.shared.delegate?.window??.snapshotView(afterScreenUpdates: false) {
        presentedViewController?.view.addSubview(fullscreenSnapshot)
    }
    if !isBeingDismissed {
        dismiss(animated: animated, completion: completion)
    }
}

tl; dr

Was ist mit anderen Lösungen ist falsch?

Es gibt viele Lösungen, aber keiner von ihnen mit falschem entlasse Kontext zählt so:

z. root A -> Presents B -> C Geschenke und Sie möchten die A von C entlassen, können Sie officialy von dismissViewControllerAnimated auf rootViewController aufrufen.

 [[UIApplication sharedApplication].delegate.window.rootViewController dismissModalStackAnimated:true completion:nil];

Doch Anruf auf dieser Wurzel aus C entlassen wird mit falschem Übergang zum richtigen Verhalten führen (B zu A würde anstelle von C nach A gesehen wurde).


so

Ich habe universelle Methode entlassen. Diese Methode wird die aktuelle Vollbild Snapshot erstellen und über den präsentierten View-Controller des Empfängers platzieren und entlassen sie dann alle (Beispiel: Genannt Standard aus C entlassen, aber B ist wirklich als Entlassung aus gesehen).

Sagen Sie Ihre erste View-Controller ist auch der Root / Initial-View-Controller (die Sie in Ihrem Storyboard als Initial-View-Controller nominiert). Sie können es bis zu Anfragen hören alle seine präsentierten Ansicht-Controller schließen:

in FirstViewController:

- (void)viewDidLoad {
    [super viewDidLoad];

    // listen to any requests to dismiss all stacked view controllers
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissAllViewControllers:) name:@"YourDismissAllViewControllersIdentifier" object:nil];

    // the remainder of viewDidLoad ...
}

// this method gets called whenever a notification is posted to dismiss all view controllers
- (void)dismissAllViewControllers:(NSNotification *)notification {
    // dismiss all view controllers in the navigation stack
    [self dismissViewControllerAnimated:YES completion:^{}];
}

Und in einem anderen View-Controller auf den Navigationsstapel, die wir an die Spitze des Navigationsstapels zurückkehren entscheiden sollten:

[[NSNotificationCenter defaultCenter] postNotificationName:@"YourDismissAllViewControllersIdentifier" object:self];

Dies sollte alle modal präsentierte View-Controller mit einer Animation entlassen, nur die Root-View-Controller zu verlassen. Dies funktioniert auch, wenn Ihr erster View-Controller eine UINavigationController und der erste View-Controller ist als seine Wurzel-View-Controller eingestellt.

Bonus-Tipp: Es ist wichtig, dass die Benachrichtigung Name identisch ist. Wahrscheinlich eine gute Idee, diese Benachrichtigung Namen irgendwo in der App als eine Variable zu definieren, da nicht Missverständnissen zu erhalten wegen Tippfehlern.

[[self presentingViewController]presentingViewController]dismissModalViewControllerAnimated:NO];

Sie können auch einen Vertreter in allen Controllern implementieren Sie entlassen wollen

Wenn Sie alle verwenden sind Model View Controller Sie eine Benachrichtigung für Abweisung aller preseted View-Controller verwenden können.

1.Register Mitteilung in RootViewController wie folgt

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(dismissModelViewController)
                                             name:dismissModelViewController
                                           object:nil];

2.Umsetzung die dismissModelViewController Funktion in RootViewController

- (void)dismissModelViewController
{
    While (![self.navigationController.visibleViewController isMemberOfClass:[RootviewController class]])
    {
        [self.navigationController.visibleViewController dismissViewControllerAnimated:NO completion:nil];
    }
}

3.Notification jede der Nähe veröffentlichen oder Tastenereignis schließen.

   [[NSNotificationCenter defaultCenter] postNotificationName:dismissModelViewController object:self];

In Swift:

self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)

Das Problem mit den meisten Lösungen ist, dass, wenn Sie den Stapel von präsentiert Viewcontrollers entlassen, wird der Benutzer kurz die erste präsentiert Viewcontroller in dem Stapel sehen, wie sie entlassen wird. Jakubs ausgezeichnete Lösung löst das. Hier ist eine Erweiterung auf der Grundlage seiner Antwort.

extension UIViewController {

    func dismissAll(animated: Bool, completion: (() -> Void)? = nil) {
        if let optionalWindow = UIApplication.shared.delegate?.window, let window = optionalWindow, let rootViewController = window.rootViewController, let presentedViewController = rootViewController.presentedViewController  {
            if let snapshotView = window.snapshotView(afterScreenUpdates: false) {
                presentedViewController.view.addSubview(snapshotView)
                presentedViewController.modalTransitionStyle = .coverVertical
            }
            if !isBeingDismissed {
                rootViewController.dismiss(animated: animated, completion: completion)
            }
        }
    }

}

Verbrauch:. Rufen Sie diese Erweiterungsfunktion von jedem präsentiert Viewcontroller, dass Sie an die Wurzel entlassen zurück

@IBAction func close() {
    dismissAll(animated: true)
}

Versuchen Sie, diese ..

ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self.view addsubview:tvc];    
[tvc release];

Vor allem Oscar Peli Dank für Ihren Code.

Um Ihre Navigation am Anfang beginnen, könnten Sie es auf diese Weise ein wenig dynamischer machen. (Falls Sie nicht wissen, die Anzahl der in Viewcontrollers-Stack)

NSArray *viewControllers = self.navigationController.viewControllers;
[self.navigationController popToViewController: [viewControllers objectAtIndex:0] animated: YES];
  id vc = [self presentingViewController];
  id lastVC = self;
  while (vc != nil) {
    id tmp = vc;
    vc = [vc presentingViewController];
    lastVC = tmp;
  }
  [lastVC dismissViewControllerAnimated:YES completion:^{
}];

Hier ist eine Lösung, dass ich alle View-Controller, um knallen und zu entlassen, um die Root-Ansicht-Controller zu gehen. Ich habe diese beiden Methoden in einer Kategorie von UIViewController:

+ (UIViewController*)topmostViewController
{
    UIViewController* vc = [[[UIApplication sharedApplication] keyWindow] rootViewController];
    while(vc.presentedViewController) {
        vc = vc.presentedViewController;
    }
    return vc;
}

+ (void)returnToRootViewController
{
    UIViewController* vc = [UIViewController topmostViewController];
    while (vc) {
        if([vc isKindOfClass:[UINavigationController class]]) {
            [(UINavigationController*)vc popToRootViewControllerAnimated:NO];
        }
        if(vc.presentingViewController) {
            [vc dismissViewControllerAnimated:NO completion:^{}];
        }
        vc = vc.presentingViewController;
    }
}

Dann rufe ich nur

[UIViewController returnToRootViewController];

Eine schnelle Version mit einigen Ergänzungen, basierend auf diesem Kommentar

func dismissModalStack(viewController: UIViewController, animated: Bool, completionBlock: BasicBlock?) {
    if viewController.presentingViewController != nil {
        var vc = viewController.presentingViewController!
        while (vc.presentingViewController != nil) {
            vc = vc.presentingViewController!;
        }
        vc.dismissViewControllerAnimated(animated, completion: nil)

        if let c = completionBlock {
            c()
        }
    }
}

Einfache rekursive näher:

extension UIViewController {
    final public func dismissEntireStackAndSelf(animate: Bool = true) {
        // Always false on non-calling controller
        presentedViewController?.ip_dismissEntireStackAndSelf(false)
        self.dismissViewControllerAnimated(animate, completion: nil)
    }
}

Dies zwingt die Nähe jedes Kind-Controller und dann nur belebtes Selbst. Sie können wechseln, was auch immer Sie mögen, aber wenn Sie jeden Controller animieren sie gehen eins nach dem anderen und es ist langsam.

Anruf

baseController.dismissEntireStackAndSelf()

Swift 3 Erweiterung auf der Grundlage der oben genannten Antworten.

Prinzip für einen Stapel wie folgt aus: A -> B -> C -> D

  • Nehmen Sie einen Schnappschuss von D
  • Fügen Sie diese Momentaufnahme auf B
  • Entlassen von B ohne Animation
  • Nach Abschluss entlassen von A mit Animation

    extension UIViewController {
    
        func dismissModalStack(animated: Bool, completion: (() -> Void)?) {
            let fullscreenSnapshot = UIApplication.shared.delegate?.window??.snapshotView(afterScreenUpdates: false)
            if !isBeingDismissed {
                var rootVc = presentingViewController
                while rootVc?.presentingViewController != nil {
                    rootVc = rootVc?.presentingViewController
                }
                let secondToLastVc = rootVc?.presentedViewController
                if fullscreenSnapshot != nil {
                    secondToLastVc?.view.addSubview(fullscreenSnapshot!)
                }
                secondToLastVc?.dismiss(animated: false, completion: {
                    rootVc?.dismiss(animated: true, completion: completion)
                })
            }
        }
    }
    

Ein wenig Flimmern auf Simulator, aber nicht auf dem Gerät.

Swift-Erweiterung auf der Grundlage der oben genannten Antworten:

extension UIViewController {

    func dismissUntilAnimated<T: UIViewController>(animated: Bool, viewController: T.Type, completion: ((viewController: T) -> Void)?) {
        var vc = presentingViewController!
        while let new = vc.presentingViewController where !(new is T) {
            vc = new
        }
        vc.dismissViewControllerAnimated(animated, completion: {
            completion?(viewController: vc as! T)
        })
    }
}

Swift 3.0 Version:

extension UIViewController {

    /// Dismiss all modally presented view controllers until a specified view controller is reached. If no view controller is found, this function will do nothing.

    /// - Parameter reached:      The type of the view controller to dismiss until.
    /// - Parameter flag:         Pass `true` to animate the transition.
    /// - Parameter completion:   The block to execute after the view controller is dismissed. This block contains the instance of the `presentingViewController`. You may specify `nil` for this parameter.
    func dismiss<T: UIViewController>(until reached: T.Type, animated flag: Bool, completion: ((T) -> Void)? = nil) {
        guard let presenting = presentingViewController as? T else {
            return presentingViewController?.dismiss(until: reached, animated: flag, completion: completion) ?? ()
        }

        presenting.dismiss(animated: flag) {
            completion?(presenting)
        }
    }
}

ganz vergessen, warum ich gemacht das, wie es unglaublich dumm Logik ist die meiste Zeit machen dies eine modale Ansicht Controllers präsentiert View-Controller ist UITabBarController Berücksichtigung völlig nutzlos. Es macht viel mehr Sinn, um tatsächlich die Basis-View-Controller-Instanz und Anruf dismiss auf, dass erwerben.

Für Swift 3.0 +

self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)

Damit werden alle präsentierten View-Controller abtun auf RootViewController.

Mit dieser generischen Lösung dieses Problem zu lösen:

- (UIViewController*)topViewController
{
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }
    return topController;
}


- (void)dismissAllModalController{

    __block UIViewController *topController = [self topViewController];

    while (topController.presentingViewController) {
        [topController dismissViewControllerAnimated:NO completion:^{

        }];
        topController = [self topViewController];
    }
}

Entlassen der Top-VC animierte und die andere, die nicht. Wenn Sie hace drei modalen VC

[self dismissModalViewControllerAnimated:NO]; // First
[self dismissModalViewControllerAnimated:NO]; // Second
[self dismissModalViewControllerAnimated:YES]; // Third

EDIT:., Wenn Sie wollen, dass diese mit einem Verfahren nur tun, speichern Sie in einer Reihe von VC Hierarchie und schließen Sie das letzte Objekt animiert und die anderen nicht

Apple-Dokument über entlassen (animiertes: Abschluss :) Methode .

In Abschnitt Discussion, gesagt:

any intermediate view controllers are simply removed from the stack.

Wenn Sie mehr View-Controller nacheinander präsentieren, so dass ein Stapel von präsentiert View-Controller erstellen, rufe diese Methode auf einem View-Controller senken in dem Stapel seine unmittelbaren Kind-View-Controller und all View-Controller über dem Kind auf dem Stapel entlässt. Wenn dies geschieht, wird nur die meisten Top-Ansicht wird in einer animierten Art und Weise entlassen; Jede Zwischen Ansicht-Controller werden einfach aus dem Stapel entfernt. das oberste Ansicht entlassen wird seine modal Übergangsstil verwendet wird, die sich von den Arten von anderen Ansicht-Controller verwendet werden, unterscheiden kann, in dem Stapel abzusenken.

Mit anderen Worten, wenn der View-Controller-Stack wie folgt

Root -> A -> B -> C -> D ... -> Z

D dismiss Methode aufruft, alle View-Controller behide D, z. B.: (E ... Z), wird aus dem Stapel entfernt werden

In swift 4 und Xcode 9 Dieser Wille hilft Ihnen.

var vc : UIViewController = self.presentingViewController!
        while ((vc.presentingViewController) != nil) {
            vc = vc.presentingViewController!
        }
        vc.dismiss(animated: true, completion: nil)

Genießen Sie !!! :)

Wenn Sie direkt zurück an den Start gehen, können Sie den Code verwenden [Self.navigationController popToRootViewControllerAnimated: YES];

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top