Dismissing ViewController while presenting another one - ios

I have a timer in my app. If the timer reaches 0, the user is brought to a failure page. When that happens, the previous ViewController was not dismissed, leading to more memory being taken up.
I want the previous ViewController to be dismissed if the Failure Page is presented.
if (self.countdownTimer == 0) {
FailurePage *failurePage = [[FailurePage alloc] init];
[self presentViewController:failurePage animated:YES completion:NULL];
//I want to dismiss the current ViewController when the Failure Page is presented
}

You can do something like this:
if (self.countdownTimer == 0) {
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: nil];
FailurePage *failurePage = (FailurePage*)[mainStoryboard instantiateViewControllerWithIdentifier: #"<Controller ID>"];
[self presentViewController:failurePage animated:YES completion:^{
[self.parentViewController dismissViewControllerAnimated:NO completion:nil]
}];
}

Related

PresentViewController dismiss doesn`t go back to previous viewController

On my project we have three windows A, B and C. From A I would like to push view B, and from B I would like to present view C.
My code:
ViewController A:
ViewControllerB *vcB = [[viewControllerB alloc]
initWithNibName:#"ViewControllerB" bundle:nil];
[[self navigationController] pushViewController:vcB animated:YES];
View Controller B:
ViewControllerC *vcC = [[ViewControllerC alloc]
initWithNibName:#"ViewControllerC" bundle:nil];
[self presentViewController:vcC animated: true completion: nil];
Everything is ok until now, but when I dismiss the last view controller with:
[[self presentingViewController] dismissViewControllerAnimated:NO completion:nil];
The app goes back to first view controller (vcA) instead the second one (vcB)
What am I doing wrong?
Thank you, guys.
You must be doing something else that you're not telling us about...
This works as expected:
In MyFirstViewController.m
- (IBAction)pushTapped:(id)sender {
MyPushedViewController *vc = [[MyPushedViewController alloc] initWithNibName:#"MyPushedViewController" bundle:nil];
[self.navigationController pushViewController:vc animated:YES];
}
In MyPushedViewController.m
- (IBAction)presentTapped:(id)sender {
MyPresentedViewController *vc = [[MyPresentedViewController alloc] initWithNibName:#"MyPresentedViewController" bundle:nil];
[self presentViewController:vc animated:YES completion:nil];
}
In MyPresentedViewController.m
- (IBAction)dismissTapped:(id)sender {
[self dismissViewControllerAnimated:NO completion:nil];
}
Tapping the "dismiss" button in MyPresentedViewController dismisses the presented view controller (your vcC), leaving me at MyPushedViewController (your vcB) ... NOT at MyFirstViewController (your vcA).
try this to dismiss vc
[self dismissViewControllerAnimated:NO completion:nil]
and use this to pop
[self.navigationController popToViewController:controller animated:YES];
[self presentingViewController] = Bvc , so here you actually dismissing B not C
[Bvc dismissViewControllerAnimated:NO completion:nil];
but self = Cvc so here you dismissing C only
[self dismissViewControllerAnimated:NO completion:nil];

Dismiss 2 UIViewController that was loaded via a presentViewController

When the user clicks on a button I am displaying a UIViewController as follows:
NSString * storyboardName = #"Main";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
UIViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"newviews"];
[self presentViewController:vc animated:YES completion:nil];
However, when the vc viewcontroller loads successfully, the user will click on another button and another UIViewcontroller is displayed. Again this too uses a presentViewController: as in the previous code.
Now, there's 2 UIViewController that's being displayed by presentViewController:,
When the user clicks on a button I need these 2 ViewControllers to Dismiss. How can I do this ?
I tried the following code but it did not work.
[self.navigationController popToRootViewControllerAnimated:YES];
You need to use [self.parentViewController dismissViewControllerAnimated:YES] since you presented them modally. You can take advantage of the parentViewController property when dismissing viewControllers.
UIViewController *mainController = [UIViewController new];
UIViewController *vcA = [UIViewController new];
UIViewController *vcB = [UIViewController new];
// User taps button to present vcA
[mainController presentViewController:vcA animated:YES completion:nil];
// User taps button to present vcB
[vcA presentViewController:vcB animated:YES completion:nil];
// Dismiss both vcB and vcA
[vcB.parentViewController dismissViewControllerAnimated:YES completion:^{
[vcA.parentViewController dismissViewControllerAnimated:YES completion:^{
// Now you are back on mainController
}];
}];

Removing ViewController after showing modally a second ViewController

Guys in my app I have some code in the app delegate method application:didFinishLaunchingWithOptions: that determines if the initial View Controller should be the LoginViewController or the MainViewController.
If the LoginViewController is showed first and the user logs in successfully I show the MainViewController modally with this piece of code:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
FSMainViewController *vc = (MainViewController *)[storyBoard instantiateViewControllerWithIdentifier:#"MainViewController"];
vc.loginViewController = self;
[self presentViewController:vc animated:YES completion:nil];
What I want to do next, after the MainController is showed on the screen, is remove the LoginViewController from memory so in the viewWillApper:animated: method of the MainViewController I use this code to remove (or at least try to) the LoginViewController:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.loginViewController) {
[self.loginViewController dismissViewControllerAnimated:NO completion:nil];
}
}
Problem is that this code leads to strange behaviors like the MainViewController being removed from the screen and this error message showing up in the console.
Unbalanced calls to begin/end appearance transitions for <LoginViewController: 0xb06e350>
I also tried calling [self dismissViewControllerAnimated:NO completion:nil] in the completion block of the presentViewController:animated:completion method but still no luck, it didn't work.
What am I doing wrong? How can I remove from memory the underlying LoginViewController when the MainViewController is presented modally?
Don't present your main view controller if you want the login controller to go away, just make it the window's root view controller.
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
FSMainViewController *vc = (MainViewController *)[storyBoard instantiateViewControllerWithIdentifier:#"MainViewController"];
Self.window.rootViewController = VC;
You can't dismissViewController after presenting another one on it or its presentingViewController. At here, you should dismiss LoginViewController first, then present MainViewController.
Otherwise, if you'd like pushViewController, you can call [self.navigationController setViewControllers: animated:] to remove LoginViewController.
If you think presentingViewController is just what you want, try something like this in application:didFinishLaunchingWithOptions:
if (self.loginViewController) { //Define loginViewController in appDelegate.h
[self dismissViewControllerAnimated:NO completion:^{
[self presentViewController:mainViewController animated:YES completion:nil];
}];
}
else{
[self presentViewController:mainViewController animated:YES completion:nil];
}

iOS switch back to background UIViewController

I'm looking for a way to present a modal view over my current UIViewController to basically show a UIActivityIndicator and force users to wait while data is being loaded.
in BaseViewController.m (base class of all my UIViewControllers):
// show loading view
-(void) showLoading
{
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
LoadingViewController *loading = [storyBoard instantiateViewControllerWithIdentifier:#"loadingView"];
loading.view.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:0.7];
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentViewController:loading animated:NO completion:nil];
}
This works great, but how can I go back to the background view after the loading view should be done?
Need a stopLoading method to go back to the original view:
// stop loading
-(void) stopLoading
{
// code here
}
If I try to present a new view after I present the loading view like so:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
UIViewController *view = [storyBoard instantiateViewControllerWithIdentifier:#"loadingView"];
[self presentViewController:view animated:YES completion:nil];
The debugger gives Warning:
Attempt to present PropertyPickerViewController: 0x8af6010 on ViewController: 0x8ab23c0 which is already presenting LoadingViewController: 0x8acf530.
Try:
[self dismissViewControllerAnimated:YES completion:nil];
In fact, I'm not sure that it'a great idea to present new controller with animated gif.
The best option is (imo) show UIActivityIndicator + place a view on top on all other views to prevent user from clicking anything.
You must [self dismissViewControllerAnimated:YES completion:nil] first.
Check the Apple Documentation.

How to present a TabBarController modal

From one view(LoginTesteInicial) I can go to two tabBarControllers, but when I run the code, it crashes with this error:
Attempt to present <UITabBarController: 0x8a4a870> on <LoginTesteInicial: 0x8a46970> whose view is not in the window hierarchy!
here is my code from LoginTesteInicial.m:
UITabBarController *vc;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
vc = [[UIStoryboard storyboardWithName:#"Main_iPad" bundle:nil] instantiateViewControllerWithIdentifier:#"TabBarSemLogin"];
} else {
vc = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"TabBarSemLogin"];
}
[vc setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentViewController:vc animated:YES completion:nil];
The answer to your question is that when -viewDidLoad is called, the view controller's view is not in the view hierarchy. You need to wait until the view is placed into the view hierarchy. This can been done either in -viewWillAppear: or -viewDidAppear:.
The "Unbalanced calls to begin/end appearance transitions" warning you get is because a view controller has not be fully loaded before being replaced with another view controller. To avoid that warning, you can use -performSelector:withObject:afterDelay: to schedule the present view controller in the next run loop.
- (void)viewDidAppear:(BOOL)animated
{
…
[self performSelector:#selector(showTabBarController) withObject:nil afterDelay:0.0];
}
- (void)showTabBarController
{
UITabBarController *vc;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
vc = [[UIStoryboard storyboardWithName:#"Main_iPad" bundle:nil] instantiateViewControllerWithIdentifier:#"TabBarSemLogin"];
} else {
vc = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"TabBarSemLogin"];
}
[vc setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentViewController:vc animated:YES completion:nil];
}
Move [self presentViewController:vc animated:YES completion:nil] and related code it either of these
1 viewWillAppear
2 viewWillLayoutSubviews
3 viewDidLayoutSubviews
You will need to keep one flag which will take care that even if one of these method triggered multiple times your view will get presented only once.
Or you can avoid presenting view and add as subview, do this instead of presenting if you didn't like keeping flag etc.
[self.view addSubview:vc.view];
[self addChildViewController:vc];
[vc didMoveToParentViewController:self]

Resources