I've discovered that after dismissing a modal view controller that's presented fullscreen in a compact view and as a popover in a horizontally regular view, the screen goes black after the animation.
My View Hierarchy is the following
View (of my rootViewController on the window)
--->UISplitViewController.view ( set as a child viewController )
--------> rootViewController.view (set as the mainViewController of the splitView)
--------> detailViewController.view (set as the detailViewController of the split view)
Via the iPhone 6 simulator(split view is always collapsed) I present a modal viewcontroller with the following code:
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
[navigationController.navigationBar setBarStyle:UIBarStyleBlack];
[navigationController setModalPresentationStyle:UIModalPresentationPopover];
navigationController.popoverPresentationController.sourceView = view;
navigationController.popoverPresentationController.barButtonItem = barButtonItem;
navigationController.popoverPresentationController.delegate = self;
[self presentViewController:nav animated:YES completion:nil];
I dissmiss the presented controller from that viewController by calling:
[self dismissViewControllerAnimated:true completion:nil];
If I set animated to "false" I dont have any problems, but it looks bad and doesnt make sense.
I see some posts regarding this and custom presenatation methods, but I'm not using anything custom here.
On iPhone the ModalPresentationStyle should default to UIModalPresentationOverFullScreen, so I tried setting the presentationStyle directly to that, and it worked!
If I set the presentationStyle to "FullScreen" (not over fullscreen) I get the same behavior, a black screen after dismissing.
Any thoughts or experiences on this one?
I've come up with the following work around, but I still don't think it should be needed.
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection{
if( traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact )
{
return UIModalPresentationOverFullScreen;
}
else
{
return UIModalPresentationPopover;
}
}
Related
I came across a strange problem, i'am in landscape and present a VC
SomeViewController * vc = [[SomeViewController alloc] init];
[self.navigationController presentViewController:vc animated:YES completion:^{
}];
in the viewWillAppear the frame is incorrectly in portrait and in viewDidAppear it is correctly in landscape
but when i put it inside a UINavigationController
SomeViewController * vc = [[SomeViewController alloc] init];
UINavigationController * nc = [[UINavigationController alloc] initWithRootViewController:vc];
[self.navigationController presentViewController:nc animated:YES completion:^{
}];
than in the viewWillAppear the frame is correctly landscape
Is this a bug? I'am i missing something? Why it is behaving like this?
I think it is by design, see this answer A: View frame changes between viewWillAppear: and viewDidAppear:.
By viewDidAppear the frame geometry is correct since it is now on-screen and added to the view hierarchy but I would actually recommend doing it in viewDidLayoutSubviews since that will be called when rotating orientations as well.
That simple example but that don't work;
I have ViewController where inside on NavigationConroller, then I want to add new ViewConroller with its self navigation controller.
In main viewController:
CustomViewController *vc = [[CustomViewController alloc] init];
NewNavigationVC *nav = [[NewNavigationVC alloc] initWithRootViewController:vc];
[self presentViewController:nav animated:NO completion:nil];
Two controllers has a background color clear, but still black color.
Navigation bar I can do clear, but not a view.
UPDATE:
if i change self.window.backroundColor to red for example, that work but not clear
UPDATE 2:
[self addChildViewController:vc];
[self.view addSubview:vc.view];
[vc didMoveToParentViewController:self];
and when I want to dealloc vc
[vc willMoveToParentViewController:nil];
[vc.view removeFromSuperview];
[vc removeFromParentViewController];
All work ok without navigation controller
A viewController's view's backgroundColor can't be clear (as in showing the previous viewController's view on the stack). Pushing or presenting a viewController will put the new viewController on the stack and hide the previous viewController completely.
If you want a clear backgroundColor on the view, you will need to either:
1) set the viewController as a childViewController of the previous viewController - then animate the transition yourself.
Or
2) transplant the viewController logic into the previous viewController and have a new uiview act as that view (you also need to animated the transition yourself).
The solution is as follows. For clear example we use tableViewController:
UITableViewController *modalVC = [UITableViewController new];
UINavigationController *modalNVC = [[UINavigationController alloc] initWithRootViewController:modalVC];
UIViewController *mainVC = [UIViewController new];
UINavigationController *mainNVC = [[UINavigationController alloc] initWithRootViewController:mainVC];
modalVC.view.backgroundColor = UIColor.clearColor;
mainVC.view.backgroundColor = UIColor.redColor;
mainNVC.modalPresentationStyle = UIModalPresentationCurrentContext;
[mainNVC presentViewController:modalNVC animated:YES completion:NULL];
The key feature is that you have to set modalPresentationStyle of presentingViewController to UIModalPresentationCurrentContext.
It works fine BUT without slide animation. You will get result immediately.
But you can still use "blood hack" to retain visual animation by successive presenting, dismissing and presenting again:
modalVC.view.backgroundColor = UIColor.clearColor;
mainVC.view.backgroundColor = UIColor.redColor;
[mainNVC presentViewController:modalNVC animated:YES completion:^{
[modalNVC dismissViewControllerAnimated:NO completion:^{
mainNVC.modalPresentationStyle = UIModalPresentationCurrentContext;
[mainNVC presentViewController:modalNVC animated:NO completion:NULL];
}];
}];
You basically need to tell the navigation controller to:
navigation.modalPresentationStyle = .overCurrentContext
In other words:
A presentation style where the content is displayed over another view controller’s content.
and that's it.
You can also make sure that:
navigation.view.backgroundColor = .clear
I have an ipad app.
I am trying to open view 2 (kind of push view) full with entire screen. how normally do with push view or UIModalPresentationFullScreen. but my base view which is view 1 is also modal view.
so i was trying to open view 2 when view 1 get dismiss…
- (void) handleNewButton :(int)id
{
[self dismissViewControllerAnimated:YES
completion:^{
NewViewController *View2 = [NewViewController alloc] init];
View2.modalPresentationStyle = UIModalPresentationFullScreen;
View2.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController: View2 animated:YES completion:nil];
}];
}
but my view 2 is not opening. i know i can not do push view. But is there any way to achieve it?.
When you do this dismissViewControllerAnimated the UIViewController (self in this case) is gone, in the sense that he is not on the screen anymore, if it has been released or not, that's another story. The reason for you to not be able to show the View2 (very poor name, it should at least ViewController2) is because you are trying to show it from a UIViewController that is not on the screen anymore.
So, what can you do?
The current self in the context of the handleNewButton method, in theory was presented by another UIViewController, that's from where you want to present your View2.
Probably the quickest way of implementing of what I said, would probably be with a notification described here. Although I would do it with a block, so when the self would be created, I would pass a dismissiCompletionBlock that would be called when that UIViewController was dismissed.
try to allocate NewViewController with nib name if you are not using storyboard,
[self dismissViewControllerAnimated:YES
completion:^{
NewViewController *n=[[NewViewController alloc]initWithNibName:#"NewViewController" bundle:nil];
View2.modalPresentationStyle = UIModalPresentationFullScreen;
View2.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController: View2 animated:YES completion:nil];
}];
or if you are using storyboard get NewViewController using identifier.
For my iPad app, I have a view displayed modally as a formsheet when a button is pushed. In order to have the keyboard dismissed after entering text in a textfield i tried as suggested;
the "disablesAutomaticKeyboardDismissal" method.
This does not work, in fact, the method is never called acording to the log.
The keybord will dismiss for iPhone or when i choose to not present modally.
Here is my code:
- (BOOL)disablesAutomaticKeyboardDismissal
{
NSLog(#"method calls");
return NO;
}
- (IBAction)showNewView:(id)sender
{
MyViewController *mvc =
[[MyViewController alloc] init];
// some lines about setting content
//...
UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:mvc];
[navController setModalPresentationStyle:UIModalPresentationFormSheet];
[self presentViewController:navController animated:YES completion:nil];
}
-(BOOL)disablesAutomaticKeyboardDismissal
or not, the keyboard is not dismissed unless i remove tis line:
// [navController setModalPresentationStyle:UIModalPresentationFormSheet];
However, then it is not presented the way I want anymore.
Can anyone see what I am doing wrong?
-(BOOL)disablesAutomaticKeyboardDismissal needs to overridden to return NO by the view controller that is presented as a form sheet, not by the presenter; That's your mistake. In your case you could subclass UINavigationController to get the desired behaviour:
#interface AutomaticKeyboardDismissingNavigationController : UINavigationController
#end
#implementation AutomaticKeyboardDismissingNavigationController
- (BOOL)disablesAutomaticKeyboardDismissal
{
return NO;
}
#end
(The class name could probably be a bit shorter and still be comprehensible.)
I have another orientation problem. But this one is very tricky.
My RootViewController is a normal NavigationController.
self.window.rootViewController = _naviController;
which has another ViewController inside, lets call it VC1.
VC1 has some buttons and labels. Its like an overview with folders.
If I press a button I come to the next ViewController with 3 ViewController (Page) and another bunch of buttons (like inside a folder looking at the pictures/thumbnails inside):
Archiv *archiv = [[Archiv alloc] init];
[self.navigationController pushViewController:archiv animated:YES];
[archiv release];
in loadView:
firstPage = [[Page alloc] initViewWithFrame:CGRectMake(0, 0, 768, 960)];
[firstPage setRootViewController:self];
secondPage = [[Page alloc] initViewWithFrame:CGRectMake(0, -960, 768, 960)];
[secondPage setRootViewController:self];
thirdPage = [[Page alloc] initViewWithFrame:CGRectMake(0, 960, 768, 960)];
[thirdPage setRootViewController:self];
If I now click again on a button the active Page push my third ViewController (image with resizing, dragging...):
Picture *pic = [[Picture alloc] initWithPicURLString:urlString];
[rootViewController.navigationController pushViewController:pic animated:YES];
[pic release];
With the BackButton of the NavigationController I can always come back to the previous view.
Some more informations:
Every ViewController supports all orientations
Every ViewController implements - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation with return YES;
Every ViewControler calls the [super init] in their init-methode
I already read Apple's Q&A: Why won't my UIViewController rotate with the device
Now the tricky problem:
If I switch from 2nd VC to the 3rd VC, change the orientation there from portrait to landscape and press the BackButton everything is working (shouldAutorotateToInterfaceOrientation is calling, frame size and origins changing ...).
BUT if I do it the other way around, I am in landscape mode, switch from 2nd VC to 3rd VC, rotate to portrait and come back to 2nd VC with BackButton, the status- and controllerBar are at the top but the shouldAutorotateToInterfaceOrientation wasn't called.
Please help me. $h#rky
Try this, it works for me:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self shouldAutorotateToInterfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation] ];
}
Today I got the idea that solved the problem without knowing the cause.
In my third VC I just created a pointer to the 2nd View and called the shouldAutorotateToInterfaceOrientation myself.
But the point is still the same: Why isn't shouldAutorotateToInterfaceOrientation not calling in the described situation?
Kind regards. $h#rky
shouldAutorotateToInterfaceOrientation only called when user rotate, so when you from landscape to portrait or otherwise then view controller still landscape, so this solve problem, you have to hack code, it's mean when you push to view controller from landscape to portrait presentViewController example:
ListCurrentViewController *list = [self.storyboard
instantiateViewControllerWithIdentifier:#"ListCurrentViewController"];
[self.navigationController presentViewController:list animated:NO completion:Nil];
[list dismissViewControllerAnimated:NO completion:Nil];
[self.navigationController pushViewController:list animated:YES];
in ListViewController function called:
(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation // iOS 6 autorotation fix { return UIInterfaceOrientationPortrait; }
and you have to create category for UINavigationController
(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [self.visibleViewController preferredInterfaceOrientationForPresentation];
}
I hope this solve will help you.