43

I have 2 view controllers presented modally.

A presents B which presents C.

When I dismiss C I would like to dismiss B as well. But I am not sure how to do this:

Dismiss C:

[self dismissModalViewControllerAnimated:YES]
//[delegate dismissB] //this doesn't work either when i create a delegate pattern

Now I am left with B. How can I dismiss B from C?

Sheehan Alam
  • 57,155
  • 123
  • 348
  • 546

15 Answers15

113

Just found out you need to use presentingViewController in iOS 5.

[self.presentingViewController.presentingViewController dismissModalViewControllerAnimated:YES];

A -> B -> C

Running the above code in modal C will take you back to A

Andy Davies
  • 4,227
  • 5
  • 21
  • 31
  • 1
    No problem :-) Every example I came across was for pre iOS5 and never worked – Andy Davies May 10 '12 at 08:39
  • It would be correct to dismiss view controller from presentingViewController using delegate. (According to Apple guides) – JastinBall Jul 26 '14 at 06:32
  • 4
    `...dismissModalViewControllerAnimated` is deprecated now. Try `[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];` – DevC Aug 15 '14 at 15:43
  • this works on ios7, but in ios8 if B is presented with modalPresentationStyle OverCurrentContextyou have to call the [A dismissController] twice – Ted Sep 09 '15 at 09:16
  • Wasn't working for me because I had it in the completion block of controller C's dismiss method... realized of course it wouldn't work, self is nil! lol – Jose Ramirez Jun 28 '19 at 03:41
14

This worked for me, very simple

// Call inside View controller C    
self.presentingViewController?.dismissViewControllerAnimated(false, completion: nil)
self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)

Explanation:

If you call dismiss on C, it can only remove C. If you call dismiss on B, it will do the right thing: Remove the topmost modal view controller. The first call therefore removes C (with no animation). The second call removes B.

The easiest way to access view controller B from C is to use the presentingViewController variable.

n13
  • 6,568
  • 48
  • 40
13

In B. Put:

[self dismissModalViewControllerAnimated:NO];
[self dismissModalViewControllerAnimated:YES];

Only run one animation.

Curt
  • 94,964
  • 60
  • 257
  • 340
  • For Swift 5, I use this and it works: self.presentingViewController?.dismiss(animated: false) self.presentingViewController?.dismiss(animated: true) – clopex May 24 '21 at 09:59
9

In swift 4

self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil);

BEingprabhU
  • 1,336
  • 1
  • 15
  • 24
TheSwiftGuy77
  • 546
  • 1
  • 7
  • 24
8

Try using the next code in B (right after dismissing C, as you already do):

[self.parentViewController dismissModalViewControllerAnimated:YES];

IMPORTANT:
Don't do anything in the method after this line.
This view controller (B) probably will be released and deallocated...

UPDATE:
Starting from iOS7 the method above is deprecated.
Use the next method instead:

[self.parentViewController dismissViewControllerAnimated:YES completion:^{ /* do something when the animation is completed */ }];
Michael Kessler
  • 14,102
  • 12
  • 45
  • 61
  • 3
    I tried this but it just simply dismisses C. B is still visible. – Sheehan Alam Jul 11 '10 at 20:08
  • 3
    Try dismissing C without animation and then dismiss B with animation... BTW, where is this dismissing code located (which class and how is this method called)? – Michael Kessler Jul 11 '10 at 20:32
  • Dismissing code is located in C in a method called dismissAll. It is triggered by pressing a UIBarButtonItem. – Sheehan Alam Jul 11 '10 at 20:42
  • 1
    I was sure that it is located in B when I wrote my answer... The common solution for dismissing modal view controllers is to call a method in delegate (view controller that opened the modal one) and in that method the modal view controller should be dismissed. In your case A should be a delegate of B and B should be a delegate of C; in C you should call a delegate method in B that should dismiss C and call a delegate method in A, that should dismiss B... I hope it is clear enough now. – Michael Kessler Jul 12 '10 at 11:23
  • This method is deprecated in iOS 7/8 — what should take its place? – fatuhoku Feb 20 '15 at 16:20
  • More generic way to dismiss more that one modal view controllers is [here](https://stackoverflow.com/a/44583711/1151916) – Ramis Jun 16 '17 at 07:55
8

Check this for swift:

self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil);
Victor Rius
  • 4,033
  • 1
  • 17
  • 24
2

This worked for me:

// Swift
presentingViewController?.dismissViewControllerAnimated(true, completion: nil)

// Objective-C
[self.presentingViewController dismissViewControllerAnimated:true completion:nil];
Nagra
  • 1,449
  • 3
  • 15
  • 25
  • 1
    Messaging arguments in Objective-C is not separated by coma, so It will be without coma after "true" for // Objective-C [self.presentingViewController dismissViewControllerAnimated: true completion: nil] – Nikunj Joshi Nov 08 '17 at 06:49
  • 1
    B is not dismissed when I add your code and please update your objective c syntax also. remove the comma after true. – Mihir Oza Feb 12 '18 at 11:41
2

I read all the topics and I didn't find a proper answer. If you dismiss B, than C will immediately disappear and create a weird effect. The proper way is to present C as a child view controller with custom animation from the bottom, like:

   [b addChildViewController:c];
    c.view.frame = CGRectOffset(b.view.bounds, 0, b.view.bounds.size.height);
    [b.view addSubview:c.view];
    [c didMoveToParentViewController:b];

    [UIView animateWithDuration:0.5 animations:^{
        c.view.frame = CGRectOffset(c.view.frame, 0, -b.view.bounds.size.height);
    } completion:^(BOOL finished) {

    }];

And then you simply dismiss B and it all looks much nicer!

Rudolf J
  • 515
  • 4
  • 10
1

You only need one dismiss command. Just dismiss B, and then C will go away with it.

Dancreek
  • 9,387
  • 1
  • 29
  • 33
0

Inspired by Albertos solution, I created a delegate method in B with a block to show a result of deleting an account:

#pragma - mark - AddAccountViewControllerDelegate Methods

- (void) dismissToSettings {
    [self dismissModalViewControllerAnimated:NO];
    [self dismissViewControllerAnimated:YES completion:^(void){[DKMessage showMessage:LS(@"Account was successfully created")];}];
}
Denis Kutlubaev
  • 13,009
  • 6
  • 76
  • 67
0

I faced the same problem, and a better solution was creating a "DismissViewProtocol" as follow:

File: DismissViewProtocol.h

@protocol DismissViewProtocol <NSObject>
-(void)dismissView:(id)sender;
@end

In my B-modal view, let's response for the delegate method:

in my b.h file:

#import "DismissViewProtocol.h"
@interface B-Modal : UIViewController <DismissViewProtocol>
...
@end

in my b.m file:

-(void) dismissView:(id)sender
{
 [((UIViewController *) sender) dismissModalViewControllerAnimated:NO];
 [self dismissModalViewControllerAnimated:YES];
}

In the same B-view controller, when I call the Next, in my B modal view, when I call the other modal view C, supposing that for segue:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    ((C-ViewController *)segue.destinationViewController).viewDelegate=self;
}

Finally, in my c.h file, lets prepare for the delegate:

@property(nonatomic, weak) id <DismissViewProtocol> viewDelegate;

And in my c.m file, I just tell my viewDelegate to dismiss my modal view controller and itself:

-(void)closeBothViewControls
{
       [self.viewDelegate dismissView:self];
}

And that's it.

Hope it works for all of you.

Anibal Itriago
  • 1,011
  • 10
  • 13
0

The navigation controller has a "viewControllers" property that is an array - you can set that to a new array minus the two view controllers you want to remove.

Kendall Helmstetter Gelner
  • 73,251
  • 26
  • 123
  • 148
0

Here is a way how to dismiss more that one modal view controller using repeat cycle:

Swift 3

// In this example code will go throw all presenting view controllers and 
// when finds it then dismisses all modals.
var splitViewController: UIViewController? = self

repeat {
    splitViewController = splitViewController?.presentingViewController
} while (!(splitViewController is UISplitViewController) && (splitViewController != nil))

splitViewController?.dismiss(animated: true, completion: nil)
Ramis
  • 8,718
  • 5
  • 56
  • 79
0

I know this answer may feel redundant, but the statement below should make sense and you get an idea of how this works.

Just dismiss the oldest view controller and all other later view controllers go away with this.

In case of 2 view controllers:

Objective C:

[self.presentingViewController dismissViewControllerAnimated:true completion:nil]

Swift:

presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
Shobhit C
  • 808
  • 10
  • 14
0

I found a solution.

You can put these ViewControllers inside an individual NavigationController. And than dismiss the NavigationController will cause all these ViewControllers to dismiss at once.

https://gist.github.com/ufo22940268/2949fdf59c9860292f263ebb1e8036d7

Frank Cheng
  • 5,429
  • 8
  • 45
  • 71