如何在iOS 7中以编程方式设置设备方向?


72

我正在使用自动布局在iPad应用程序上工作,如果用户启用了某种模式(“平视”模式),则我仅希望支持纵向(或纵向颠倒)方向,并且如果设备处于风景,我想自动切换到人像模式。

在顶视图控制器中,我具有以下内容:

- (NSUInteger) supportedInterfaceOrientations {
    if (self.modeHeadsUp) {
        return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
    } else {
        return UIInterfaceOrientationMaskAll;
    }
}

- (BOOL) shouldAutorotate {
    return TRUE;
}

根据我在其他地方看到的答案,答案似乎是我应该使用“ application setStatusBarOrientation”。因此,在用户选择“抬头”模式的方法中,我包括:

    UIApplication *application = [UIApplication sharedApplication];
    [application setStatusBarOrientation:UIInterfaceOrientationPortrait
                                animated:YES];

但是,这似乎没有任何作用。虽然我可以物理移动设备以使其旋转成纵向,但它不会自动旋转。

实际上,当在运行上述代码以尝试以编程方式设置方向之后以横向模式运行时,当我使用以下代码查询应用程序“ statusBarOrientation”时,对于横向而言,它保持在“ 4”:

UIApplication *application = [UIApplication sharedApplication];
int orientation = [application statusBarOrientation];
self.movesTextView.text = [NSString stringWithFormat:@"ORIENTATION %d", orientation];

似乎没有用setStatusBarOrientation触发自动布局,所以我尝试在此之后添加以下代码,但没有任何效果:

    [super updateViewConstraints];
    [self.view updateConstraints];

我意识到Apple希望将设备定位交给用户。但是,我希望能够在不采用“平视”模式时支持横向模式。

我是否缺少能够强制改变方向的东西?

Answers:


195

对于iOS 7和8:

目标C:

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

迅捷3+:

let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")

我叫它- viewDidAppear:


1
这可行!现在,我担心这会生成一个编译器错误,并且人们似乎对objc_msgSend皱着眉头(请参阅stackoverflow.com/questions/17263354/…),但这有效!我将尝试看看是否有一种方法可以更干净地进行此操作。
约翰·斯图尔特

4
当我尝试此操作时,它仅适用于iPad。iPad Mini(iOS 7.1)和iPhone 5s(iOS 7.1)均不起作用。适用于所有设备的模拟器都可以正常工作
-Jeef

1
@Jeef确保已为应用程序设置了所有定向模式,
Sunny Shah

4
没有动画,有什么办法可以使它起作用?
Jiho Kang 2014年

4
这是黄金,尤其是当您使用UIViewController.attemptRotationToDeviceOrientation()和stackoverflow.com/questions/24928057/…时,您可以获得所需的任何视图设置!
DogCoffee 2015年

21

用这个。定位问题的完美解决方案..ios7和更早版本

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

1
即使在iOS 8上也可以使用。完美。
Josef Rysanek 2014年

1
苹果应用程序审查小组已经接受了此操作(目前),但是我不会说它“被苹果计算机接受”。也就是说,使用键值编码是一种黑客行为,并且可能会在将来的任何iOS版本中停止工作。
亚当·卡普兰

@Arpan应该在哪里添加此代码,我在viewwillapear中添加了但不能正常工作,请解释。
拉姆S

@arpan_techisavy:我尝试了所有方法,didload,willapear,didapear。由于方向问题,我的一个项目卡在了ios8中。我希望所有视图都为纵向,但一个视图(视频播放器)为横向模式,我在情节提要中检查了纵向和左横向模式。我尝试了所有代码,但无法正常工作,我想我缺少了一些东西。请指教,如果您有演示请兔子。
拉姆S

@Josef Rysanek:我已经尝试了所有方法,didload,willapear,didapear。由于方向问题,我的一个项目卡在了IOS-8中。我希望所有视图都为纵向,但一个视图(视频播放器)为横向模式,我在情节提要中检查了纵向和左横向模式。我尝试了所有代码,但无法正常工作,我想我缺少了一些东西。请指教,如果您有演示请兔子。
Ram S

10

当条件发生变化时,您需要致电attemptRotationToDeviceOrientation (UIViewController)以使系统致电您supportedInterfaceOrientations


在我的代码中,按照您的建议,我尝试用[UIViewController tryRotationToDeviceOrientation]替换应用程序setStatusBarOrientation方法。但是,a,这似乎没有效果。
约翰·斯图尔特

我发现这个线程还哪里,而你的建议似乎是“正确答案”,这是行不通的:stackoverflow.com/questions/19125263/...
约翰·斯图尔特

2
如果设备的方向是不同于这仅适用UIViewController的方向
yasirmturk

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

确实有效,但是您必须在视图控制器中返回shouldAutorotate并使用YES

- (BOOL)shouldAutorotate
{
    return self.shouldAutoRotate;
}

但是,如果这样做,如果用户旋转设备,VC将自动旋转...因此我将其更改为:

@property (nonatomic, assign) BOOL shouldAutoRotate;

- (BOOL)shouldAutorotate
{
    return self.shouldAutoRotate;
}

我打电话

- (void)swithInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    self.rootVC.shouldAutoRotate = YES;

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

单击按钮以强制新的方向。要将shouldAutoRotate设置为NO,我将其添加到了rootVC

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    self.shouldAutoRotate = NO;
}

PS:此替代方法也适用于所有模拟器。


1
好笑..我正在搜索类似内容,发现了什么?唯一的iOS 11解决方案,是1.5年前我自己写的。.当我想为该解决方案+1时,我意识到了这一点:D
Rikco

哇...先生,您值得投票,这是迄今为止我所见过的最干净的解决方案
daisura99

4

这对我来说适用于Xcode6和5。

- (BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return (UIInterfaceOrientationMaskPortrait);
}

3

对我有用的唯一方法是呈现虚拟模态视图控制器。

UIViewController* dummyVC = [[UIViewController alloc] init];
dummyVC.view = [[UIView alloc] init];
[self presentModalViewController:dummyVC animated:NO];
[self dismissModalViewControllerAnimated:NO];

关闭模态视图控制器时,将要求您的VC更新界面方向。

奇怪的是,当以不同的受支持的界面方向推送/弹出子视图控制器时,UINavigationController确实做到了这一点(在iOS 6.1、7.0上进行了测试)。


如其他地方所提到的,这不再适用于
iOS8。– mnemia

3

如果您有一个UIViewController应该保持在纵向模式,则只需添加此覆盖就可以了。

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}

最好的部分是显示此视图时没有动画,它只是已经处于正确的方向。


3

此解决方案使您可以通过暂时覆盖的值来强制确定界面方向 UIDevice.current.orientation然后要求系统旋转界面以匹配设备的旋转度:

重要提示:这是一个hack,可能随时会停止工作

在您应用的根视图控制器中添加以下内容:

class RootViewController : UIViewController {
    private var _interfaceOrientation: UIInterfaceOrientation = .portrait
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return UIInterfaceOrientationMask(from: _interfaceOrientation) }
    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return _interfaceOrientation }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Register for notifications
        NotificationCenter.default.addObserver(self, selector: #selector(RootViewController.handleInterfaceOrientationChangeRequestedNotification(_:)), name: .interfaceOrientationChangeRequested, object: nil)
    }

    deinit { NotificationCenter.default.removeObserver(self) }

    func handleInterfaceOrientationChangeRequestedNotification(_ notification: Notification) {
        guard let interfaceOrientation = notification.object as? UIInterfaceOrientation else { return }
        _interfaceOrientation = interfaceOrientation
        // Set device orientation
        // Important:
        // • Passing a UIDeviceOrientation here doesn't work, but passing a UIInterfaceOrientation does
        // • This is a hack, and could stop working at any moment
        UIDevice.current.setValue(interfaceOrientation.rawValue, forKey: "orientation")
        // Rotate the interface to the device orientation we just set
        UIViewController.attemptRotationToDeviceOrientation()
    }
}

private extension UIInterfaceOrientationMask {

    init(from interfaceOrientation: UIInterfaceOrientation) {
        switch interfaceOrientation {
        case .portrait: self = .portrait
        case .landscapeLeft: self = .landscapeLeft
        case .landscapeRight: self = .landscapeRight
        case .portraitUpsideDown: self = .portraitUpsideDown
        case .unknown: self = .portrait
        }
    }
}

extension Notification.Name {
    static let interfaceOrientationChangeRequested = Notification.Name(rawValue: "interfaceOrientationChangeRequested")
}

确保在“部署信息”下选中所有界面方向:

界面方向

在需要的地方请求界面方向更改:

NotificationCenter.default.post(name: .interfaceOrientationChangeRequested, object: UIInterfaceOrientation.landscapeRight)

2

如果您想将应用程序的主视图锁定为纵向视图,但要在横向模式中打开弹出视图,并且将tabBarController用作rootViewController,则可以在AppDelegate上使用此代码。

AppDelegate.h

@interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UITabBarController *tabBarController;

@end

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Create a tab bar and set it as root view for the application
    self.tabBarController = [[UITabBarController alloc] init];
    self.tabBarController.delegate = self;
    self.window.rootViewController = self.tabBarController;

    ...
}

- (NSUInteger)tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController
{
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)tabBarControllerPreferredInterfaceOrientationForPresentation:(UITabBarController *)tabBarController
{
    return UIInterfaceOrientationPortrait;
}

效果很好。

在要以横向显示的viewController中,只需使用以下代码:

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscape;
}

- (BOOL)shouldAutorotate {
    return YES;
}

有趣。重组应用程序后,我将解决这个问题。当前没有容器视图控制器,但无论如何我一直打算将其包装在一个容器中。谢谢。
约翰·斯图尔特2014年

对我来说,最后一块代码工作正常!但我也没有任何UINavigationControllerUITabBarController以其他方式集成,因此也许它有一点区别。
Alex Cio

2
  1. 将此语句添加到 AppDelegate.h

    //whether to allow cross screen marker 
    @property (nonatomic, assign) allowRotation BOOL;
    
  2. 将这段代码写下来 AppDelegate.m

    - (UIInterfaceOrientationMask) application: (UIApplication *) supportedInterfaceOrientationsForWindow: application (UIWindow *) window {
        If (self.allowRotation) {
            UIInterfaceOrientationMaskAll return;
        }
        UIInterfaceOrientationMaskPortrait return;
    }
    
  3. 更改allowRotation委托应用程序的属性


欢迎使用StackOverflow。您能否更好地格式化代码以使其更具可读性?
slfan

1

基本的UINavigationController应该具有以下回调,以便子项可以确定其所需的方向。

-(NSUInteger)supportedInterfaceOrientations {
    UIViewController *topVC = self.topViewController;
    return topVC.supportedInterfaceOrientations;
}

-(BOOL)shouldAutorotate {
   UIViewController *topVC = self.topViewController;
   return [topVC shouldAutorotate];
}

1

如果只需要纵向模式,则在iOS 9(Xcode 7)中,您可以:

  • 转到Info.plist
  • 选择“支持的界面方向”项
  • 删除“风景(左主页按钮)”和“风景(右主页按钮)”

在此处输入图片说明


1

我遇到了与您类似的问题。我需要锁定某些屏幕的设备方向(例如“登录”),并允许其他屏幕旋转。

经过一些更改并在下面给出了一些答案之后,我通过以下方式做到了:

  • 在项目的Info.plist中启用所有方向。

在此处输入图片说明

  • 在我不需要设备旋转的ViewController中禁用方向,例如在我的登录屏幕中。我需要重写shouldAutorotate此VC中的方法:

-(BOOL)shouldAutorotate{ return NO; }

希望这对您有用。


1

这是iOS 7,8,9,10的FULL WORKING示例,如何将应用程序方向更改为其当前相反的方向

物镜

- (void)flipOrientation
{
    NSNumber *value;
    UIInterfaceOrientation currentOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    if(UIInterfaceOrientationIsPortrait(currentOrientation))
    {
        if(currentOrientation == UIInterfaceOrientationPortrait)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown];
        }
        else //if(currentOrientation == UIInterfaceOrientationPortraitUpsideDown)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
        }
    }
    else
    {
        if(currentOrientation == UIInterfaceOrientationLandscapeRight)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
        }
        else //if(currentOrientation == UIInterfaceOrientationLandscapeLeft)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
        }
    }
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    [UIViewController attemptRotationToDeviceOrientation];
}

迅捷3

func flipOrientation() -> Void
{
    let currentOrientation : UIInterfaceOrientation = UIApplication.shared.statusBarOrientation
    var value : Int = 0;
    if(UIInterfaceOrientationIsPortrait(currentOrientation))
    {
        if(currentOrientation == UIInterfaceOrientation.portrait)
        {
            value = UIInterfaceOrientation.portraitUpsideDown.rawValue
        }
        else //if(currentOrientation == UIInterfaceOrientation.portraitUpsideDown)
        {
            value = UIInterfaceOrientation.portrait.rawValue
        }
    }
    else
    {
        if(currentOrientation == UIInterfaceOrientation.landscapeRight)
        {
            value = UIInterfaceOrientation.landscapeLeft.rawValue
        }
        else //if(currentOrientation == UIInterfaceOrientation.landscapeLeft)
        {
            value = UIInterfaceOrientation.landscapeRight.rawValue
        }
    }
    UIDevice.current.setValue(value, forKey: "orientation")
    UIViewController.attemptRotationToDeviceOrientation()
}

此解决方案使用了反射技术,因为iOS没有/未显示任何公共方法来实现此目标,因此我们必须通过反射来完成
ddb

好吧,最终,我们正在修改Apple不想让我们做的事情:UIDeviceorientation属性仅是就绪的。您曾经遇到过拒绝此代码的应用程序吗?
Sunil Chauhan

抱歉,但我无法回答,因为我使用此代码的应用程序是在iTunes外部发布的,您是否尝试提交过这样的代码?
ddb

1

对于像我一样努力获得@Sunny Shah的人,他们接受了在iPad上工作的答案。您需要在项目设置中选中“要求全屏显示”复选框。请注意,这将阻止您的应用在多任务模式下工作,这可能会接受,也可能无法接受。

在此处输入图片说明


0

与您的代码一起尝试。

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation  

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

一旦用户选择了任何选项,便会调用此方法,因为用户可以处于横向模式,然后他只能在同一视图控制器中设置纵向模式,因此应自动将视图移至纵向模式,因此该按钮acton会调用此方法

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

我不太确定您的建议,但是在iOS7中应该不应该使用shouldAutorotateToInterfaceOrientation,而应该使用我已经实现的shouldAutorotate。
约翰·斯图尔特

抱歉,是我的错。仅使用shouldAutorotate和-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation持续时间:(NSTimeInterval)duration
Charan Giri

我添加[self willRotateToInterfaceOrientation:UIInterfaceOrientationPortrait duration:ROTATE_VIEW_DELAY];了选择平视模式的方法,替换了上面的Sunny Shah的objc_msgSend答案。las,仍然对显示屏没有影响。据我了解,当显示器即将旋转时,将响应用户的物理移动设备调用“将旋转”。
约翰·斯图尔特

@John Stewart我们可以独立调用此方法...但是请告诉我您在此方法中编写的代码是什么。如果可能的话,您能否使用针对方向编写的所有代码(您使用的所有方法)来更新您的问题
Charan Giri 2014年

Charan Giri-我创建了一个简单的测试项目来演示:github.com/johnstewart/RotateTest。该项目是一个带有ImageView的简单iPad应用程序,以及一个用于打开和关闭“仅纵向”的开关。我从一个空白的仅查看项目开始,并添加了此属性:@property (nonatomic) bool modePortraitOnly;
John Stewart

0

这对我非常有效。

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

0

2020年Swift 5:

override var supportedInterfaceOrientations:UIInterfaceOrientationMask {
    return .portrait
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.