如何在iOS 6中通过编程方式更改设备方向

46

iOS 5中,我们可以通过编程方式来更改设备的方向:

[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];

但在 iOS 6 中,setOrientation 已被弃用,我该如何在编程中更改设备方向?


请查看此链接:https://dev59.com/mm855IYBdhLWcg3wc0Dy#4331014 - Bala
3
最好您能接受答案。这将提高您的采纳率。 - Dinesh Raja
Swift 4: -> https://dev59.com/61wY5IYBdhLWcg3wq5Xk#51036885Swift 4 是苹果公司发布的一种编程语言,用于开发 iOS、macOS、watchOS 和 tvOS 应用程序。它具有许多新特性和改进,例如 Codable 协议、原生字符串、强大的错误处理机制等。如果你是一名 iOS 或 macOS 开发者,学习 Swift 4 肯定会使你受益匪浅。 - Jack
15个回答

49

这是我的“五分之一”,在具有ARC的iOS7上进行了测试。

[[UIDevice currentDevice] setValue:
                      [NSNumber numberWithInteger: UIInterfaceOrientationPortrait]
                            forKey:@"orientation"];

performSelector 会产生“泄漏”警告,而这不会。

UIAlertView - 使用此代码,在视图(will/Did)appear期间打开UIAlertView时,您会注意到除了此视图之外,所有视图都处于纵向状态(苹果,真的吗?)。我无法强制视图重新定位,但发现如果在打开UIAlertView之前稍微延迟一下,视图就有时间改变方向。

注意:我将在2014年12月9日发布我的应用程序,如果它通过或未通过审核,我将更新帖子。


1
它可以工作,但是警告视图仍然以纵向模式显示在设备上。 - User-1070892
苹果是否接受使用这种方法开发的应用程序? - hariszaman
我很快就会找到答案。 - Lukasz 'Severiaan' Grela
3
苹果是否拒绝了这个?你有查到吗?@Lukasz'Severiaan'Grela - alper_k
试过并成功时,我无法描述那种兴奋感!请问应用程序通过了吗? - Calios
显示剩余7条评论

20

以下内容并不回答如何更改设备方向,但它是一些可能对你有帮助的额外信息。

iOS 6 UI界面方向 - shouldAutorotateToInterfaceOrientation: 不起作用

shouldAutorotateToInterfaceOrientation:方法在iOS 6中已经不再被支持。如果你是一个刚开始接触cocoa编程的新手,并且想知道为什么你的视图控制器在iOS 6中混乱而在iOS 5中完美无缺,那么请注意shouldAutorotateToInterfaceOrientation:已经不再被支持了。即使它在Xcode 4到4.3版本中运行良好,它也不会在Xcode 4.5上工作。

苹果提供了一种更加干净的方法来解决这个问题,你可以使用supportedInterfaceOrientations代替。它返回一个掩码值,表示视图控制器所支持的所有界面方向。

UIInterfaceOrientationMask枚举:

这些常量是用于指定视图控制器支持的界面方向的掩码位。

typedef enum {
    UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
    UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
    UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
    UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
    UIInterfaceOrientationMaskLandscape =
        (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
    UIInterfaceOrientationMaskAll =
        (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |
    UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
    UIInterfaceOrientationMaskAllButUpsideDown =
        (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |
    UIInterfaceOrientationMaskLandscapeRight),
} UIInterfaceOrientationMask;

使用 shouldAutorotateToInterfaceOrientation: 方法:

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return UIInterfaceOrientationIsLandscapeRight(toInterfaceOrientation);
}

使用 supportedInterfaceOrientations 方法:

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight;
}

iOS6中UIViewController新增了以下关于方向的方法:

  1. preferredInterfaceOrientationForPresentation:设置视图控制器首选呈现方向。

  2. shouldAutorotate:指示视图控制器是否支持自动旋转。

  3. supportedInterfaceOrientations:返回当前视图控制器支持的所有方向。

iOS6中UIApplication新增了以下关于方向的方法:

  1. supportedInterfaceOrientationsForWindow::返回应用程序在给定窗口中所支持的所有方向。

  2. UIInterfaceOrientationMask:定义界面支持的方向组合。


3
谢谢您的回答,但我想通过编程方式更改方向。我该如何更改方向,就像我们在iOS5中使用[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];一样。 - uttam
这是我的问题 @Bala https://dev59.com/5GUp5IYBdhLWcg3wCUGx - Goutham
4
这并没有回答如何通过编程改变设备方向的问题。 - Justin Tanner
2
那不是问题的答案,更像是额外的信息。 - alper_k

20

我发现强制设备改变方向的最简单方法是通过呈现新的视图控制器(使用presentViewController:animated:completion:),其中新的视图控制器指定了特定的首选方向(通过实现方法-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation)。

当呈现一个新的视图控制器时,预期的结果是,方向将更改为新视图控制器所希望的方向。因此,最简单的实现方式(最佳实践?)是将您需要的所有功能嵌入到一个特定方向的单独视图控制器中,并根据需要呈现它。系统将为您处理方向更改。

显然,这可能不适用于所有用例,但幸运的是,同样的技巧也适用于强制设备更改现有视图控制器的方向。

技巧是呈现一个具有所需首选方向的新视图控制器,然后立即隐藏它。这会在呈现新视图控制器时导致方向临时更改。最好的部分是,当新视图控制器被解除显示时,原始(呈现)视图控制器的preferredInterfaceOrientationForPresentation将再次查询,您可以在此处指定最终方向。

在此要注意的一个重要事项是,在从新呈现-然后解除显示的视图控制器返回时,还要临时禁用原始视图控制器中的自动旋转,这样当用户将其手机旋转到新方向时,它不会触发进一步的自动旋转。

以下代码应该说明我的观点,我的示例强制旋转为纵向,如果您想要其他方向,请相应更改。

假设您将原始视图控制器命名为Original,并将一个临时视图控制器命名为ForcePortrait

@interface Original : UIViewController
{
    BOOL orientationToPortrait; //should set to NO by default
}
@end

@implementation Original
- (UIInterfaceOrientation) preferredInterfaceOrientationForPresentation
{
    if(orientationToPortrait)
    {
        //when we manually changed, show in Portrait
        return UIInterfaceOrientationPortrait;
    }
    else
    {
        //before manual orientation change, we allow any orientation
        return self.interfaceOrientation;
    }
}

-(BOOL) shouldAutorotate
{
    //we should 'lock' the rotation once we manually change it
    return !orientationToPortrait;
}

-(void) changeOrientationToPortrait
{
    //Sample method to change the orientation
    //when called, will show (and hide) the temporary view
    //Original.preferredInterfaceOrientationForPresentation will be called again after this method

    //flag this to ensure that we tell system we prefer Portrait, whenever it asked again
    orientationToPortrait = YES;

    //presenting the following VC will cause the orientation to temporary change
    //when the new VC is dismissed, system will ask what is our (Original) orientation preference again
    ForcePortrait* forcePortrait = [[ForcePortrait alloc] init];
    [self presentViewController:forcePortrait animated:NO completion:^{
        [forcePortrait dismissViewControllerAnimated:NO completion:nil];
    }];
}


@end

@interface ForcePortrait : UIViewController
@end

@implementation ForcePortrait
- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationPortrait;
}
@end

非常优雅的解决方案。在我的iOS 7上完美运行! - Nico
1
一个很好的解决方案,但如果我尝试从父视图控制器弹出到任何视图控制器,它会失败。 - hokiewalrus
有没有办法强制这个更改进行动画?我正在推送一个从图库(相机胶卷)中获取的图像选择器控制器,它会非动画地旋转状态栏,并在新的视图控制器过渡动画之前不会对我的视图中的任何其他内容进行动画处理。 - Dennis L

10

试试这个:

#import <objc/message.h>

if(UIDeviceOrientationIsLandscape(self.interfaceOrientation)){
        if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)])
        {
            objc_msgSend([UIDevice currentDevice], @selector(setOrientation:), UIInterfaceOrientationPortrait );
        }
    }

2
这个选项可能会被苹果拒绝,但对于企业应用程序来说,这正是我所需要的。 - Austin
它运行得非常完美!但是在隐藏状态栏后,它变得可见了。 - nesimtunc
1
@JustinTanner,它确实可以在iOS 7中工作。但是你不需要#import <objc/message.h>。 - Jared Price
它在IOS7.1的开发环境中运行良好。 然而,当我部署到应用商店时,在IPhone5s上无法工作。 有什么猜测吗? - srcnaks
1
这不是一个好的做法,也不符合苹果的指南 - 令人好奇的是你的应用程序没有因为使用私有库而被拒绝。我只是用它进行内部开发。 - Bissy
@Bissy,这在iPad Air的iOS 7.1.1上不起作用。虽然在iPad 3的iOS 7.1.1上完美运行。有什么特别的原因/解释吗? - Meet

5

您应该在AppDelegate的didFinishLaunchingWithOptions方法中添加以下代码:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

这样,在您的应用程序的任何地方,您都可以使用以下代码获取当前方向:

UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];

使用以下命令测试方向:

UIInterfaceOrientationIsPortrait(orientation) 
UIInterfaceOrientationIsLandscape(orientation)

as, like

if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation))
{
    // code for landscape orientation
     // OR
    [[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
     //  OR
    [[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeLeft];

}
else if (UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation))
{
    // code for Portrait orientation
    //  OR
   [[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortraitUpsideDown];
    //  OR
   [[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
}

太棒了!在iOS6之前不需要使用beginGeneratingDeviceOrientationNotifications,但现在你需要加上这一行。非常感谢! - borisdiakur
3
"Receiver type 'UIDevice' for instance message does not declare a method with selector 'setOrientation:'" 的翻译是:"实例消息的接收器类型 'UIDevice' 没有声明方法 'setOrientation:'"。 - matt
[[UIDevice currentDevice] setOrientation:] 的目的是什么?设备不会在旋转时自动设置自己的方向吗? - zakdances
整个星期都在寻找解决方案,最终只有这段代码才能正常工作!特别是在iPad上使用相机预览并且设备横向定位时,几乎不可能强制应用程序定位为纵向。感谢您的回答! - Benny7500
看起来有点混乱。UIDevice方向属性是只读的。即使如此,这里还是试图使用UIInterfaceOrientation值来设置UIDevice方向属性。 - Jeff
它显示警告:“从枚举类型隐式转换为不同的枚举类型”...? - Jayprakash Dubey

4

这段代码适用于iOS 8或更高的版本。

NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];

我写了相同的代码,它可以正常工作,但是如果我使用 [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight],那么它也会朝着同一个方向旋转(逆时针)。我想要从左到右旋转(顺时针方向旋转)。 - Dipak
不应该使用此 Apple 私有 API - 一旦他们将方向字符串更改为其他任何内容,您的应用程序就会崩溃。 - XcodeNOOB

2

试试这个...对我很有效...

UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview]; [window addSubview:view];

2

这适用于iOS7,强制自动旋转为竖屏。

//In your viewController.m
#import <objc/message.h>

// for autorotate viewController to portraid
- (void)viewWillAppear:(BOOL)animated {
    UIInterfaceOrientation orientationStatusBar =[[UIApplication sharedApplication] statusBarOrientation];
    switch (orientationStatusBar) {
        case UIInterfaceOrientationPortrait:break;
        case UIInterfaceOrientationLandscapeLeft:
            objc_msgSend([UIDevice currentDevice], @selector(setOrientation:), UIInterfaceOrientationPortrait);
            break;
        case UIInterfaceOrientationLandscapeRight:
            objc_msgSend([UIDevice currentDevice], @selector(setOrientation:), UIInterfaceOrientationPortrait);
            break;
        default:
            break;
    }
}

// this permit autorotate
- (BOOL) shouldAutorotate
{
   // this lines permit rotate if viewController is not portrait
    UIInterfaceOrientation orientationStatusBar =[[UIApplication sharedApplication] statusBarOrientation];
    if (orientationStatusBar != UIInterfaceOrientationPortrait) {
        return YES;
    }
    //this line not permit rotate is the viewController is portrait
    return NO;
}

注意:我在我的应用程序中实现了这个选项,但可能会被苹果拒绝(注释给Austin,由Sergey K.在2012年10月编辑第6版)。


2
苹果在 iOS6 中故意让通过编程方式改变设备方向变得相当困难(请注意)。
据我所知,实现您所要求的唯一方法是模拟设备方向的变化。
使用 setTransform 旋转 UIView 并重新应用其自身框架可以得到期望的结果。
[YourView setTransform:CGAffineTransformMakeRotation(1.57)];
[YourView setFrame:CGRectMake(0, 0, YourView.frame.size.width, YourView.frame.size.height)];

当设备的物理方向改变时,我们可以撤消变换。
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [YourView setTransform:CGAffineTransformMakeRotation(0)];
    [YourView setFrame:CGRectMake(0, 0, YourView.frame.size.width, YourView.frame.size.height)];
}

2
稍微修改一下Bissy的答案,如果你想避免使用Runtime库:
if (UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation]))
{
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)])
    {
        int orientationPortrait = UIInterfaceOrientationPortrait;
        NSMethodSignature *sig = [[UIDevice currentDevice] methodSignatureForSelector:@selector(setOrientation:)];
        NSInvocation* invo = [NSInvocation invocationWithMethodSignature:sig];
        [invo setTarget:[UIDevice currentDevice]];
        [invo setSelector:@selector(setOrientation:)];
        [invo setArgument:&orientationPortrait atIndex:2];
        [invo invoke];
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接