How to dismiss a modal that was presented in a UIStoryboard with a modal segue?

随声附和 提交于 2019-12-30 00:17:09

问题


Setup: I have a storyboard set up, with two simple view controllers A and B. There is a button in A, that transitions to B with a modal segue. B is presented with a modal transition on top of A. It’s fine.

Question: is there a way to pop B away and get back to A with some simple storyboard magic?

Note that if this was all in a navigation controller, and I used a push segue, it would be implicitly be taken care of by navigation controller. There would be a “back” button. There’s nothing comparable for modals, I need to build the UI myself which is fine, but I am wondering if there is a segue mechanic I can use to signal to go back from B to A.

Now the oldskool method to build going back from B to A would be:

  • create a delegate property on B
  • set A to be B's delegate when the modal transition segue plays back (I can hook into this using prepareForSegue:sender: in A’s code)
  • when it’s time to dismiss, B signals to its delegate
  • A implements a delegate method that dismisses B

This works, but feels like too much overhead and silly.

Is there some UIStoryboard mechanic that I have missed, that would basically do a “reverse modal segue”?


回答1:


There isn't any storyboard magic for dismissing a modal view controller without writing at least a little bit of code.

But while you do have to implement some code of your own, you don't necessarily have to go to that much trouble. You can just have a button in view controller B that calls [self dismissViewControllerAnimated:YES completion:nil]. (The docs say the presenting view controller should be the one to dismiss, but they also say that the message will be forwarded to the presenting view controller if called on the presentee. If you want to be more explicit about it -- and you'll need to be in some cases, like when one modal view controller is presented from another -- you can explicitly reference the presenter with self.presentingViewController and call dismiss... from there.)

You see the delegate business in some apps because it's one way of notifying view controller A about whatever the user did while in view controller B... but it's not the only way. There's KVO, notifications, or just plain calling A's methods after referencing it with self.presentingViewController (assuming B knows it's always getting presented by A). And if A doesn't need to know about what happened in B (say, because the user hit a Cancel button), there's no need to do any of that -- you can just dismiss the modal and be done with it.


In iOS 6 and later, unwind segues add another option, providing a little bit of "storyboard magic" for dismissing modal view controllers (or otherwise "backing out" of a sequence of segues). But this approach still requires some code -- you can't set it up entirely in storyboard. On the plus side, though, that code provides a path for getting info from the view controller being dismissed (B) to the one that presented it (A).

Apple has a tech note about unwind segues that covers them in detail, but here's the short version:

  1. Define an IBAction method on the view controller class you want to unwind to -- the one that presents a modal view controller, not the modal view controller itself (view controller A in your question). Unlike normal IBAction methods, these should take a parameter of type UIStoryboardSegue *; e.g.

    - (IBAction)unwindToMainMenu:(UIStoryboardSegue*)sender
    
  2. In the presented view controller (B in the question), wire a control to the green Exit icon, and choose the method you defined.

  3. In your unwind method implementation, you can refer to the segue's sourceViewController to retrieve information from the view controller being dismissed. You don't need to call dismissViewControllerAnimated:completion: because the segue handles dismissing the view controller that's going away.




回答2:


There is storyboard magic to achieve this. It's known as an unwind segue. In A's .h file you implement whatever "target action" style methods you need for however many unwind segues you need. For a modal, it's usually two (cancel and save). So in my A.h file I would add:

// A.h file
- (IBAction)myCancelUnwindSegueCallback:(UIStoryboardSegue *)segue;
- (IBAction)mySaveUnwindSegueCallback:(UIStoryboardSegue *)segue;

Now, in your storyboard, if you have a segue from A to B. You can now do a "target action" style control drag from your cancel/save buttons in B to the green "Exit" icon at the bottom of the B controller in your storyboard. When you do this, Xcode will pick up the two methods we created (since they're in A's header file and they have the correct signature (e.g. IBAction and UIStoryboardSegue *.) and B is the destination of a segue from A) So, there you have it. You have the storyboard magic you were looking for!

In the implementation of the two callbacks, you would have something such as:

// A.m file
- (IBAction)myCancelUnwindSegueCallback:(UIStoryboardSegue *)segue {
    UIViewController *modalGoingAway = segue.sourceViewController;
    // Do something (like get data) from modalGoingAway if you need to...
}

- (IBAction)mySaveUnwindSegueCallback:(UIStoryboardSegue *)segue {
    UIViewController *modalGoingAway = segue.sourceViewController;
    // Do something (like get data) from modalGoingAway if you need to...
}

Lastly, if this approach meets your needs, great. You're done. However, I still wire up the whole protocol delegate/dataSource design pattern if "on cancel" or "on save" I want to perform some operations on B's private properties before passing control over to A to remove B from the view hierarchy.



来源:https://stackoverflow.com/questions/9732499/how-to-dismiss-a-modal-that-was-presented-in-a-uistoryboard-with-a-modal-segue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!