How does a UISplitViewController know when it has rotated so that it can trigger the appropriate behavior with managing its views? Is there some way I can manually trigger it myself? I have a split view controller owning a view that is not at the root of my hierarchy, so it is not getting the rotation events that (I think) normally allow it to handle rotation behavior.
You can try to implement UISplitViewController delegate which is:
// Landscape mode
– splitViewController:willShowViewController:invalidatingBarButtonItem:
// Portrait mode
– splitViewController:willShowViewController:invalidatingBarButtonItem:
Since the masterView (left) will show/hide accordingly when the rotation occurs, I found this is more effective compared to handling the orientation changes if each view
I guess UiSplitViewController doesn't autorotate and
iPad: SplitView does not rotate pretty much say that unless the controller's view is the root view, it won't work. Oh apple.
You could sign up for notifications of orientation changing, make sure you have shouldAutorotateToInterfaceOrientation set to YES for the rotations you want to support as well.
Related
In my iPhone app I have a view that I want to show only in portrait mode. When navigating to that view it should be automatically displayed in portrait view. When navigating away, the orientation should change back to what it was, or, if the device orientation has changed, adapt to that. I could find information on forcing an orientation and preventing auto-rotate. I could not find anything on how to change back to the correct orientation after navigating away from that view.
So my idea was to
save the initial orientation (store in currentOrientation)
subscribe to orientation change event to keep track of orientation changes while the content is locked to portrait (update currentOrientation)
when leaving the view, restore the correct orientation using the currentOrientation value.
Edit (code now removed): Apart from it not working it was a dangerous way to go as it made extensive use of unsupported APIs.
Edit:
I believe this question can now be boiled down to the following:
Is there a documented, supported way to force the interface orientation independent of the device orientation? setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation") has been recommended many times on SO and elsewhere but it does indeed seem to be an unsupported hack.
Is there a documented, supported way to update the interface orientation to the device orientation? That would be needed to "recover" from the forced interface orientation in another view without having to trigger auto rotation by turning the device back and forth.
Supported are supportedInterfaceOrientations() and shouldAutorotate(). But these will only lock the interfaceOrientation after the device has been turned to that position. They do not prevent wrong initial orientation.
There are many questions similar to this one, showing that this problem setting is not uncommon, but so far no satisfactory and complete solution using supported methods.
I had a similar problem except I needed one view controller to only work in Landscape mode and another when it was in portrait. The way I achieved this was making a custom 'root' view controller. Then on the viewWillTransitionToSize method for that controller checking for orientation and non animatedly pushing the correct view controller (so it looks like a rotation to the user). And then in Interface Builder I set the view controller's orientation property explicitly instead of being inferred. You could apply this solution by having only the landscape orientation set on the restricted view controller and then on the portrait rotation doing nothing and disabling auto rotation on the restricted view controller.
Update
I haven't had the time to test any of these but these are just the ideas I used when implementing my solution for a different VC for a different orientation, some combination of the following should hopefully work I can't be a 100% certain about it cause I did this some months ago and don't exactly remember what did and didn't work.
First of all make sure that you have setup the constraints as shown in the screenshot. Mine has iPad full screen and landscape because that's what I was doing change yours to whatever you need (portrait and the size can be inferred).
Now before doing anything else I would first check to see if this solved the problem. I needed the root view controller cause I needed a different VC for portrait and and a different one for landscape. You only need to restrict it so if this works than that's perfect otherwise there are a few other things you can try as mentioned below.
Once that's setup I would first go to the view controller who you want to restrict's class and prevent autorotation using:
override func shouldAutorotate() -> Bool {
return false
}
Now if you do that since you are restricting to portrait I'm guessing you don't really care about upside down so you don't need to do anything additional. If you do want to use the viewWillTransitionToSize method and rotate manually.
If things still don't work you can finally try the root controller way (but I would use this in the last case). Heres a sketch of it:
class VC : UIViewController {
override func viewDidLoad () {
UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name: "UIDeviceOrientationDidChangeNotification", object: nil)
// this gives you access to notifications about rotations
}
func orientationChanged(sender: NSNotification)
{
// Here check the orientation using this:
if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) { // Landscape }
if UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation) { // Portrait }
// Now once only allow the portrait one to go in that conditional part of the view. If you're using a navigation controller push the vc otherwise just use presentViewController:animated:
}
}
I used the different paths for the if statements to push the one I wanted accordingly but you can do just push the portrait one manually for both and hopefully one of the ways above will help you.
Background: I want to make sure my viewControllers rotate properly when it appears. My viewControllers have excellent codes managing the rotation and orientation when it is visible.
Problem: Given two viewControllers in a NavigationController, viewC1 and viewC2. I did the following:
1.) Set rootViewController to viewC1
2.) Push viewC2 into the NavigationController
3.) Rotate
4.) Pop viewC2
5.) viewC1 is still stucked in the old orientation look (as in the transformation code in willAnimateRotationToInterfaceOrientation was not called) with the new orientation.
What can I do to ensure viewC1 call willAnimateRotationToInterfaceOrientation to reconstruct itself to look correctly in the new rotation?
Additional info:
This is all code (no storyboard/xib). I have shouldAutorotateToInterfaceOrientation return YES on all the views. I use willAnimateRotationToInterfaceOrientation to manage all my rotation.
Oh, and please no hacks. For example, copy the code from rotation then check the rotation mannually and manage it in viewDidAppear.
Think about the name of the method, and what you're trying to achieve.
willAnimateRotationToInterfaceOrientation indicates that the view controlled by the view controller is about to animate to a particular orientation. If your view is in the middle of a navigation stack, then it is not being displayed on screen. To animate something that isn't on screen is costly and ultimately worthless. So, that particular method is out of the question, but the problem that remains is there isn't anything else more appropriate in UIKit. The reason is to rotate something (even if not animated) when it's offscreen is worthless cost. It's the responsibility of the developer to handle a change in orientation when the view appears ("transformation on demand" as you will).
You say you don't want hacks, but the method you've described as a hack is probably the most appropriate thing to do. Having a generic method named something like
-(void) updateLayoutForOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated { ... }
isn't a bad idea. This can be the handler for orientation change transformations for the whole view controller.
The places you need to possibly check/handle orientation issues are
-(void) viewWillAppear:(BOOL)animated
-(void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation duration: (NSTimeInterval) duration
and in both of these, call updateLayoutForOrientation:animated: to do the work for you.
I've got a universal ipad/iphone app that allows the user to watch a video, which they can then expand into full screen mode.
I have implemented (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration, and in that method I perform various setFrame calls on my view elements depending on whether they are in landscape or portrait orientation.
That all seems to work fine in normal use, i.e. rotating back and forth works fine.
But if the user starts in portrait mode, starts a video, goes to full screen mode, turns into landscape orientation, and then the video stops -- the elements are often not resized properly. They appear to be sized still as if they are portrait mode.
If I then turn to portrait mode, and then turn back to landscape, the view resets correctly.
The strange part is, I have implemented (void)exitedFullscreen:(NSNotification*)notification and in there I print out the orientation, and it's seen correctly. I also call my code to reset the view elements based on the current orientation, and I am still having this problem.
Another related issue is sometimes when dealing with rotation, my views will end up too far up the screen, actually going under the status bar at the top of the device.
Edit Here's the latest example. I rotate to landscape mode during full screen video playback, and then when I left full screen video, you can see the issue with the navigation bar at the top of the view.
One possible way to solve this is by presenting your view controller modally instead of using the navigation view controller.
Refer to Kenny's answer at Problem pushViewController from Landscape to Portrait
Your ViewController might not be rotating because another controller is the first responder. What you can do to avoid this is register the view controller to the device rotation changes and implement the rotation in the selector you call when you receive such a notification.
In appDelegate:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
In your view controller
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:)name:UIDeviceOrientationDidChangeNotification object:nil];
In did rotate you can check the orientation with
[[UIDevice currentDevice] orientation]
The navigation bar at the top of the view. I solved it, using this code ->
[[UIApplication sharedApplication] setStatusBarHidden:NO animated:NO];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
Using this after your rotation.
Mason, did you logged and checked whether your method willAnimateRotationToInterfaceOrientation:duration: gets called after each state transition?
To me this latest screenshot does not look like an orientation change issue.
The navigation bar is basically off by the status bar's height.
Possibly your position calculation fails because you are using the view's frame
while the fullscreen video (w/o status bar) is playing and this fails as soon as
the statusbar is back?
Your orientation may not get updated properly if there is another controller acting as a first responder. The best way to overcome this is to call the functions you use to orientate the screen at the method viewWillAppear: using the current orientation of the view controller: [self interfaceOrientation]
If you use a subclassed subview you may need to reimplement the methot layoutSubviews and call setNeedsLayout. Another thing that may be causing this is resigning the viewcontroller where you have the video as first responder (you mays search if somewhere you use the methon resignfirstresponder and try how it works without it). If this does not work, I don't know, this things may be very tricky and dependent on how you have implemented it. But for the things you say you do you should not need much code, since automatic rotation and resizing of views is handled now by the sizes inspector of the views editor.
I think that this should do.
I'm doing an universal ios game and I'm having an orientation problem. My app is all in landscape mode. If I do presentModelViewController is all ok, but if I do setRootViewController, the new controller appear in portrait mode.
What am I doing wrong?
I'm unsure if this was your problem, but I have an application that starts with one view controller in portrait mode and then I'm trying to present a second view controller in landscape mode. I'm using the setRootViewController technique as well so that I do not have to deallocate/reallocate the second view controller and lose my state information since users will be switching between the two views frequently.
I had the same issue where the second view controller would always be displayed in portrait mode instead of landscape, even though the view controller itself specifies that it never allows portrait mode.
The fix for me was to make sure that in the application delegate I presented the first view controller using
[window setRootViewController:controller];
instead of
[window addSubview:controller.view];
This was an older application, and the original template used addSubview by default. it seems that if there was not an original root view controller specified, the necessary orientation messages will never make it to subsequent view controllers that are set as root. Hope that helps!
Have you set the
UIInterfaceOrientation
key in your info.plist file to your desired orientation? (in this case landscape)
I am developing an iPhone application. My active UIViewController is vc1. The shouldAutorotateToInterfaceOrientation method of vc1 returns YES and hence the device orientation is handled for its view automatically.
All the subviews added to this view has autoresizing masks set properly to fit the orientation.
The view's frame is not updated to fit the orientation for the following scenario:
Push another UIViewController say vc2
change the device orientation
pop vc2
The new orientation is not reflected for view of vc1.
I can register for orientation change notification and manually set the frame to the view. Is there any way that handles this automatically?
I did the same. I don't think there is any way you can do this automatically.
You can probably send in a notification about the device orientation or you can check the current device orientation in ViewWillAppear and set the frame accordingly.
What exactly is your app viewController structure?
Make sure you are using a navigationController (you can have the navigationBar hidden if you like). Then it should just work.