Mina
Mina

Reputation: 2212

Dismissing all presented ViewControlelrs in a UINavigationController hierarchically

I was looking for a way to dismiss all the modally presented viewControllers in a UINavigationController hierarchically without knowing the name of them. so I ended up to the while loop as follow:

Swift

while(navigationController.topViewController != navigationController.presentedViewController) {
      navigationController.presentedViewController?.dismiss(animated: true, completion: nil)
}

Objective-c

while(![self.navigationController.topViewController isEqual:self.navigationController.presentedViewController]) {
    [self.navigationController.presentedViewController dismissViewControllerAnimated:YES completion:nil];
}

I want to dismiss all the presentedControllers one by one till the presentedViewController and topViewcontroller become equal.

the problem is that the navVC.presentedViewController doesn't changed even after dismissing.

It remains still the same even after dismissing and I end up to an infiniteLoop.

Does anyone knows where is the problem?

Upvotes: 0

Views: 1229

Answers (7)

dwcho
dwcho

Reputation: 475

I had a similar issue of deleting/dismissing existing/previous push notification when a new push notification arrives where different pictures are sent as a push notification. In my situation, using Swift 5, I wanted to delete/dismiss previous push notification and display a new push notification all by itself regardless whether the user acknowledged the previous notification or not (i.e. without user's acknowledgement).

I tried Kadian's recommendation with a minor change and it worked flawlessly.

Here is my NotificationDelegate.swift

import UIKit
import UserNotifications

extension AppDelegate: UNUserNotificationCenterDelegate {
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    willPresent notification: UNNotification,
    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

    completionHandler([.alert, .sound, .badge])   
  }

  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void) {
    
    UNUserNotificationCenter.current().removeAllDeliveredNotifications()

    defer { completionHandler() }
    guard response.actionIdentifier == UNNotificationDefaultActionIdentifier else {return}

    let payload = response.notification.request.content
    
    let pn = payload.body  
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    
    let vc = storyboard.instantiateViewController(withIdentifier: pn)

    //***Below cmd will erase previous push alert***
    self.window!.rootViewController?.dismiss(animated: false, completion: nil)

    //Below cmd will display a newly received push notification
    self.window!.rootViewController!.present(vc, animated: false)

  }
  }

Upvotes: 0

Nike Kov
Nike Kov

Reputation: 13734

In my case nothing works but:

func dismissToSelf(completion: (() -> Void)?) {
    // Collecting presented
    var presentedVCs: [UIViewController] = []
    var vc: UIViewController? = presentedViewController
    while vc != nil {
        presentedVCs.append(vc!)
        vc = vc?.presentedViewController
    }

    // Dismissing all but first
    while presentedVCs.count > 1 {
        presentedVCs.last?.dismiss(animated: false, completion: nil)
        presentedVCs.removeLast()
    }

    // Dismissing first with animation and completion
    presentedVCs.first?.dismiss(animated: true, completion: completion)
}

Upvotes: 2

Mina
Mina

Reputation: 2212

I've found the answer. I can dismiss all presentedViewControllers on a navigationController by:

navigationController.dismiss(animated: true, completion: nil)

It keeps the topViewController and dismiss all other modals.

Upvotes: 1

Jaylen
Jaylen

Reputation: 33

Glad to see you have found the answer, and I've done this by another way.

You can create a BaseViewController(actually lots of app do that), and defined a property like 'presentingController' in appdelegate that indicate the presenting ViewController, then in the viewWillAppear method, set the property so that it always indicate the top view controller.

-(void)viewWillAppear:(BOOL)animated{  

    AppDelegate *delegate=(AppDelegate *)[[UIApplication sharedApplication]delegate];  
    delegate.presentingController = self;    
}

All the class inherited from BaseViewController will call it. When you want to dismiss all the controller, just loop as follow:

- (void)clickButton:(id)sender {  

    AppDelegate *delegate=(AppDelegate *)[[UIApplicationsharedApplication]delegate];  

    if (delegate.presentingController)  
    {  
        UIViewController *vc =self.presentingViewController;  

        if ( !vc.presentingViewController )   return;  

        while (vc.presentingViewController)  
        {  
            vc = vc.presentingViewController;  
        }  

        [vc dismissViewControllerAnimated:YEScompletion:^{  

        }];  
    }  
}

Hope this will help you :)

Upvotes: 0

Mahesh Prasad Mahalik
Mahesh Prasad Mahalik

Reputation: 151

Please check this code

-(void)dismissModalStack {
UIViewController *vc = self.window.rootViewController;
while (vc.presentedViewController) {
    vc = vc.presentedViewController;
    [vc dismissViewControllerAnimated:false completion:nil];
}

}

Upvotes: 0

BuLB JoBs
BuLB JoBs

Reputation: 871

Not need to used self.navigationController.presentedViewController.

Might be help! my code is as follows: Objective-c

[self dismissViewControllerAnimated:YES completion:^{

}];
// Or using this 

dispatch_async(dispatch_get_main_queue(), ^{
    [self dismissViewControllerAnimated:YES completion:nil];
});

Upvotes: 0

Kadian
Kadian

Reputation: 654

Form your question I understood that you want to dismiss all view controllers above the root view controller. For that you can do it like this:

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

Upvotes: 0

Related Questions