I have rotation problem on UISplitViewController. In my case, after calling present modal on splitViewController (this splitViewController is the root view of window) to show another view. When that modalView is rotated, the splitView rotate incorrectly.
The splitView appears like it rotated, but the DetailViewController stays like it appear on portrait, and the RootViewController did not show at all. It still behave like on portrait mode. It seems that the splitView is rotated, but not the underlying views (DetailViewController and RootViewController).
I try to implement willRotateToInterfaceOrientation on the modalView, and call the splitView's willRotateToInterfaceOrientation method using delegate.
This is what I implement in modalView:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
MacProjectAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.splitViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
The result is, the DetailViewController behave correctly, but not with RootViewController. It stay hidden. It will re-appear after the user manually rotate to portrait and back to landscape. But obviously user did not want that.
This is what I did when presenting modalView:
ModalViewController *modalView = [[ModalViewController alloc] init];
MacProjectAppDelegate *delegate = (MacProjectAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.splitViewController presentModalViewController:modalView animated:YES];
[modalView release];
Is there a way to show the RootViewController in this case? Or is my doing things is wrong?
Edit:
It seems all views did not rotate following the modalView. neither the RootViewController nor DetailViewController did rotate (the didRotateFromOrientation on both ViewController did not called at all). So now I assume it didn't rotate following the device/modalView.
OK. My problem has been solved.
What I did is as I wrote as comment on Shurakov's comment.
I acquire appDelegate and get the splitViewController to call willRotateToInterfaceOrientation.
MacProjectAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.splitViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
I did it twice since only one of them seems did not work (I don't know why). One at modalViewController's willRotateToInterfaceOrientation method, one at RootViewController's willAppear method.
To remove the navigator/popover button on DetailViewController, I simply invalidate that button by calling invalidateRootPopoverButtonItem at RootViewController's willAppear method. This is what it's looks like on this particular method (for the first one at modalViewController, I just pass the willRotateToOrientation command to splitViewController):
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (UIInterfaceOrientationIsLandscape(orientation)) {
MacProjectAppDelegate *delegate = (MacProjectAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.splitViewController willRotateToInterfaceOrientation:orientation duration:1];
[[[delegate.splitViewController viewControllers] objectAtIndex:1] invalidateRootPopoverButtonItem:self.rootPopoverButtonItem];
}
Hope this can explain how I overcome my problem and help anyone who had the same problem as me.
Related
I'm using modal segue (without navigation controller) to move between viewController A and viewController B like so:
viewA *first = [self.storyboard instantiateViewControllerWithIdentifier:#"viewA"];
[self presentViewController:first animated:YES completion:nil];
And to move back :
[self dismissViewControllerAnimated:YES completion:nil];
Now, I want to know from the AppDelegate whether A or B is the current view right now.
The problem is when I'm checking
[(AppDelegate *)[[UIApplication sharedApplication] delegate] window]
the answer is always view A - the first one.
I've tried to set the current view every time I'm using modal segue like so:
viewA *first = [self.storyboard instantiateViewControllerWithIdentifier:#"viewA"];
[self presentViewController:first animated:YES completion:^
{
[[(AppDelegate *)[[UIApplication sharedApplication] delegate] window] setRootViewController:first];
}];
But it cause a few bugs (like unable to use "dismissViewControllerAnimated"),and it's impossible to work like that in every segue in a big project with many segues.
How should I work with that? And how should I detect the current view in more appropriate way?
As was answered here
UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
return win1.windowLevel - win2.windowLevel;
}] lastObject];
UIView *topView = [[topWindow subviews] lastObject];
However, doing this logic here sounds like bad architecture. What is the reason for you needing to know which view is currently presented inside of your AppDelegate?
Edit It seems like you want to respond to the applicationWillResignActive event from your view controller. Use something like this in the your game view controller.
- (void) applicationWillResign {
NSLog(#"About to lose focus");
}
-(void) viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillResign)
name:UIApplicationWillResignActiveNotification
object:NULL];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter]
removeObserver:self];
}
The appDelegate's window won't be equal to either view controller (viewControllerA or viewControllerB). You can ask the window for it's root view controller...
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
if (appDelegate.window.rootViewController == viewControllerA) {
// always true if you always start the app with viewControllerA
... and you can ask any view controller for the view controller it presented...
if (appDelegate.window.rootViewController.presentedViewController == viewControllerB) {
// will be true if viewControllerA has presented viewControllerB
But this is a tricky game. If, for example, viewControllerB presents some other viewControllerC, the condition above will continue to be true.
See the #Eric answer here (not the accepted answer) for a way to find the topmost vc in general.
First, I want to say that this question does not solve my problem: Orientation problem when I set a RootViewController
My ipad application only works in landscape mode. When I'm changing the root controller, the new storyboard appears correctly. But, it appears in portrait mode and then rotates to the correct orientation. How can I specify the orientation before changing the root controller or tell the storyboards to display in landscape by default ? It is pretty ugly seing the rotation of the view just after the animation.
Also, the landscape left and right are set in the project property.
Here is the code when I change the root view controller.
UIStoryboard *editorStoryboard = [UIStoryboard storyboardWithName:#"EditorStoryboard" bundle:nil];
UISplitViewController *editorViewController = [editorStoryboard instantiateInitialViewController];
UINavigationController *navigationController = [editorViewController.viewControllers lastObject];
editorViewController.delegate = (id)navigationController.topViewController;
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
[UIView transitionWithView:appDelegate.window duration:0.5 options: UIViewAnimationOptionTransitionFlipFromLeft animations:^{
appDelegate.window.rootViewController = editorViewController;
} completion:nil];
Did you try to override the
preferredInterfaceOrientationForPresentation
for the new root viewcontroller? Something like this:
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscape
}
This from the View Controller Programming Guide for iOS
EDIT: ok, what about trying to get rid of the animation?
I have one view controller, named AViewController. There is a button in AViewController. When the button is clicked, i will present another view controller,lets called BViewController. If AViewController is in portrait-orientation, it works well.If the orientation is in landscape, then the BViewController is still initialized in portrait but presented in landscape.
This is the problem. I wonder how can i init BViewController in current orientation. I use [UIScreen mainScreen].bounds in the BViewController's init. but the bounds is not in landscape if the device is in landscape. I know pass the frame from AViewController to BViewController, it may work. but i don't think this is a good solution. So is there any better ways?
the function to present BViewController is as follows:
- (void)BtnClicked
{
BViewController *bvc = [[BViewController alloc] init];
bvc.delegate = self;
bvc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:movie animated:YES completion:nil];
}
I still don't know how to do it efficiently. But i use one solution to call the willAnimateRotationToInterfaceOrientation method manually in viewDidAppear.
the snippet is as follows:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self willAnimateRotationToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation duration:0];
}
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.
I'm converting and iphone project to ipad. On iphone I have a mainViewController that opens a loginViewController using addSubView.
On the iPad I would like to display that loginViewController in a popover. so I did something like:
UIPopoverController *loginPop = [[UIPopoverController alloc] initWithContentViewController:loginViewController];
[loginPop presentPopoverFromRect:CGRectMake(150, 150, 90, 90) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:true];
This works fine. The problem is when the loginViewController finishes its "business". On the iPhone I just call a simple [self.view removeFromSuperview]; But on the ipad this causes the view to be removed from the PopoverController but the frame of the popup stays.
So my question is: is there any simple way from within the loginViewController to remove its PopoverController container (without using delegate or notifications)?
Yes, your loginViewController should keep a reference to the popover. Then you can use the dismissPopoverAnimated: method of your popover itself to remove it.
Actually, I want to implement that, but I remembered that we can access application delegate, which in turn will have access to main view of it, In there, you can store property of the popover, and you can call the dismissPopoverAnimated.
Like this :
MyAppDelegate *app = (MyAppDelegate *) [[UIApplication sharedApplication] delegate];
MyViewController * myView =[app viewController];
[myView.popover dismissPopoverAnimated:YES];