I'm doing some fancy stuff with rotation, and am returning NO from shouldAutorotateToInterfaceOrientation:. I then manually rotate a UIWebView according to [UIDevice currentDevice].orientationCached. When I hold my finger down on a link, the menu which appears is always in PortraitLeft orientation, no matter how the device is actually oriented, and no matter how the UIWebView is oriented.
It looks to me like the link menu takes its orientation from the main view of the view controller, rather than the UIWebView its associated with, and that the only way to get it to behave correctly is to return YES from -shouldAutorotateToInterfaceOrientation:.
Is this assumption correct? Is there any way to control the orientation of the link-related popup menu, or to force it to take its orientation from the UIWebView which spawns it?
I wouldn't actually set the orientation manually how you're doing it in the UIWebView. Instead, force the orientation of the parent view controller through code.
As an example of a similar situation, I have an app that I've developed that displays a map in a few views. When rotated from portrait to landscape, it layouts the view in a very different manner from the portrait layout with a fancy animation. I like it, but some users don't, so I provide the option to disable map rotation. And I do that by passing in only certain rotation abilities to the parent view. So if they disable landscape, I tell the parent view controller it can only rotate to landscape. Otherwise, it's free to rotate to any orientation except upside down.
Enough with the explanation: here's my relevant code I use to accomplish this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
//Rotate the view if rotation is actually enabled
if ([self.prefs boolForKey:#"SHOULD_ROTATE"]) {
return (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) || UIInterfaceOrientationPortrait == toInterfaceOrientation);
}
//Rotate it to portrait only if rotation is disabled
else if (![self.prefs boolForKey:#"SHOULD_ROTATE"]) {
return (UIInterfaceOrientationPortrait == toInterfaceOrientation);
}
//Otherwise, rotate only to portrait (for all views minus the map views)
else {
return (UIInterfaceOrientationPortrait == toInterfaceOrientation);
}
}
I actually implement this in the UITabBarController so that it applies to all views and view controllers in the app, but it's just as easily implemented only for the UIWebView parent view controller. The gist is that you're returning yes or no based on if the view orientation matches what you want it to. For the web view, you would want to lay out the only allowed rotation orientations to by returning this:
return (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)
Of course, I haven't discussed forcing the view to set the view to a landscape view, only what it does once a user turns it. So the best course of action there is simply to manually do it once the view is initialized:
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationLandscapeLeft animated:NO]
You'll have to set it to either Left or Right to start with, but the user can rotate between right and left as they wish if you implement my earlier code as well.
That's a lot of explanation, but I think the combination of these two methods for the parent view controller should allow you to present the view exactly how you wish. Let me know if you have any questions!
By "popup menu", do you mean the Action Sheet with Open … Copy buttons?
If so, then yes, Action Sheet always presents itself according to the top view controller's interfaceOrientation. Because you are rotating the view yourself without its controller updating its interfaceOrientation, the Action Sheet still thinks it is portrait.
Related
Requirements: just can not automatically rotate a single page, go to horizontal screen outside always come in horizontal screen, vertical screen outside come in to keep the vertical screen.
Action: In the controller of this page and write shouldautorotate method returns NO, find work for all iphone have reached the demand, but the iPad is not working.
Note: Please see, it is a single, specific page without automatic rotation, which is to ensure the program settings page in several directions are checked, and the pull-down device along the shortcut menu to ensure open automatically rotated.
System: iOS9
I had a similar case. Note that I wasn't using navigation controller, it was a simple modal view being presented and this could affect the rotation code.
On the first view of my application I had:
-(BOOL)shouldAutorotate {
return YES;
}
On the view that I wanted to lock on a certain orientation (landscape in my case) I didn't use the shouldAutorotate but used this one:
-(UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
I am working on a landscape view for a currently existing application. I believe I have autoRotate, supported interface, etc set up correctly, I am actually reusing code that works with a much simpler app. However when the simulator is rotated into landscape mode, the correct view loads, but the status bar and view stay with the short edge of the iPad. I've attached a screenshot and code. Is the problem with a view controller higher up in the chain, or the appdelegate? I've traced the called controllers in the debugger and it appears they are dismissed once this page is loaded. I am fairly new-ish to obj-c so it is possible this is something simple I am missing, but I have checked all attributes for the .xib file and everything looks copasetic.
Some of the code:
-(BOOL)shouldAutorotate
{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;// | UIInterfaceOrientationMaskPortraitUpsideDown;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return NO;
}![enter image description here][2]
-(void)orientationChanged{
UIInterfaceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
if ((interfaceOrientation==UIInterfaceOrientationPortrait)||(interfaceOrientation ==UIInterfaceOrientationPortraitUpsideDown)){
self.view = self.portraitView;
} else {
self.view = self.landscapeView;
}
}
EDITS - This problem occurs on both iOS 7.1 and 6.1 and this is the first time any screen in the app supports a landscape view. To clarify the views, the portrait and landscape views are separate Views in single .xib file. The file owner class is set to the correct view controller class, and the parent view controller, a sales screen, should not rotate. It does not have a landscape view, but even with its autoRotate methods set to return YES the subview does not orient correctly.
Like 0x7ffffffff already said you need to allow rotation in your shouldAutorotate functions. Also you need to set up the supported rotation directions in your project setup.
First go to your project's settings:
Next you need to select all the orientations you want to support:
Another that is very important: ONLY the root View Controller will receive rotation events. If you nest a View Controller inside a View Controller then that nested Controller will not receive those events unless you wire them up manually from the parent. That's why I usually don't nest ViewControllers but use ad-hoc NSObjects or UIView implementations for nested views.
Last but not least: make sure your device is not rotation-locked: http://www.iphonefaq.org/archives/972915
The problem was occurring because a subclass of the customerView was not receiving the rotation notification. After tracking that class down it was a matter of setting up the NSNotificationCenter for orientation changes and then allowing autoRotation and supprotedInterfaceOrientations.
I have been trying to get multiple orientations to work with a single view controller. Currently it checks for the device orientation and view controller. Then switches based on whether it's landscape or portrait. The problem is that it works fine in portrait, but since it pushes another view on the stack whenever it's in landscape the back button links to the portrait view instead of the actual screen we want to get back to (which is one further step away).
if (UIDeviceOrientationIsLandscape(deviceOrientation) &&
self.navigationController.visibleViewController == self)
{
self.landscapeViewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"view_landscape"];
[self.navigationController pushViewController:self.landscapeViewController
animated:NO];
}
else if (UIDeviceOrientationIsPortrait(deviceOrientation) &&
self.navigationController.visibleViewController == self.landscapeViewController)
{
[self.navigationController popViewControllerAnimated:NO];
}
I cant present the landscape view controller in modal fashion, since there is a navigation controller involved.
Another thing is that I'm instantiating the same view controller for each orientation (using the same class but linking to different identifiers in the storyboard).
The thing you're trying to do is REALLY bad and goes against Apples way of doing things.
There's something called Autolayout, with which you can design a single view to work both in landscape and portrait mode.
It is possible you can handle programatically or simply use auto-layout depends on your requirement .just prefer this LINK
I've noticed that most consumer-friendly Android and iPhone fitness apps have two interface modes - in portrait mode the user gets more detailed information, but when the user turns the device to landscape mode, a full screen graph is added to cover the entire screen.
I'm interested in how to implement transition to a different view controller in response to device rotation on iPhone. My initial thoughts are to intercept (willRotateToInterfaceOrientation event, then get the app delegate and add a full screen graph view controller to the window).
Is there a better way of turning an iPhone rotation into a transition to another view controller? Like hiding the status bar and pushing a modal view controller in landscape mode with animation?
First ask yourself whether you really need a separate view controller. One view controller can easily hide or unhide a graph.
If this graph needs its own view conroller then you could use a container view that contains the graph which refers to its own view conroller. That is what container views are made for.
The "Master" view controller then would just hide and unhide the container view in response to rotation events (and layout them accordingly etc. pp.)
If you prefer to add or remove the container view from its super view (most probably self.view from the "Master" view controller's point of view) then do that instead of hiding and unhiding. That is probably most appropriate.
The upside of this appoach would be that it works regardless of the navigaiton structure you are in, regardless of whether the rotated view controller was pushed or presented modally, regardless of whether you are in a tab bar driven app or a single view app, whether you are using storyboard, works with IB as well as programmatically, etc. pp.
There is nothing wrong with fetching the window instance from the app's delegate. I just don't see the need for doing so. Seems rather complicated to me compared to the alternatives.
The willRotateToInterfaceOrientation method works well.
In addition to switching views, two other useful things you might want to do in there are:
1) Hide/Show the status bar. (I like to hide it in landscape)
[[UIApplication sharedApplication] setStatusBarHidden:UIInterfaceOrientationIsLandscape(toInterfaceOrientation) withAnimation:UIStatusBarAnimationSlide];
2) Hide/Show any UINavigationBar. (Maybe your landscape view will benefit from the extra height)
[self.navigationController setNavigationBarHidden:UIInterfaceOrientationIsLandscape(toInterfaceOrientation) animated:YES];
You could have one view controller that has the willRotateToInterfaceOrientation method, and that viewcontroller has two other viewcontrollers as variables.
Once the device rotates, you switch the viewcontrollers' views (very crude code example:)
-(void)willRotateToInterfaceOrientation: (UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration {
if ((orientation == UIInterfaceOrientationLandscapeLeft) || (orientation == UIInterfaceOrientationLandscapeRight)) {
[self.secondViewController.view removeFromSuperView];
self.firstViewController.view.frame = self.bounds;
[self.view addSubView:self.firstViewController.view];
} else {
[self.firstViewController.view removeFromSuperView];
self.secondViewController.view.frame = self.bounds;
[self.view addSubView:self.secondViewController.view];
}
}
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.