Since iOS6, I realize that the shouldAutorotateToInterfaceOrientation: method has been deprecated. Most of my app I would like the user to be able to rotate, which does work in iOS6 and 5 currently. But, I have a modal view that I ONLY want to be portrait, so I have added the following without it actually working (tested in simulator and device):
// Tell the system what we support
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationPortrait;
}
// Tell the system It should autorotate
- (BOOL) shouldAutorotate {
return NO;
}
// Tell the system which initial orientation we want to have
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
Why isn't this code preventing my modal from rotating?
How can I still support the iOS5 method as well as the iOS6 methods without crashing for users on iOS5?
You have to embed the presented vc in a navigation controller where you can set the preferred orientation.
https://stackoverflow.com/a/12522119
You missed to but this line inside your -(void)viewDidLoad method
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"UIApplicationSupportedInterfaceOrientationsIsEnabled"];
I hope this can help you
Related
My game is going into background mode when performing a swipe from the bottom edge of the screen on iPhone X iOS 12.
As per Apple documentation overriding preferredScreenEdgesDeferringSystemGestures and calling setNeedsUpdateOfScreenEdgesDeferringSystemGestures should stop the app from going to background but this is's not working on iOS 12.
I am using Unity3D and the editor has the Defer system gestures on edges option , which is implemented as per apple documentation, but also does not work.
I am compiling the project in Xcode 10.
Does anyone else have this problem and do you have a fix?
PS: I am testing this in an empty single view iOS project, the only added code is the following:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear: animated];
[self setNeedsUpdateOfHomeIndicatorAutoHidden];
[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
}
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
{
return UIRectEdgeAll;
}
- (BOOL)prefersHomeIndicatorAutoHidden
{
return YES;
}
Update: Turns out that if I use a swift implementation it works. Too bad I cannot do this for the Unity3D 2017 generated project.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 11.0, *){
setNeedsUpdateOfScreenEdgesDeferringSystemGestures()
}
}
override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge{
return [.all];
}
}
New Update: In Unity 2019 it works by unchecking "Status Bar Hidden" in Project Stttings\Resolution and presentation and making sure you check at least one edge in Poject Settings\Other Settings\Defer system gestures on edges
Removing prefersHomeIndicatorAutoHidden makes it work in Objective C also.
This is the working example implementation, for anyone having the same problem:
- (void)viewDidLoad {
[super viewDidLoad];
if (#available(iOS 11.0, *)) {
[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
}
}
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
{
return UIRectEdgeAll;
}
And for those who, like me, are using Unity3d just delete the following method from UnityViewControllerBase+iOS.mm in the generated Xcode project:
- (BOOL)prefersHomeIndicatorAutoHidden
{
return YES;
}
As per the apple documentation, preferredScreenEdgesDeferringSystemGestures doesn't stop the app from going to background, it just gives your gesture precedence over system gesture.
However, if you try to do it successively a second time, the system gesture would work. You can easily verify this by comparing with other apps.
By default the line at the bottom which helps in swiping up is black in colour and the swipe up gesture would work instantly if you do not override this method. But in your app, the line will look gray'ed out initially. If you do a swipe up, it will become black again and if you swipe up a second time, the system gesture will work.
I am putting this as an answer because of limited characters for commenting.
For Swift the answer is to override the instance property like so within your UIViewController.
override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
get { .all }
}
for example.
I have developed a game in cocos2d and all game screens are in Landscape mode. I am trying to implement game Center but getting crash on authentication. I did not find answer of similar type of issues. please suggest right approach...
Crash issue:-'UIApplicationInvalidInterfaceOrientation', reason: 'Supported orientations has no common orientation with the application, and shouldAutorotate is returning YES'
I tried below solution but it also disturb game orientations, game starts work in portrait mode also, that i don't want:-
(NSUInteger)application:(UIApplication*)application
supportedInterfaceOrientationsForWindow: (UIWindow*)window
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
Make sure you selected landscape in Xcode summary page.
Also add these code in your viewcontroller
-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
Update this function in AppDelegate:
- (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
{
return UIInterfaceOrientationMaskLandscape;
}
the solution for that is short, i spent a lot of time before finding it:
in the AppDelegate in the method didFinishLaunchingWithOptions put this line:
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
obviously before call the login game center method, i put that before create the UIWindows
I have an iPhone app with a root view controller (VC) of UITabBarController (set to portrait orientation) with several tabs, one of which is a simple UIViewController. In that UIViewController is a single button - "Play Video", which, when clicked opens a modal view of the video (and automatically starts playing the video). The video view is a UIWebView in a UIViewController. I've been trying to get the Web View's VC to change orientation to landscape but have not had any luck.
I've looked around and understand that if you have a Tab Bar or a Nav controller, all children VCs will be the same orientation as the parent - makes sense. This is why I made the web view's VC modal, hoping this is a way around the orientation issue.
My question is: is this accurate - that using modal will not require the web view VC to be portrait and can respond to the shouldAutorotateToInterfaceOrientation method (even though I have not yet been able to get it to work)?
BTW, using iOS 6.
Thanks in advance.
Apparently in ios6 and above, the way rotation works is different. So what you have to do is the following
In your .plist support all 4 orientations.
Subclass the UITabBarController (for e.g: CustomTabBarController)
In the CustomTabBarController put the following lines of code
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
In your app delegate or where ever you are initializing UITabBarController, replace those instances with CustomTabBarController instances.
In your modal controller put the lines
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeLeft;
}
-(BOOL)shouldAutorotate{
return NO;
}
And it should all work.
Apparently the trick, I found is that, UITabBarController will not listen to your instructions. It will support all the orientations you mention in the .plist.
There fore you have to subclass it.
I tried doing all of the above and it works fine. Do let me know and I can send you the code if you want.
Try this. Just have portrait set in the summary screen, then in the app delegate, implement this:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAll;
}
In the tab bar controller (and no other rotation code):
-(BOOL)shouldAutorotate {
return NO;
}
And finally, in the modal view controller:
-(BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
I am having custom split view controller in my App with a master controller and a detailed controller.
- (id)initWithMasterController:(UIViewController*)aMasterController
detailedController:(UIViewController*)aDetailedController;
The controllers provided for the master controller and details controller are UINavigationController.
As part of my app, there are two possible cases for orientation handling:
When six combination of controllers are used in master and details controller, all the orientations are supported for the app.
When there is a StudentDetailsViewController at the details controller alone, only two possible orientations can be supported. (Landscape)
When ever the device's orientation is changed, the below things happen in versions below iOS 6.0
The -shouldAutorotateToInterfaceOrientation: method gets called. The implementation of that method is below: At run time, I forward the request to master controller and details controller with the same call.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
BOOL res = [masterController shouldAutorotateToInterfaceOrientation:interfaceOrientation]
&& [detailedController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
return res;
}
The masterController's -shouldAutorotateToInterfaceOrientation will return TRUE. The implementation of the method in StudentViewController is below.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (IS_IPAD) ? UIInterfaceOrientationIsLandscape(interfaceOrientation)
: UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
The ability to get information on the new orientation to be changed helps me to decide if rotation should be enabled or not.
With iOS 6.0:
When ever the device's orientation is changed, the below things happen in versions of iOS 6.0
The method -shouldAutorotate of the split view controller gets called. Its implementation is below
- (BOOL)shouldAutorotate {
BOOL res = [masterController shouldAutorotate]
&& [detailedController shouldAutorotate];
return res;
}
The detailedController's shouldAutorotate calls the navigationController. The implementation of autorotate feature in StudentsController:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return (UIInterfaceOrientationMaskLandscapeLeft
| UIInterfaceOrientationMaskLandscapeRight);
}
But with iOS 6.0, I am unable to control the orientation. Even though the supportedInterfaceOrientations method gets called, when the shouldAutorotate method of the StudentsDetailsController gets called, from the detailsController's shouldAutorotatemethod, the shouldAutorotateMethod does not obey the options mentioned in the supportedInterfaceOrientations method.
UPDATE:
I read the docs and the below notes are provided in the document.
sometimes you may want to dynamically disable automatic rotation. For
example, you might do this when you want to suppress rotation
completely for a short period of time. You must temporarily disable
orientation changes you want to manually control the position of the
status bar (such as when you call the
setStatusBarOrientation:animated: method).
If you want to temporarily disable automatic rotation, avoid
manipulating the orientation masks to do this. Instead, override the
shouldAutorotate method on the topmost view controller. This method is
called before performing any autorotation. If it returns NO, then the
rotation is suppressed.
Is it possible to temporarily disable automatic rotation based on the current orientation?
I believe this is some type of issue in iOS where the rootViewController does not consult the childViewController for their preferred orientation. However, you should try something like the following :
if (self.interfaceOrientation != UIInterfaceOrientationPortrait)
{
[[UIDevice currentDevice] performSelector:NSSelectorFromString(#"setOrientation:") withObject:(id)UIInterfaceOrientationPortrait];
}
to change the orientation back to portrait for a given view.
In your application delegate class define the following method, this method gets called before any other rotation methods in application.
Create a flag(isRotationEnabled) which will decide orientation of your app.
- (NSUInteger) application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return self.isRotationEnabled ?
UIInterfaceOrientationMaskAll :
UIInterfaceOrientationMaskPortrait;
}
Change this flag based on different conditions in you app using the following code
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.isRotationEnabled = NO;
I have a table view with some words, and i present flash-card style landscape view when the device rotates. I made it by observing the "UIDeviceOrientationDidChangeNotification".
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(openLandscapeMode) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
1)That works fine and smooth, but the problem is that when the we are in the landscape, i don't want the viewcontroller to react to the spinning around the vertical axis,so that i could lay the phone on the table and it would still be in the landscape.
Maybe i should somehow observe the horizontall spinnings, instead of deviceorientation?
-(void)openLandscapeMode
{
if([[UIDevice currentDevice]orientation]==UIDeviceOrientationLandscapeLeft||[[UIDevice currentDevice]orientation]==UIDeviceOrientationLandscapeRight)
{
LandscapeCardViewController *landscape = [[LandscapeCardViewController alloc]init];
landscape.words = words;
landscape.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:landscape animated:YES];
NSLog(#"Switch to %#",[[NSUserDefaults standardUserDefaults]valueForKey:#"ChosenWordInCard"]);
[landscape release];
}
else
{
[self dismissModalViewControllerAnimated:YES];
[[UIApplication sharedApplication]setStatusBarOrientation:UIInterfaceOrientationPortrait];
}
}
2)The second question is where to remove observer, if this controller is in a tab bar, and i want to perform the same transition in another controller in the same tabbar,but,of course,with another landscape view?
I tried in viewWillDissappear, but it doesn't work properly.
Thanks a lot!
For your first question, there should be a method in your viewcontroller which you may need to edit to only support portrait
-(BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation {
return UIInterfaceOrientationPortrait(interfaceOrientation); //only allow portrait
}
That will stop it auto rotating to landscape, while keeping your original method intact
For the second. What about when the transition is complete? Then re-add it when the view appears again. And then in your landscape controller, add it to re-detect when the device is portrait.
I found the solution
I changed else to if([[UIDevice currentDevice]orientation]==UIDeviceOrientationPortrait||[[UIDevice currentDevice]orientation]==UIDeviceOrientationPortraitUpsideDown) and everything works fine!
Strange, but it works!
About removing the observer - i do it in -viewWillAppear,checking,if i am not in landscape now.