dismissViewController: not working
Asked Answered
C

3

7

I have a view controller called vc0 which is presented like this:

[self presentViewController: vc1 animated: YES completion: nil];

And in vc1 I have a button to present another view controller:

[self presentViewController: vc2 animated: YES completion: nil];

Then in vc2, I have a button to dismiss the view controller:

[self dismissViewControllerAnimated:YES completion: ^{
// over here I call one method in vc1
}

And as expected it returns back to vc1.. however there is a button in vc1 to go back to vc0 by dismissing the view controller like this:

    [self dismissViewControllerAnimated:YES completion:nil];

But for some reason it doesn't work, the view controller does not get dismissed back to vc0. When I first present vc1, I can press the button to dismiss the view controller and it works. But when I press the button to open vc2, and when I dismiss vc2 back to vc1, and THEN I press the button to dismiss the view controller, that is when it doesn't work.

Sorry if the question is a bit unclear, it is a bit hard to phrase what I am trying to say.

Also one more thing:

I tried replacing dismissViewControllerAnimated: in vc1 to manually present vc0, but then I get a log in the console saying that I am trying to present a vc0 but vc1's view is not in the window hierarchy. What does this mean?

Thanks for help!

UPDATE:

IN THIS CASE VC0 IS MenuMileIndexViewController - VC1 IS FlightViewController - VC2 IS BookmarksTableViewController

Here is code involved:

MenuMileIndexViewController:

- (IBAction)goToOriginPage {

FlightRecorder *origin = [[FlightRecorder alloc] init];
[self presentViewController:origin animated:YES completion:nil];

}

Flight Recorder:

    - (void)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar {

        [self bringUpBookmarkkTable];
}

- (void) bringUpBookmarkkTable {

    BookmarkTableViewController *bookmarkTVC = [[BookmarkTableViewController alloc] init];

    [bookmarkTVC setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal];

    [self presentViewController:bookmarkTVC animated:YES completion:nil];
}

- (IBAction)cancel {

[self dismissViewControllerAnimated:YES completion:nil];

}

- (void)endBookmarkProcessWithBookmarkCollection: (NSDictionary *)dict {

    presetBookmarkContext = [dict mutableCopy];

    bookmarkMode = YES;

    NSString *compiledText = nil;

    NSNumber *number1 = [NSNumber numberWithInt: 1];

    if ([dict objectForKey: @"bookmarkTag"] == number1) {

        compiledText = [NSString stringWithFormat: @"%@ to %@", [dict objectForKey: @"origin"], [dict objectForKey: @"destination"]];
    }
    else {

        compiledText = [NSString stringWithFormat: @"%@ to %@", [dict objectForKey: @"destination"], [dict objectForKey: @"origin"]];
    }

    compiledText = [compiledText stringByReplacingOccurrencesOfString:@"Origin: " withString:@""];

    compiledText = [compiledText stringByReplacingOccurrencesOfString:@"Destination: " withString:@""];

    flightContext = [NSDictionary dictionaryWithObjectsAndKeys: [dict objectForKey: @"miles"], @"miles", compiledText, @"location", [[NSUserDefaults standardUserDefaults] objectForKey: @"tempD"], @"date", nil];

    NSString *string = [NSString stringWithFormat: @"\nMiles: %.2f\nFlight: %@\nDate: %@", [[dict objectForKey: @"miles"] floatValue], compiledText, [[NSUserDefaults standardUserDefaults] objectForKey:@"tempD"]];

    UIAlertView *bvkBookmarkAlertView = [[UIAlertView alloc] initWithTitle:@"Confirmation" message:string delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Add", nil];

    [bvkBookmarkAlertView show];
}



 - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {

    if (buttonIndex == 1) {

        [self cancel]; // Even though cancel is an IBAction, IBAction is the same thing as void so it is callable
    }
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {

    if (buttonIndex == 1) {

        [TheMileIndexViewController addDesiredMilesToIndex: [[flightContext objectForKey: @"miles"] doubleValue]];

        [TravelLogViewController addFlight: flightContext];

        if (!bookmarkMode) {

            if ([checkbox isSelected]) {

                [BookmarkHandler uploadBookmark: bookmarkFlightContext];
            }    
        }
    }

    if (buttonIndex == 0) {

        if ([alertView.title isEqualToString: @"Confirmation"]) {

            bookmarkMode = NO;
        }
    }

}

BookmarksTableViewController:

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    [tableView deselectRowAtIndexPath:indexPath animated: YES];

    NSDictionary *dict = [[BookmarkHandler bookmarkCollection] objectAtIndex: indexPath.row];

    fl = [[FlightRecorder alloc] init];

    [self dismissViewControllerAnimated:YES completion:^{

        [fl endBookmarkProcessWithBookmarkCollection: dict];
    }];
}

NOW, I have created a screen recording of the app in simulator showing what is the problem. I can email that to you for reference. So I can email that to you.

Capillaceous answered 29/9, 2012 at 4:10 Comment(2)
how and where are you setting the action of your button?Greenfield
1. "What does this mean?": It s because you are creating a new instance of vc0 by "manually present vc0". 2. What do you do with "// over here I call one method in vc1"?Selectee
B
6

I would recommend to always dismiss a VC from the VC that actually presented it - using a delegate. This is actually also the Apple recommended way - as was pointed out in a previous answer to my question regarding this issue.

So if you have VC0 presenting VC1, have the dismiss VC1 code in VC0 too, using delegate scheme.

I have learned that this is the savest way to handle presenting and dismissing - even though sometimes it works to dismiss VC1 within VC1 itself.

I have asked a very related question, you might be interested to check this too. It shows the code...

ps I also read that some dismiss only VC1 - which in turn will also dismiss VC2. However, if my previous suggestion works, I would not do this. Sometimes I get information during execution (no crash) that the VC does not exist anymore or anything related to that - so I assumed this is not the best solution. But if my prvious suggestion does not work, you may try the second one.

Though no guarantee that this will last the updates to new iOS, since this issue keeps hauting me for several iOS updates now :-), so I decided to go the standard recommended route.

EDIT: This is my modified code - it works without problems - however it is not clear what you intend to happen AFTER the user reponds to the Alert and whether the Alert should be on VC1 or VC0. Anyway using delegate and callbacks I do not see any issue. Please explain should I have missed your point...

FlightViewControllerProtocol.h

@protocol FlightViewControllerProtocol <NSObject>
-(void) dismissVCAndEndBookmark;
@end

FlightViewController.m

#import "FlightViewController.h"
#import "FlightViewControllerProtocol.h"
#import "BookmarksTableViewController.h"
@interface FlightViewController ()

@end

@implementation FlightViewController
@synthesize delegate;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar {

    [self bringUpBookmarkkTable];
}

- (IBAction) bringUpBookmarkkTable {

    BookmarksTableViewController *bookmarkTVC = [[BookmarksTableViewController alloc] init];
    bookmarkTVC.delegate = self;
    [bookmarkTVC setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal];

    [self presentViewController:bookmarkTVC animated:YES completion:nil];
}

- (IBAction)cancel {

    [self dismissViewControllerAnimated:YES completion:nil];

}

- (void)endBookmarkProcessWithBookmarkCollection: (NSDictionary *)dict {

//    presetBookmarkContext = [dict mutableCopy];

//    bookmarkMode = YES;

    NSString *compiledText = nil;

    NSNumber *number1 = [NSNumber numberWithInt: 1];

    if ([dict objectForKey: @"bookmarkTag"] == number1) {

        compiledText = @"Text1";
    }
    else {
        compiledText = @"Text2";
    }


//    flightContext = [NSDictionary dictionaryWithObjectsAndKeys: [dict objectForKey: @"miles"], @"miles", compiledText, @"location", [[NSUserDefaults standardUserDefaults] objectForKey: @"tempD"], @"date", nil];

    NSString *string = compiledText;

    UIAlertView *bvkBookmarkAlertView = [[UIAlertView alloc] initWithTitle:@"Confirmation" message:string delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Add", nil];

    [bvkBookmarkAlertView show];
}


- (void) dismissVCAndEndBookmark {
    [self dismissViewControllerAnimated:YES completion:nil];
     [self endBookmarkProcessWithBookmarkCollection: nil];
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {

    if (buttonIndex == 1) {

        [self cancel]; // Even though cancel is an IBAction, IBAction is the same thing as void so it is callable
    }
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {

    if (buttonIndex == 1) {

        NSLog(@"alertView1");
           }

    if (buttonIndex == 0) {
        NSLog(@"alertView2");

    }

}

@end

BookmarksTableViewController.h

 @interface BookmarksTableViewController : UIViewController
{
    id delegate;
}

@property (nonatomic,strong)   id delegate;

@end

BookmarksTableViewController.m

- (IBAction)goBack {
    [self.delegate dismissVCAndEndBookmark];
}

Esp the callback in BookmarksTableViewController.m seems to be the main issue in your implementation if I understood your intentions correctly.

Backspace answered 29/9, 2012 at 6:35 Comment(12)
The answer to that question, and as you quote 'the good old way of doing it' is what I am doing... but why doesn't it workCapillaceous
you are doing: [self dismissViewControllerAnimated:YES completion: ^{ // over here I call one method in vc1 } in VC2 not sth like [delegate dismissVC] where this would execute in VC1. Also try animation:NO - but please make sure you do dismiss VC2 from within VC1 and dismiss VC1 from within VC0Backspace
Why should I use a delegate though? Apple has provided me with a completion block.... I will try animation NOCapillaceous
I have not used completion blocks for this since somewhere I read that the order of execution, ie the FINISHED dismissed VCs is not guaranteed to be really finished before the code in the block is called. Probably this is also true in your case - except for the use of delegates I am out of ideas :-(. ps also try your app in iOS release code version, since this behaves slightly different from debug version code - it executes faster and therefore causes probably a different order of execution (as it did in my case too)Backspace
Actually, I tested this out. I put a NSLog in viewDidLoad and the method called in vc1 by the completion block in vc2 and viewDidLoad is called firstCapillaceous
If you can post the complete code involving this issue I don't mind testing it out myself....Backspace
Ok, I just figured out something. Where I am doing this dismissing code is in an alert view delegate method, the alert view is created and displayed in the method that is called in the completion block of vc2. The problem is with my alert view, because when I press the back button it works, just that alert view does not work. I can demonstrate this to you by screen recording it and emailing it to you...Capillaceous
If possible (I know it might be too much effort though) could you just post the code involved, so I can play around? I am not sure that a screen recording would help sufficiently - since I would still like to try a delegate solution - without completion blocks. Also just came to my mind, you could try some kind of delay or deleayed code so the VC in question surely finishes to be dismissed BEFORE your subsequent code executes - either a dely or better a perform.Backspace
First thing that appears strange is that you create a brand new fl in BookmarksTableViewController which should run endBookmarkProcessWithBookmarkCollection. But this is a DIFFERENT VC than the one that presented it! I would expect that you pass the fl as a delegate and use it when you want to go back... Anyway I will implement it this way using delegate and see... -> please check my EDIT to the answerBackspace
You have got it wrong. The alert view should be the one dismissing the Flight Recorder (FlightViewController). Why you are commenting out my code (flightContext = and bookmarkMode = YES etc)Capillaceous
And if delegate is an id how can I send a Flight Recorder message to itCapillaceous
let us continue this discussion in chatBackspace
S
6
[self.navigationController popViewControllerAnimated:YES];

did the trick for me also. In my case, I had a (iPhone) viewController that was pushed on to the stack using a push segue. Since the viewController that was initiating the segue had a navigation bar, I had to send the parent controller's navigationController the popViewControllerAnimated message instead of calling it's own dismissViewControllerAnimated:completion message.

Ray

Star answered 3/4, 2013 at 18:20 Comment(2)
I always thought popViewController: was to return if you have used pushViewController: in which case you must have a navigation controller to do all this - which I don'tCapillaceous
Yes. Apologies that I missed that specific detail from your original post. I was trying to point out that with segues, you may unknowingly be "under" a NavigationController, so pop may be the method instead of dismiss.Star
S
0

First View:

Firstly embed in navigation controller in first view. and use this code to navigate to another view

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle: nil]; 
FirstView *rvc = [storyboard instantiateViewControllerWithIdentifier:@"apps"];
[self.navigationController pushViewController:rvc animated:YES];

Second view:

Add this line in the method for dismiss view

- (IBAction)back{
    [self.navigationController popViewControllerAnimated:YES];
}
Shrievalty answered 29/9, 2012 at 7:41 Comment(2)
Why do I need to create a navigation controller?Capillaceous
@MCKapur, it's considered best practice to have an object manage navigation instead of manually trying to manage each part.Errantry

© 2022 - 2024 — McMap. All rights reserved.