I am trying to present a modal view controller C from view controller B. B's view does not cover the full screen, and is a subview of another view controller, A. What I am seeing when I try to present a fullscreen modal is the modal is covering the full screen, but when I tap on certain places in the screen the control will get 'passed through' to A's view.
I can bypass this by presenting the modal from A via some kind of delegation, but I have no idea why this is happening! After all, if you have a tab bar controller managing one of your views, and you try to present a modal view, it covers the full screen just fine. Is there some magic going on behind the scenes?
I don't think there is any official documentation on how the modal is implemented, but any view can get the UIWindow of the UIApplication and call -presentModal... on the rootViewController property. This will have the affect of making your modal full screen. I'm sure there are other ways of achieving the same effect though.
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentModalViewController:myModalVC animated:YES];
In that scenario, you need to implement your own 'modal' methods for all your view controllers, using addSubview: and bringSubviewToFront:. I've done this in one of my larger project where I wanted some different behavior from the modal views.
This worked for me:
UIViewController* vc = [[[UIViewController alloc] init] autorelease];
UIWindow* keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView* topView = [[keyWindow subviews] objectAtIndex:[[keyWindow subviews] count] - 1];
vc.view = topView;
[vc presentModelViewController...
Basically, you create a new UIViewController and attach it to the topmost view on the main window (which is the view the user currently sees).
Simple, but not sure about acceptance and efficiency. This is what I use.
Embed all the view controllers in a mainView below the rootView.
Add any modal views to the rootView while disabling userInteractionEnabled flag for the mainView. The modal view should be added to rootView and not mainView
Once done, re-enable the user interaction for mainView.
Hope this helps...
Related
I am having a strange problem that I can't seem to find the cause for.
When attempting to present a modal view controller on a navigation controller the navigation controller is popping all of my view controllers underneath when the modal is dismissed.
So after pushing a few view controllers and presenting a modal on the topViewController, I end up back at the rootViewController when the modal is dismissed.
Anyone had this happen to them lately, I can't seem to find the reasoning for why this is happening?
This answer is for #rshev:
It was actually a user error. Here's what was happening: I had a view controller with a manually added navigationController on top of it (as a subview/child VC). The nav controller then had 3 VCs in its stack. The third (and visible) VC was presenting an image picker controller. When the image picker was dismissed, I momentarily saw my third VC , then it quickly popped back to the 1st, discarding the other two VC's from memory.
So what went wrong? What I didn't realize is that viewDidAppear (and viewWillAppear) was being called on my content view controller (the one with nav controller for its subview). This content VC was actually setting its navigation controller (and adding it as a subview) on viewDidAppear, thus covering up the original nav controller.
To solve it, I just added a static boolean to determine when the first VC FIRST appears, like so:
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
static BOOL firstAppearance = YES;
if (firstAppearance)
{
firstAppearance = NO;
UINavigationController *navController = [self.storyboard instantiateViewControllerWithIdentifier:#"NavigationController"];
[navController.view setFrame:self.view.bounds];
[self.view addSubview:navController.view];
[self addChildViewController:navController];
[navController didMoveToParentViewController:self];
}
}
Hope that helps.
Im starting to learn OC.
The first question is about _window.rootviewcontroller and [_window addSubview:...]
Both of the two ways can set view for UIWindow (actually, UIWindow is inherited from UIView).
So what I want to know is :
Is setting the rootviewcontroller for window just using the addSubview method to implement , or it's something else?
more exactly:
is
_window.rootviewcontroller = viewController;
meaning
[_window addSubview: viewController.view];
or
_window = viewController.view; //UIWindow : UIView
or something else?
Thanks a lot.
Is there anyone who can tell me some details about UIWindow and the rootViewController property?
If you use addSubview: to have to pass a UIView instance but when you call rootviewcontroller you passing UIViewController instance to the UIWindow.
You can use addSubview but you have to associate UIView superview (Which needs to be UIViewController) to the UIWindow, to make it behave the same,
something like that (old way to to that:
[window addSubview:myViewController.view];
[window makeKeyAndVisible];
By using rootviewcontroller it will do it for you.
This is taken from Apple:
The root view controller provides the content view of the window.
Assigning a view controller to this property (either programmatically
or using Interface Builder) installs the view controller’s view as the
content view of the window. If the window has an existing view
hierarchy, the old views are removed before the new ones are
installed.
Obviously not. The root view controller is generally assigned to window in appdelegate class.
Also, root view controller is always associated with UINavigationController. So that any root view controller of a UINavigationController will be a its content view controller.
Where as, add subview is just a method of UIView class. Which helps to add any subview to the respective view.
I am updating our app to be compiled with xcode6/iOS8.
One issue I am running into is that when a modal view is presented. the underlying subview is removed. It is completely blacked out.. until the modal view is dismissed.. then it re-appears.
Has anyone run into this with iOS8? The same code has worked since iOS4.
Code:
PigDetailViewController *pigDetailViewController = [[PigDetailViewController alloc] initWithNibName:#"PigDetailViewController" bundle:nil];
self.navigationController.modalPresentationStyle = UIModalPresentationCurrentContext;
self.navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:pigDetailViewController animated:YES completion:nil];
In iOS 8 they've added a new presentation style that behaves like UIModalPresentationCurrentContext in the circumstance you've described, it's UIModalPresentationOverCurrentContext. The catch here is that unlike with UIModalPresentationCurrentContext, you want to set the view controller to be PRESENTED with this presentation style, like so:
PigDetailViewController *pigDetailViewController = [[PigDetailViewController alloc] initWithNibName:#"PigDetailViewController" bundle:nil];
pigDetailViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:pigDetailViewController animated:YES completion:nil];
Note that to support iOS 7 and below you'll likely need to check the OS version and decide how to present the view controller based on that.
edit: I'd like to add one more note to this. In iOS7 with UIModalPresentationCurrentContext, when the presented VC was dismissed, the underlying VC had its viewDidAppear method called. In iOS8 with UIModalPresentationOverCurrentContext, I've found the underlying VC does not have its viewDidAppear method called when the VC presented over top of it is dismissed.
Adding a point to BrennanR's answer.. even viewWillAppear doesn't call when the VC presented over top of it is dismissed.
I think you are misunderstanding how a modal view controller works.
When you present a view controller modally it will control the entire screen. It has an opaque background (normally black) and then draws its view on top of that.
So, if you set the view.backgroundColor to yellow (for example) it will have a yellow background. If you set it to clear then it will show through to the black background.
What I think you want is for the other view to "show through" so it looks like the modal view is sitting on top of it.
The best way I have found of doing this is to use this method...
// in the view controller that is presenting the modal VC
modalVC = // the modal VC that you will be presenting
UIView *snapshotView = [self.view snapshotViewAfterScreenUpdates:NO];
[modalVC.view insertSubView:snapshotView atIndex:0];
// present the modal VC
This will take a "screenshot" of the current view hierarchy and then place that snapshot underneath everything in the modal VC.
That way your black screen will be replaced by a screenshot of the previous view controller.
thanks for your time,here's my app's structure:It contains a MainController,and a LoginController, when user lanuch the app, first thing is to judge the login state,if it's yes, then set childController(aka.MainController) to RootViewController's (aka,NavigationController), else set childController(aka.LoginController) to RootViewController's (aka,NavigationController), when user input the username and password correctly,set childController(aka.MainController) to RootViewController's (aka,NavigationController)
When user click the quit button in the setting controller which belongs to MainController,then:
LoginController *vcLogin = [[LoginController alloc] init];
UINavigationController *ncRoot = [[UINavigationController alloc] initWithRootViewController:vcLogin];
[[UIApplication sharedApplication].keyWindow setRootViewController:ncRoot];
Now, the problem appears, console printed:
attempt to dismiss modal view controller whose view does not currently appear.
self = <_UIModalItemAppViewController: 0x9da0060> modalViewController =<_UIModalItemsPresentingViewController: 0x9e9bac0>
You know where the wrong-thing is?
This is not the way to set a new UIViewController. You should use presentViewController to present a modal view controller. If you want to use pushViewController without animation that's also possible. But it's a bad idea to change the rootViewController. Since that's not how the ViewController hierarchy is set up in iOS.
I am trying to display a modal viewController in an iPad app using the UIModalPresentationFormSheet view style. I am looking to produce something similar to the Mail app's new message UI/animation.
There are two things that are not behaving correctly:
The modal viewController that is presented always animates to y=0, i.e. to the very top of the
view and not some pixels below the status bar as it does in the mail app.
The documentation says:
UIModalPresentationFormSheet The width
and height of the presented view are
smaller than those of the screen and
the view is centered on the screen. If
the device is in a landscape
orientation and the keyboard is
visible, the position of the view is
adjusted upward so that the view
remains visible. All uncovered areas
are dimmed to prevent the user from
interacting with them.
However, in my case there is no dimming and I can still interact with the parentView below the modalViewController.
The controller that presents the modalView I do this:
AddNewItemViewController *newItemViewController = [[AddNewItemViewController alloc] initWithNibName:#"AddNewItemViewController" bundle:nil];
[self presentModalViewController:newItemViewController animated:YES];
[newItemViewController release];
In the viewController being presented I do this:
- (void)viewDidLoad {
[nameField becomeFirstResponder];
[self setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self setModalPresentationStyle:UIModalPresentationFormSheet];
[super viewDidLoad];
}
I hope someone can help me out.
Is there some other properties I need to set on the parent and modalViewController?
Is the viewDidLoad not the right place to do this setup?
Thanks in advance:)
You set the transition and presentation styles when you create the modal view, before you call presentModalViewController. Remember, the view that creates the modal view 'owns' that object. You want the owner to set these properties because you might implement this modal view elsewhere in the app and want different transition or presentation styles. This way, you set it each time as appropriate.
AddNewItemViewController *newItemViewController = [[AddNewItemViewController alloc] initWithNibName:#"AddNewItemViewController" bundle:nil];
newItemViewController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:newItemViewController animated:YES];
[newItemViewController release];
You're right in calling becomeFirstResponder in viewDidLoad.