检测iOS设备方向

75

我需要检测设备是否处于竖屏模式,以便启动特殊的动画。但我不希望我的视图自动旋转。

当设备旋转到竖屏时,如何覆盖视图的自动旋转?我的应用程序只需要在横向显示视图,但似乎如果我要能够检测到旋转到竖屏的情况,也需要支持竖屏模式。


注意:现在更现代的做法是使用viewDidLayoutSubviews,它将处理所有布局更改。(请记住,在不久的将来,用户将在设备上调整应用程序大小。)响应式布局现在是常态。 - Fattie
9个回答

180

尝试在应用程序加载或视图加载时执行以下操作:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]
   addObserver:self selector:@selector(orientationChanged:)
   name:UIDeviceOrientationDidChangeNotification
   object:[UIDevice currentDevice]];

接着添加以下方法:

- (void) orientationChanged:(NSNotification *)note
{
   UIDevice * device = note.object;
   switch(device.orientation)
   {
       case UIDeviceOrientationPortrait:
       /* start special animation */
       break;

       case UIDeviceOrientationPortraitUpsideDown:
       /* start special animation */
       break;

       default:
       break;
   };
}

通过上述方法,您可以在不启用视图自动旋转的情况下注册设备方向更改操作。


注意事项

iOS中的所有情况下,当您添加观察者时,请同时在适当的时候将其移除(可能是视图出现/消失时,但不总是)。您只能拥有“观察/取消观察”代码对。如果您不这样做,应用程序将崩溃。选择何时观察/取消观察超出了此QA的范围。然而,您必须有一个与上面的“观察”代码匹配的“取消观察”代码。


在此需要特别注意的是,(int)device.orientation属性在竖屏模式下可能为1或2,在横屏模式下可能为3或4。还有一个值为"5",表示设备只是放在桌子上,如果使用UIDeviceOrientationIsLandscape(device.orientation)进行检查,则会返回"横屏"。即使您的应用程序/设备处于竖屏模式,也可能得到这个"横屏"答案。 - emrahgunduz
1
UIDevice在应用程序启动时不会提供方向,直到应用程序更改方向。 iOS>5的另一种可能性为 [UIApplication sharedApplication].statusBarOrientation。 - khunshan
不要忘记在适当的位置(取决于您添加观察者的位置)删除观察者,否则会出现异常。 - Amr Lotfy

21

如果您来到这个问题是为了检测方向变化(而不一定想禁用旋转),您还应该了解从iOS 8开始可用的viewWillTransitionToSize方法。

以下是来自这里的Swift示例:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in

        let orient = UIApplication.sharedApplication().statusBarOrientation

        switch orient {
        case .Portrait:
            println("Portrait")
            // Do something
        default:
            println("Anything But Portrait")
            // Do something else
        }

        }, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
            println("rotation completed")
    })

    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
}

如果您不需要担心实际方向:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    // do something

    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
}

来自此处的Objective-C示例

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{   
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
    {
        UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
        // do whatever
    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
    { 

    }];

    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}

如果你不需要担心实际方向(参考这个答案):

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    // Do view manipulation here.
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}

另请参阅


即使您最小化应用程序或按下电源按钮,viewWillTransitionToSize方法也会被调用。 - Rajneesh071

3

1) David的回答的Swift版本 2) 如果您仍然希望在没有方向更改时检测方向(Moe对如何在iOS上检测设备方向?的Swift版本的答案)

    // Initial device orientation
    let orientation: UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation
    if(orientation == UIInterfaceOrientation.Unknown){
        // code for Unknown
    }
    else if(orientation == UIInterfaceOrientation.Portrait){
        // code for Portrait
    }
    else if(orientation == UIInterfaceOrientation.PortraitUpsideDown){
        // code for Portrait
    }
    else if(orientation == UIInterfaceOrientation.LandscapeRight){
        // code for Landscape        
    }
    else if(orientation == UIInterfaceOrientation.LandscapeLeft){
        // ode for Landscape
    }

    // To detect device orientation change
    UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()
    NSNotificationCenter.defaultCenter().addObserver(
        self,
        selector: "orientationChanged:",
        name: UIDeviceOrientationDidChangeNotification,
        object: UIDevice.currentDevice())

方向改变函数

func orientationChanged(note: NSNotification)
{
    let device: UIDevice = note.object as! UIDevice
    switch(device.orientation)
    {
        case UIDeviceOrientation.Portrait:
        // code for Portrait
        break
        case UIDeviceOrientation.PortraitUpsideDown:
        // code for Portrait
        break
        case UIDeviceOrientation.LandscapeLeft:
        // code for Landscape
        break
        case UIDeviceOrientation.LandscapeRight:
        // code for Landscape
        break
        case UIDeviceOrientation.Unknown:
        // code for Unknown
        break
        default:
        break
    }
}

1
如果您不想创建设备对象,也可以使用。
-(void) seObserverForOrientationChanging
{
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter]
     addObserver:self selector:@selector(orientationChanged:)
     name:UIDeviceOrientationDidChangeNotification
     object:[UIDevice currentDevice]];
}


- (void) orientationChanged:(NSNotification *)note
{
    if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)){
        //Do something in landscape
    }
    else {
        //Do something in portrait
    }
}

1
如果我理解正确的话,您的应用程序只支持横屏模式。您可以在应用程序设置中指定它仅支持横屏模式,因此无需担心旋转问题。无论iPad如何方向,应用程序都将始终以横屏模式启动并保持不变。

让我用一个简单的例子来解释一下。我有一个应用程序,旨在以横向视图查看。我的视图显示了一个带球的桌子。当设备旋转到纵向时,球会从桌子上滚落。一切都保持横向,包括桌子、背景等。 - Jace999

0

这里有一些额外的代码,用于避免不必要的UI重置或过渡:

- (void) orientationChanged:(NSNotification *)note
{
    UIDeviceOrientation newOrientation = ((UIDevice*)note.object).orientation;
    
    // don't do anything if device is flat...
    if (UIDeviceOrientationIsFlat(newOrientation) || newOrientation == UIDeviceOrientationUnknown)
        return;
           
    // ... and only if orientation has changed
    if (newOrientation != self->lastOrientation) {
        // do whatever you need to do to change UI
        
        self->lastOrientation = newOrientation;
    }
}

0

.UIDeviceOrientationDidChange 通知在 iPhone 上即使设备没有旋转也会被多次调用。不知道原因,但如果你只需要在设备真正旋转时才需要它,那么请按照以下步骤操作。

NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged), name: .UIDeviceOrientationDidChange, object: nil)

从观察者调用的方法应该像这样:

func orientationChanged() {

    if traitCollection.isIphone {

        defer {
            self.previousTraitCollectionForIphone = traitCollection
        }

        guard let previousTraitCollectionForIphone = previousTraitCollectionForIphone else {

            updateView()
            return
        }

        if previousTraitCollectionForIphone != traitCollection {
            updateView()
        }

    } else {
        updateView()
    }
}

很好的解决方案。一个问题:traitCollection.isIphone 在 Xcode 8.3 - Swift 3.1 中无法识别。请使用 traitCollection.userInterfaceIdiom == .phone。 - guido

0

0

在Swift 5.0中。

DeviceOrientation vs. ScreenSize vs StatusBar.isLandscape?

如果你想要在后台检测方向的变化,并且还需要检测面朝上/下的变化,请查看此链接在Swift中,如何在启动后正确获取设备方向?

它涵盖了使用3种替代方法正确在Swift中检测方向的方法: - viewWillTransitionToSize() - 使用观察者 - 使用状态栏


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