6

I have tried a few answers on this site, but none of them seem to relate to my problem

I have a MasterDetail app which has a two types of segues that I am using. When you press a button on the detail view, it uses a push segue and pushes another detail view onto that. In the new detailview (the one that was just pushed on) there is a button which calls up another UIView (form sheet) using a modal segue.

What I am trying to do is when the user selects a row, a UIAlertView will show up displaying a message, while at the same time (doesn't have to be at the same time) it dismisses the UIViewcontroller (modal) and goes back from the Viewcontroller that was pushed on. Basically, I need to be able to dismiss all viewcontrollers, one modal and one push (nav) so that the view returns to the original main screen that they started with.

I have the UIAlertView working fine and I can get the modal viewcontroller to dismiss by using [self.dismissViewControllerAnimated:YES completion:nil]; but I don't know how to dismiss the next Viewcontroller (which is in a navigation controller). Using this: [self.navigationController popToRootViewControllerAnimated:NO]; does not work.

Here is where I want to call the function to remove all views:

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

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlWithIDAndChallenge];

    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];

    UIAlertView *message = [[UIAlertView alloc] initWithTitle@"Started" message:@"The challenge has begun!" delegate:nil cancelButtonTitle:@"OK!" otherButtonTitles:nil];

    [message show]

    //right here is where I would normally dismiss the modal VC
    //here is where I would like to dismiss all VC

}
CaptJak
  • 3,542
  • 1
  • 25
  • 48

3 Answers3

15

If you want, in iOS 6.0 (and later) projects you can use an unwind segue. For example, you can:

  1. In your top level view controller (the one you want to unwind to, not the controller you're going to unwind from), write an unwind segue method, in this case called unwindToTopLevel (personally, I find it useful if the segue bears some indication as to what the destination of the segue is, for reasons that will become apparent when we get to step 3, below):

    - (IBAction)unwindToTopLevel:(UIStoryboardSegue *)segue
    {
        NSLog(@"%s", __FUNCTION__);
    }
    
  2. In the Interface Builder scene from which you will initiate the unwind, control ⌘-drag from the view controller icon to the exit icon to create the unwind segue:

    enter image description here

    Generally you'd define the segue from a button to the exit outlet (and you're done), but if you want to invoke the segue programmatically, you might want to create it between the controller and the unwind outlet, like shown above.

  3. You'll get a little pop up that includes the name of your unwind segues (from the presenting controllers ... it's like magic):

    enter image description here

  4. If you're going to invoke that segue programmatically, you have to select that unwind segue in the document outline on the left side of the IB window and once you've done that, you can give the unwind segue a storyboard id:

    enter image description here

    I generally use the name of the the unwind segue for the storyboard id, but you can use whatever you want.

  5. Now, having done that, your alert view can invoke the segue:

    - (IBAction)didTouchUpInsideButton:(id)sender
    {
        [[[UIAlertView alloc] initWithTitle:nil message:@"go home" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil] show];
    }
    
    #pragma mark - UIAlertViewDelegate
    
    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
    {
        if (buttonIndex != [alertView cancelButtonIndex])
        {
            [self performSegueWithIdentifier:@"unwindToTopLevel" sender:self];
        }
    }
    
Rob
  • 371,891
  • 67
  • 713
  • 902
  • Thank you for the graphic answer, it helps a lot! If you look at the comment and the edit to my question, you will see that I am not using a UIAlertView function like you demonstrated. Given that, how would I perform the segue? just stick it where I put the comment? – CaptJak Jul 12 '13 at 03:50
  • OK, I stuck it where I put the comment above, and nothing happens. In the log I see this: `-[blahViewController unwindToTopLevel:]`. The IBAction doesn't seem to be connected to anything (the circle next to it is empty). Should this get connected somewhere? – CaptJak Jul 12 '13 at 03:58
  • @CaptJak Given that Alert views run asynchronously, I wouldn't really want to show alert and then start dismissing views while the alert is still shown and before the user has dismissed it (might work, but seems like an odd UI flow). I'd either do it like in my answer (don't segue until after user taps on button in alert), or you could have the `viewDidAppear` of that top level controller show the alert, instead. (you could have some class property that dictated whether the alert would be shown or not, and set it in the `prepareForSegue` in the controller _from_ which you're segueing. – Rob Jul 12 '13 at 04:09
  • @CaptJak I don't know why your insertion of `performSegueWithIdentifier` doesn't work. If you use this `clickedButtonAtIndex` technique, you have to specify the `delegate` for the `UIAlertView` (like I did in my example), but if you don't want to use the delegate, just put the `performSegueWithIdentifier` line in your `didSelectRowAtIndexPath` and it should work. If it's not, I don't know what to say, as it works fine for me (in iOS 6 and above, only). – Rob Jul 12 '13 at 04:26
  • 1
    Well, I think it was a PIBKAC on my side. I put the Unwind IBAction in the wrong VC. This works amazing! You're right about the Alertview though, it did seem at ad uncomfortable when using it, I think I'll change that! Thanks! – CaptJak Jul 12 '13 at 14:17
1

From the class you presented your modal view

call dismiss of modal and then perform selector after some delay and then do the

here is the sample code

//Write this in the class from where you presented a modal View.
//Call the function from modal class when you want to dismiss and go to root.
- (void) dismissAndGoToRoot {
      [self dismissViewControllerAnimated:YES completion:nil];
      [self performSelector:@selector(gotoRoot) withObject:nil afterDelay:0.50];
}

- (void)gotoRoot {

    [self.navigationController popToRootViewControllerAnimated:NO];
}

Here you will call the function

//right here is where I would normally dismiss the modal VC //here is where I would like to dismiss all VC

[self.parentViewController dismissAndGoToRoot];

If this does not work then take an instance variable of ParentViewController in modal class and assign when presenting modal view controller.

Rahul Vyas
  • 26,593
  • 48
  • 175
  • 252
  • All right, I think this code is correct for me. I am not sure how to call it because my UIAlertView (which, when dismissed should call your function) is not it's own function, but is in the `didSelectRowAtIndexPath`. How would I call you function to react when the UIAlert is cleared? I added my `didSelectRowAtIndexPath` to my question – CaptJak Jul 12 '13 at 03:31
0

You could try replacing [self.dismissViewControllerAnimated:YES completion:nil]; with

self dismissViewControllerAnimated:YES completion:^{
    [parentViewController.navigationController popToRootViewControllerAnimated:YES];
}

I believe parentViewController will point to the presenting view controller, and the block will cause the parent view controller's navigation controller to pop to the root view controller.

Jake Spencer
  • 1,117
  • 7
  • 13