Dismiss all UIViewController - iphone

I have app with 3 UIViewContoller
UIViewContollerA
UIViewContollerB
UIViewContollerC
UIViewContollerA open UIViewContollerB with presentModalViewController
UIViewContollerB open UIViewContollerC with presentModalViewController
And i want to have the possible that in one click on button in UIViewContollerC to dismiss all The UIViewController.
I try to call dismissModalViewControllerAnimated twice but it won't work, there is any other option to do it?
it's possible?

I would take a look at unwind segues. They allow you to unwind to any previous segue. The answers in this question are very helpful in describing how to use them: What are Unwind segues for and how do you use them?
Also as mentioned by Michael, modals weren't designed to be placed one on top of the other. I've found they work great for forms, but not much else. I would suggest that you use a UINavigation Controller as well. This will allow you to easily navigate back to the previous controller. Then you could also add a button for an unwind segue back to the initial view controller that you want.

You probably need some sort of chaining set of calls back up the modal stack to do this.
However, given you have a stack of view controllers, a UINavigationController might be a simpler solution to both presenting these view controllers in the first place and dismissing them down in one go.

Of course calling dismissModalViewControllerAnimated won't work, it's intended to be called on different view controllers. From the docs:
Dismisses the view controller that was presented by the receiver
So you'll need to set up delegation between the controllers in order to pass a request to dismiss the controller backwards to the initial controller.
Keep in mind that:
There's probably a better way of doing whatever it is you're trying to do. Modal controllers weren't intended to be presented on top of each other.
You're using deprecated methods. Use
presentViewController and dismissViewController instead

you would pass a reference of UIViewContollerB in UIViewContollerC, like this
#property(nonatomic, assign) UIViewContollerB *refB;
and call dismissModalViewControllerAnimated in UIViewContollerC to dismiss it, and fire dismiss action of UIViewContollerB in viewWillDisappear
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.refB dismissModalViewControllerAnimated:YES]
}

What you could try is to use dismissModalViewControllerAnimated with animated:NO. On the top most one and animated:YES on the controller below.
Or perhaps it workd, if you just call dismiss on the rootController?

Related

Dismissing a Presented View Controller

I have a theoretic question. Now İ'm reading Apple's ViewController guide.
They wrote:
When it comes time to dismiss a presented view controller, the
preferred approach is to let the presenting view controller dismiss
it. In other words, whenever possible, the same view controller that
presented the view controller should also take responsibility for
dismissing it. Although there are several techniques for notifying the
presenting view controller that its presented view controller should
be dismissed, the preferred technique is delegation.
But I can't explain, why I have to create a protocol in presented VC and add delegate varible, create delegate method in presenting VC for dismissing presented VC, instead of a simple call in presented view controller method
[self dismissViewControllerAnimated:NO completion:nil]?
Why is the first choice better? Why does Apple recommend it?
I think Apple are covering their backs a little here for a potentially kludgy piece of API.
[self dismissViewControllerAnimated:NO completion:nil]
Is actually a bit of a fiddle. Although you can - legitimately - call this on the presented view controller, all it does is forward the message on to the presenting view controller. If you want to do anything over and above just dismissing the VC, you will need to know this, and you need to treat it much the same way as a delegate method - as that's pretty much what it is, a baked-in somewhat inflexible delegate method.
Perhaps they've come across loads of bad code by people not really understanding how this is put together, hence their caution.
But of course, if all you need to do is dismiss the thing, go ahead.
My own approach is a compromise, at least it reminds me what is going on:
[[self presentingViewController] dismissViewControllerAnimated:NO completion:nil]
[Swift]
self.presentingViewController?.dismiss(animated: false, completion:nil)
Updated for Swift 3
I came here just wanting to dismiss the current (presented) View Controller. I'm making this answer for anyone coming here with the same purpose.
Navigation Controller
If you are using a navigation controller, then it is quite easy.
Go back to the previous view controller:
// Swift
self.navigationController?.popViewController(animated: true)
// Objective-C
[self.navigationController popViewControllerAnimated:YES];
Go back to the root view controller:
// Swift
self.navigationController?.popToRootViewController(animated: true)
// Objective-C
[self.navigationController popToRootViewControllerAnimated:YES];
(Thanks to this answer for the Objective-C.)
Modal View Controller
When a View Controller is presented modally, you can dismiss it (from the second view controller) by calling
// Swift
self.dismiss(animated: true, completion: nil)
// Objective-C
[self dismissViewControllerAnimated:YES completion:nil];
The documentation says,
The presenting view controller is responsible for dismissing the view
controller it presented. If you call this method on the presented view
controller itself, UIKit asks the presenting view controller to handle
the dismissal.
So it works for the presented view controller to call it on itself. Here is a full example.
Delegates
The OP's question was about the complexity of using delegates to dismiss a view.
This Objective-C answer goes into it quite a bit.
Here is a Swift example.
To this point I have not needed to use delegates since I usually have a navigation controller or modal view controllers, but if I do need to use the delegate pattern in the future, I will add an update.
This is for view controller reusability.
Your view controller shouldn't care if it is being presented as a modal, pushed on a navigation controller, or whatever. If your view controller dismisses itself, then you're assuming it is being presented modally. You won't be able to push that view controller onto a navigation controller.
By implementing a protocol, you let the parent view controller decide how it should be presented/pushed and dismissed/popped.
try this:
[self dismissViewControllerAnimated:true completion:nil];
In my experience, it comes in handy when you need to dismiss it from any ViewController you want and perform different tasks for each viewcontroller that dismisses it. Any viewController that adopts the protocol can dismiss the view in it's own way. (ipad vs iphone, or passing different data when dismissing from different views, calling different methods when dismissing, etc..)
Edit:
So, to clarify, if all you ever want to do is dismiss the view, I see no need to setup the delegate protocol. If you need to do different things after you dismiss it from different presenting view controllers, It would be your best way to go using the delegate.
Swift 3.0
//Dismiss View Controller in swift
self.navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
Quote from View Controller Programming Guide, "How View Controllers Present Other View Controllers".
Each view controller in a chain of presented view controllers has
pointers to the other objects surrounding it in the chain. In other
words, a presented view controller that presents another view
controller has valid objects in both its presentingViewController and
presentedViewController properties. You can use these relationships to
trace through the chain of view controllers as needed. For example, if
the user cancels the current operation, you can remove all objects in
the chain by dismissing the first presented view controller.
Dismissing a view controller dismisses not only that view controller
but also any view controllers it presented.
So on one hand it makes for a nice balanced design, good de-coupling, etc... But on the other hand it's very practical, because you can quickly get back to a certain point in navigation.
Although, I personally would rather use unwinding segues than try to traverse backwards the presenting view controllers tree, which is what Apple talks about in this chapter where the quote is from.
One point is that this is a good coding approach. It satisfies many OOP principles, eg., SRP, Separation of concerns etc.
So, the view controller presenting the view should be the one dismissing it.
Like, a real estate company who gives a house on rent should be the authority to take it back.
In addition to Michael Enriquez's answer, I can think of one other reason why this may be a good way to protect yourself from an undetermined state:
Say ViewControllerA presents ViewControllerB modally. But, since you may not have written the code for ViewControllerA you aren't aware of the lifecycle of ViewControllerA. It may dismiss 5 seconds (say) after presenting your view controller, ViewControllerB.
In this case, if you were simply using dismissViewController from ViewControllerB to dismiss itself, you would end up in an undefined state--perhaps not a crash or a black screen but an undefined state from your point of view.
If, instead, you were using the delegate pattern, you would be aware of the state of ViewControllerB and you can program for a case like the one I described.
Swift
let rootViewController:UIViewController = (UIApplication.shared.keyWindow?.rootViewController)!
if (rootViewController.presentedViewController != nil) {
rootViewController.dismiss(animated: true, completion: {
//completion block.
})
}
I like this one:
(viewController.navigationController?.presentingViewController
?? viewController.presentingViewController
?? viewController).dismiss(animated: true)
If you are using modal use view dismiss.
[self dismissViewControllerAnimated:NO completion:nil];
This is a lot of baloney. Delegation is fine when it is needed but if it makes the code more complex -- and it does -- then there needs to be a reason for it.
I'm sure Apple has its reasons. But it is clearer and more concise to simply have the presented VC do the dismiss unless there is a true reason for doing otherwise and no one here as of today has presented one that I can see.
Protocols are excellent when they're needed but object oriented design was never about having modules communicating unnecessarily with each other.
Tom Love (co-developer of Objective C) once commented that Objective C was "elegant", "small", "crisp" and "well-defined" (when comparing with C++). Easy for him to say. Delegation is a useful feature that seems to have been over-used "just because", and while I like working in the language, I dread the idea of felling compelled to use unnecessary syntax to make things more complex than they have to be.
Focusing on the question's title only, this is the right answer.
presentedViewController?.dismiss(animated: true)

ViewDidAppear is not called in tab bar based app

I have a problem. I am working on an app which is tab bar based app. In this app, we call [self.view addSubview:newVC.view] when we want to navigate to a new view. newVC is the view controller of the new view that we want to display. Also we use [self.view removeFromSuperview] when we want to go back to previous view.
So in other words, there is no navigation controller. Now problem is that I want to update the previous view. Since we are using [self.view removeFromSuperview], viewDidAppear of the previous view is not get called and so we have no way of refreshing that view.
I know the approach that we used has flaw but since its a large scale app and changing it to implement navigation controller with take lot of time so I need you to please help me find the solution of this problem. How can I call the viewDidLoad or viewDidAppear or the previous view on calling [self.view removeFromSuperview] from its subview?
Yes, as Sarah said you should hold a reference to previous controller in "stack".
And when "poping" controller from stack, call appropriate method on previous controller.
Certainly you should not call viewDidLoad (it is not called when you pop controller from navigation stack of real UINavigationController).
You can call viewWillAppear or viewDidAppear, but better use your own method, like viewRevealed (you can also call it from viewWillAppear or viewDidAppear). It is useful to make
base class where implement all this functionality and derive all you controller from the base class. It may look like:
- (void) pushViewController:(BaseViewController *)baseController{
[self.view addSubview:baseController.view];
baseController.parentController = self;
}
- (void) pop{
[self.view removeFromSuperview];
[self.parentController viewRevealed];
}
viewDidLoad method call only when when you jump into a controller through pushViewController method. If you call removeFromSupreView, it will call viewWillAppear method. Here if you want to navigate from one view to another view over tabbar you must use UINavigationController in Mainwindow.xib and connect its viewController with App delegate.

Dismiss some views

First, sorry about my English, I know it's bad, but I'm trying to make it better...
Here is the issue: I'm making an app, with a view controller, with a lot of views... I want to make a button to go back to the main one. I think I have to dismiss the views I pushed. I dont know if that is necessary or if I can push it again directly. If it is, how can I do that? I've tried to put a dismiss method after the presentModalViewController one, but it didn't work.
Any help?
Thank you so much for your help ;)
It hard to tell from your question, but you may be confused by the navigation stack and the modal controllers.
A modal controller gets presented and dismissed using:
presentModalViewController:animated:
dismissModalViewControllerAnimated:
Navigation controllers are pushed and popped using:
pushViewController:animated:
popViewControllerAnimated:
So make sure you are using the correct method to pop or dismiss the view in question.
To present and dismiss a modal view you'll have to use delegation.
Here is an explanation for how to present and dismiss modal views.
Modal views are handled differently than views controlled by the UINavigationController or UITabBarController, which just push them onto a view stack or array of views.

Adding/removing viewcontrollers view

If I instantiate a lets say, UISegmentedControl. And link each button to add a viewcontrollers view to the viewhierachy, would it be enough to just add that view to the stack or must I call other methods aswell. And what about removing views? Would it be sufficient to just say
I do NOT have the correct syntax in front of me right now so bear with me.
[self removeSuperview];
Is it necassary to call methods on the viewcontroller itself?
Use a NavigationController and either push or present Modally the views. The you can just pop or dismiss them.

popToRootViewController - notification

I have a tabBar controller. Tapping the active tab, by default, calls popToRootViewController on that tab's navigation controller. That is what I want, but I also need to do some customization when this happens. The view controller in question has a nav controller, but is not a subclass of UINavigationController. How can I listen for when popToRootViewController occurs and take some action?
You can use UINavigationControllerDelegate's method:
– navigationController:didShowViewController:animated:
and check if the shown controller is the controller you want.
Hopes this helps
(I remember using this in iOS 2.x and it was a little bit buggy, I wonder if is ok now. It should be since it's 4 already)
You can put you code inside the method – tabBarController:didSelectViewController: of the UITabBarController delegate, or maybe inside the UIViewController's - viewWillAppear:animated: method.
Note:
If you have added your Tabbarcontroller in the navigation stack,
-(void)viewWillAppear:(BOOL)animated
would not be called in iOS 4.2 or below, I wasted so many days to figure out this.