I have created a custom UIWebView to present a login page. I am able to get this to show by adding it as a subview. But I would like to present this modally and programatically without the use of a storyboard!
Put it in an otherwise empty view controller, and then call
[self presentViewController: animated: completion:]
in the view controller you're wanting to present it from. When you're done, you can call
[self dismissViewControllerAnimated: completion:]
to get rid of it again. This message can be sent to either the presenter or the presented view controller, though in either case it ultimately is the presenter which responds.
Related
I have an app where I re-init the app if it's been in the background for too long. When the user opens up the app after the allotted time, the re-init happens, and I display the splash screen while I get the data I need. When I get the data from the server I set the window.rootViewController property to a new value. If the old root view controller has had a view controller presented modally, and that view controller was being displayed when the re-init happens, dealloc doesn't get called on the view controller (I've tested this by putting NSLog's in the dealloc method). In the case where a modal view controller was not presented, the dealloc gets called as expected.
Does anyone know of a solution to this? I'm not sure if it's an Apple bug, or if it's something that I need to handle on my own.
The solution that I came up with was before I set the RootViewController, I call
- (void)_dismissRootViewControllersModalViewsIfAny {
UIViewController *rootViewController = self.window.rootViewController;
if (rootViewController.presentedViewController || rootViewController.presentingViewController)
{
[rootViewController dismissViewControllerAnimated:NO completion:nil];
}
}
This is a method that I created, which makes sure that if there is a modal view controller to dismiss, it will be dismissed.
Your modal view is presented by the rootViewController (it's presentingViewController property is set to the rootViewController), that might be the source of your problem.
You can set your rootViewController to an instance of UINavigationController and then just use its setViewControllers:animated: method to display a freshly instantiated view controller instead of switching the window's rootViewController.
I need a loading screen to show while some data is being downloaded from the server. I present a view controller with modal segue without animation. But I don't know how to dismiss the loading screen view controller since dismissViewController function can only be called from inside.
I should be able to dismiss the loading screen view controller from another view controller. Any suggestions?
One option is using NSNotificationCenter. You can post custom notification and listen it in loading view for closing it.
If you are getting the data in parent view then you can close the loading view from parent view also. You can call dismissViewController from parent view using presentingViewController property of UIViewController class.
In your case from parentView you can dismiss the child view using:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
presentingViewController
The view controller that presented this view controller. (read-only)
Declaration
Swift
var presentingViewController: UIViewController? { get }
Objective-C
#property(nonatomic, readonly) UIViewController *presentingViewController
Discussion
When you present a view controller modally (either explicitly or
implicitly) using the presentViewController:animated:completion:
method, the view controller that was presented has this property set
to the view controller that presented it. If the view controller was
not presented modally, but one of its ancestors was, this property
contains the view controller that presented the ancestor. If neither
the current view controller or any of its ancestors were presented
modally, the value in this property is nil. Import Statement
import UIKit Availability
Available in iOS 5.0 and later.
Do you need to show another VC? You could just show a subview, making it visible=true when you start the app, and on the request success or failure callbacks hide it again (depending on the networking framework you are using)
I don't know were you start the request and were you know when it is done, if the loading screen knows when the download is finished you can do one of two things, or you pass the first VC as a delegate (defining a protocol) to the second VC (loading screen), and when you know on the loading screen that the download is finished you call a method on the delegate that will dismiss the loading screen,
Or you can use NSNotificationCenter, register for some kind of events on the first VC and when the process finishes on the loading screen you notify the first VC with this method, to dismiss the loading screen.
If you start the process on the first VC, and you know when it ends also on the first VC I would not understand your question, as you would just dismiss the loading screen and it would work.
Could you give more info? If you'd like I can post some code in order to help you in one of this approaches.
I'm trying to create a small game where the user moves over an object and displays another view controller to display information. Upon pressing a button on the recently presented view controller, the view gets dismissed and shows the view controller I started off with. I've done this like this:
ViewController.m
OtherViewController *other = [self.storyboard instantiateViewControllerWithIdentifier:#"other"];
[self presentViewController:other animated:YES completion:nil];
OtherViewController.m
[self dismissViewControllerAnimated:YES completion:nil];
but when i dismiss it, the user starts from the beginning again. is there a way to save where the user is and continue from there?
You're presenting the new view controller as a modal. When you dismiss it, the previous view controller should be uncovered in it's previous state.
If it's not, then you need to look at the logic of your ViewController class.
You want your one time setup code in your viewDidLoad method. If you have code in your viewWillAppear:animated method, or your viewDidAppear:animated method that resets the state of your view controller then that is the problem.
I have a UINavigationController, in which I push a view controller with a UIModalPresentationPageSheet presentation style.
From within this page sheet's view controller, I present a view controller with UIModalPresentationFormSheet style.
When the user hits a Done button the the form sheet, I want to close out the form sheet and the page sheet.
In the action on the Done button:
-(IBAction)onDone:(id)sender
{
if(self->delegate && [self->delegate respondsToSelector:self->actionSelector])
{
[self->delegate performSelector:self->actionSelector withObject:[NSString stringWithString:self.textView.text]];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
The delegate is the page sheet's view controller, and in the selector, I dismiss the page sheet:
[self dismissViewControllerAnimated:YES completion:nil];
When I run it, I get:
Warning: Attempt to dismiss from view controller <UINavigationController: 0xa9381d0> while a presentation or dismiss is in progress!
I can see why this is happening - because the selector is called before the form view is dismissed, but I don't know the best way around this.
I have tried removing the dismiss in onDone, and call dismiss for both in the selector call (with animated:NO for the form sheet), and it seems to function, but I don't know if this is the way that I should approach fixing it.
Try just calling dismissViewControllerAnimated:completion: on the page sheet. According to Apple's docs:
"If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack."
If that's not exactly the behavior you want, then you should use the completion handler argument in dismissViewControllerAnimated:completion: to pass in a block, then dismiss the other view controller from the completion handler, eg:
[formSheetViewController dismissViewControllerAnimated:YES completion:^{
[pageSheetViewController dismissViewControllerAnimated:YES competion:nil];
}
Although really, I think just dismissing the page sheet should do the trick for you. It's still good to understand how completion handlers work. It lets you do some work after the operation is done--very handy.
In the past my app has had only 1 main view controller (MainViewController) and a login view controller (LoginViewController) but now I am moving to a Tab Bar Controller.
Before I was able to do a simple check viewDidLoad of MainViewController for the existence of a username and password in the key chain. If a username and password was not present I used a segue to pop up a modal login view controller.
With the new setup of using a Tab Bar Controller I still only have 1 view controller (MainViewController) which is the root view controller (as of now) and I am trying to do the same thing where it pops up modal of the login screen.
Now when I call the segue in the viewDidLoad of MainViewController:
[self performSegueWithIdentifier:#"loadLoginView" sender:nil];
I am getting this error:
Warning: Attempt to present <LoginViewController: 0x1757cd80> on <UITabBarController: 0x17571e50> whose view is not in the window hierarchy!
But if I associate a button to a method that loads the LoginViewController by way of a segue it works fine. I am doing that in the MainViewController like this:
-(void)loadLogin
{
[self performSegueWithIdentifier:#"loadLoginView" sender:nil];
}
I can see from the error message that when I try to perform the segue from the viewDidLoad of MainViewController it's trying to load the LoginViewController from UITabBarController.
Why can I not load the LoginViewController from the viewDidLoad of MainViewController?
Any help with this would be great.
Thanks!
It looks like -viewDidLoad is getting called before your view controller stack is added to the window. Two things you could try are:
Delaying until the next time through the run loop (this should give the view controllers time to get in place) [self performSelector:#selector(loadLogin) withObject:self afterDelay:0];. This method won't allow you to call a method with two arguments directly
You could use -presentViewController: animated: completion:. This will cause your login controller to slide in from the bottom.