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

76

我正在开发一个iPad应用程序,使用AutoLayout。如果用户启用了某种模式(“悬浮”模式),我希望仅支持竖屏(或倒置的竖屏)方向,并且如果设备处于横屏状态,我想自动切换到竖屏模式。

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

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

- (BOOL) shouldAutorotate {
    return TRUE;
}
根据我在其他地方看到的答案,似乎应该使用"application setStatusBarOrientation"。因此,在用户选择“heads-up”模式的方法中,我已包含如下内容:
    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];

我知道苹果希望将设备方向掌握在用户手中。不过,当处于非“heads-up”模式时,我希望能支持横屏模式。

我是否遗漏了什么来强制更改方向?

18个回答

201

对于iOS 7和8:

Objective-C:

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

Swift 3+:

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

我称之为 - viewDidAppear:


1
这个可行!现在,我担心这会生成编译器错误,并且人们似乎非常反感objc_msgSend(参见https://dev59.com/iGQm5IYBdhLWcg3w4CPA),但这个可行!我将尝试看看是否有更清洁的方法来实现这个。 - John Stewart
4
当我尝试这个时,它只在iPad上有效。无论是iPad Mini(iOS 7.1)还是iPhone 5s(iOS 7.1),都无法使用。所有设备的模拟器都可以使用。 - Jeef
1
@JohnStewart,这种方法没有问题。我在多个应用程序中使用过,每次苹果都接受它。 - Sunny Shah
4
有没有办法让这个工作 带动画效果? - Jiho Kang
4
这很棒,特别是当你使用UIViewController.attemptRotationToDeviceOrientation()和https://dev59.com/zGAf5IYBdhLWcg3wMwM9时,你可以得到任何你想要的视图设置! - DogCoffee
显示剩余9条评论

21

使用此方法。解决方向问题的完美解决方案。适用于iOS7及更早版本。

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

1
甚至在iOS 8上也能正常运行。完美。 - Josef Rysanek
2
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Adam Kaplan
@Arpan 这段代码应该放在哪里?我已经放在viewwillapear中了,但是没有起作用,请解释一下。 - Ram S
@Josef Rysanek:我已经尝试过所有的didload、willapear和didapear。我的一个项目因为方向问题而卡在了IOS-8上。我想让所有的视图都是竖屏,但是有一个视图(视频播放器)需要横屏模式,我已经在storyboard中检查了竖屏和左侧横屏模式。我已经尝试了所有的代码,但是没有效果,我觉得我可能漏掉了什么。请给予建议,如果您有演示,请分享。 - Ram S
@arpan_techisavy 我怎样才能知道这个方法是否已经执行完毕? - KarenAnne
显示剩余3条评论

10

1
在我的代码中,我尝试使用[UIViewController attemptRotationToDeviceOrientation]替换应用程序setStatusBarOrientation方法,正如您建议的那样。然而,不幸的是它似乎没有任何效果。 - John Stewart
我也发现了这个帖子,虽然你的建议似乎是“正确答案”,但它却不起作用:https://dev59.com/cHfZa4cB1Zd3GeqPSH2y - John Stewart
3
仅当设备方向与“UIViewController”的方向不同时,此功能才起作用。 - yasirmturk

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

这个功能可以正常工作,但是您需要在您的视图控制器中返回shouldAutorotate为YES。

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

但是如果您这样做,当用户旋转设备时,您的视图控制器将自动旋转...所以我将其更改为:

@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,我在我的根视图控制器中添加了以下内容

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

PS:这种解决方法在所有模拟器中都有效。


1
有趣的是,我正在搜索类似的东西,然后我找到了什么?唯一适用于iOS 11的解决方案,而我在1.5年前就已经自己写好了...当我想要为这个解决方案点赞时,我才意识到这一点:D - Rikco
哇...先生,您应该得到一个赞,这是我见过的最干净的解决方案。 - daisura99

4
这对我来说在 Xcode 6和5上都有效。
- (BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return (UIInterfaceOrientationMaskPortrait);
}

3

如果您有一个应该保持竖屏模式的UIViewController,只需添加这个重写方法就可以了。

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

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

3
我唯一有效的方法是呈现虚拟模态视图控制器。
UIViewController* dummyVC = [[UIViewController alloc] init];
dummyVC.view = [[UIView alloc] init];
[self presentModalViewController:dummyVC animated:NO];
[self dismissModalViewControllerAnimated:NO];

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

有趣的是,当使用不同支持的界面方向推入/弹出子视图控制器时(在iOS 6.1、7.0上测试),UINavigationController确实会执行此操作。


如其他地方所提到的,这在iOS 8上已不再适用。 - mnemia

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")
}

请确保在“部署信息”下选中所有接口方向:

Interface Orientations

在需要的地方请求接口方向更改:
NotificationCenter.default.post(name: .interfaceOrientationChangeRequested, object: UIInterfaceOrientation.landscapeRight)

2
  1. Add this statement into AppDelegate.h

    //whether to allow cross screen marker 
    @property (nonatomic, assign) allowRotation BOOL;
    
  2. Write down this section of code into AppDelegate.m

    - (UIInterfaceOrientationMask) application: (UIApplication *) supportedInterfaceOrientationsForWindow: application (UIWindow *) window {
        If (self.allowRotation) {
            UIInterfaceOrientationMaskAll return;
        }
        UIInterfaceOrientationMaskPortrait return;
    }
    
  3. Change the allowRotation property of delegate app


欢迎来到StackOverflow。您能否将代码格式化得更好一些,以使其更易读? - slfan

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;
}

它非常有效。

如果您希望在视图控制器中以横向方式呈现,只需使用以下代码:

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscape;
}

- (BOOL)shouldAutorotate {
    return YES;
}

有趣。当我重构我的应用程序时,我会试着玩一下这个。目前还没有容器视图控制器,但我一直打算包装它。谢谢。 - John Stewart
对我来说,最后一段代码运行良好!但是我也没有集成任何UINavigationControllerUITabBarController,所以可能会有一些不同。 - Alex Cio

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