在 iOS 8 中处理方向变化的“正确”方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26069874/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 02:50:28  来源:igfitidea点击:

What is the "right" way to handle orientation changes in iOS 8?

iosios8orientationuiinterfaceorientationlandscape-portrait

提问by rmp251

Can someone please tell me the "right" or "best" approach to working with portrait and landscape interface orientations in iOS 8? It seems that all the functions I want to use for that purpose are deprecated in iOS 8, and my research has turned up no clear, elegant alternative. Am I really supposed to look at the width and height to determine for myself if we are in portrait or landscape mode?

有人可以告诉我在 iOS 8 中使用纵向和横向界面方向的“正确”或“最佳”方法吗?似乎我想为此目的使用的所有功能在 iOS 8 中都已弃用,而且我的研究没有找到明确、优雅的替代方案。我真的应该查看宽度和高度来确定我们是处于纵向还是横向模式?

For example, in my view controller, how should I implement the following pseudocode?

例如,在我的视图控制器中,我应该如何实现以下伪代码?

if we are rotating from portrait to landscape then
  do portrait things
else if we are rotating from landscape to portrait then
  do landscape things

回答by smileyborg

Apple recommends using size classes as a coarse measure of how much screen space is available, so that your UI can significantly change its layout and appearance. Consider that an iPad in portrait has the same size classes as it does in landscape (Regular width, Regular height). This means that your UI should be more or less similar between the two orientations.

Apple 建议使用 size classes 作为可用屏幕空间的粗略度量,以便您的 UI 可以显着改变其布局和外观。考虑到纵向的 iPad 与横向的 iPad 具有相同的尺寸等级(常规宽度、常规高度)。这意味着您的 UI 在两个方向之间应该或多或少相似。

However, the change from portrait to landscape in an iPad is significant enough that you may need to make some smaller adjustments to the UI, even though the size classes have not changed. Since the interface orientation related methods on UIViewControllerhave been deprecated, Apple now recommends implementing the following new method in UIViewControlleras a replacement:

然而,在 iPad 中从纵向到横向的变化已经足够显着,您可能需要对 UI 进行一些较小的调整,即使尺寸类没有改变。由于UIViewController已经弃用了与界面方向相关的方法,Apple 现在建议实现以下新方法UIViewController作为替代:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    // Code here will execute before the rotation begins.
    // Equivalent to placing it in the deprecated method -[willRotateToInterfaceOrientation:duration:]

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Place code here to perform animations during the rotation.
        // You can pass nil or leave this block empty if not necessary.

    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Code here will execute after the rotation has finished.
        // Equivalent to placing it in the deprecated method -[didRotateFromInterfaceOrientation:]

    }];
}

Great! Now you're getting callbacks right before the rotation starts, and after it finishes. But what about actually knowing whether the rotation is to portrait or to landscape?

伟大的!现在,您将在轮换开始之前和完成之后收到回调。但是实际上知道旋转是纵向还是横向呢?

Apple recommends thinking about rotation as simply a change in size of the parent view. In other words, during an iPad rotation from portrait to landscape, you can think of it as the root-level view simply changing its bounds.sizefrom {768, 1024}to {1024, 768}. Knowing this then, you should use the sizepassed into the viewWillTransitionToSize:withTransitionCoordinator:method above to figure out whether you are rotating to portrait or landscape.

Apple 建议将旋转视为简单的父视图大小的变化。换句话说,在 iPad 从纵向到横向的旋转过程中,您可以将其视为根级视图,只需将其bounds.size从更改{768, 1024}{1024, 768}。知道了这一点,您应该使用size传递到viewWillTransitionToSize:withTransitionCoordinator:上面的方法来确定您是旋转到纵向还是横向。

If you want an even more seamless way to migrate legacy code to the new iOS 8 way of doing things, consider using this simple categoryon UIView, which can be used to determine whether a view is "portrait" or "landscape" based on its size.

如果你想要一种更无缝的方式将遗留代码迁移到新的 iOS 8 做事方式,可以考虑在 UIView 上使用这个简单的类别,它可以用于根据视图来确定视图是“纵向”还是“横向”尺寸。

To recap:

回顾一下:

  1. You should use size classes to determine when to show fundamentally different UIs (e.g. an "iPhone-like" UI vs. an "iPad-like" UI)
  2. If you need to make smaller adjustments to your UI when size classes don'tchange but your container (parent view) size does, such as when an iPad rotates, use the viewWillTransitionToSize:withTransitionCoordinator:callback in UIViewController.
  3. Every view in your app should only make layout decisions based on the space that it has been given to layout in. Let the natural hierarchy of views cascade this information down.
  4. Similarly, don't use the statusBarOrientation-- which is basically a device-level property -- to determine whether to layout a view for "portrait" vs "landscape". The status bar orientation should only be used by code dealing with things like UIWindowwhich actually live at the very root level of the app.
  1. 您应该使用大小类来确定何时显示根本不同的 UI(例如“类似 iPhone”的用户界面与“类似 iPad 的”用户界面)
  2. 如果您需要更小的调整你的UI时,大小班没有改变,但你的容器(父视图)的尺寸做,比如当iPad的旋转,使用viewWillTransitionToSize:withTransitionCoordinator:中的UIViewController回调。
  3. 应用程序中的每个视图都应该仅根据为其分配的布局空间做出布局决定。让视图的自然层次结构将这些信息向下级联。
  4. 同样,不要使用statusBarOrientation- 这基本上是一个设备级属性 - 来确定是否为“纵向”和“横向”布局视图。状态栏方向只能由处理UIWindow实际存在于应用程序根级别的事情的代码使用。

回答by CMont

Based on smileyborg's very well detailed (and accepted) answer, here is an adaptation using swift 3:

根据 Smileyborg 非常详细(并被接受)的答案,这是使用 swift 3 的改编:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: nil, completion: {
        _ in
        self.collectionView.collectionViewLayout.invalidateLayout()
    })        
}

And in the UICollectionViewDelegateFlowLayoutimplementation,

而在UICollectionViewDelegateFlowLayout实施中,

public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    // retrieve the updated bounds
    let itemWidth = collectionView.bounds.width
    let itemHeight = collectionView.bounds.height
    // do whatever you need to do to adapt to the new size
}

回答by inVINCEable

I simply use notification Center:

我只是使用通知中心:

Add an orientation variable (will explain at end)

添加一个方向变量(将在最后解释)

//Above viewdidload
var orientations:UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation

Add Notification when view appears

视图出现时添加通知

override func viewDidAppear(animated: Bool) {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name: UIDeviceOrientationDidChangeNotification, object: nil)
}

Remove Notification when view goes away

视图消失时删除通知

override func viewWillDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIDeviceOrientationDidChangeNotification, object: nil) 
}

Gets current orientation when notification is triggered

触发通知时获取当前方向

func orientationChanged (notification: NSNotification) {
    adjustViewsForOrientation(UIApplication.sharedApplication().statusBarOrientation)
}

Checks orientation (portrait/landscape) and handles events

检查方向(纵向/横向)并处理事件

func adjustViewsForOrientation(orientation: UIInterfaceOrientation) {
    if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown)
    {
        if(orientation != orientations) {
            println("Portrait")
            //Do Rotation stuff here
            orientations = orientation
        }
    }
    else if (orientation == UIInterfaceOrientation.LandscapeLeft || orientation == UIInterfaceOrientation.LandscapeRight)
    {
       if(orientation != orientations) {
            println("Landscape")
            //Do Rotation stuff here
            orientations = orientation
        }
    }
}

The reason I add an orientation variable is because when testing on a physical device the orientation notification gets called at every minor move in the device, and not just when it rotates. Adding the var and if statements only calls the code if it switched to the opposite orientation.

我添加一个方向变量的原因是因为在物理设备上进行测试时,方向通知会在设备的每个小动作时被调用,而不仅仅是在它旋转时。添加 var 和 if 语句仅在代码切换到相反方向时才调用代码。

回答by Aaron

From a UI perspective, I believe that using Size Classes are Apple's recommended approach for handling interfaces in different orientations, sizes and scales.

从 UI 的角度来看,我相信使用 Size Classes 是 Apple 推荐的处理不同方向、大小和比例的界面的方法。

See the section: Traits Describe the Size Class and Scale of an Interfacehere: https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

请参阅以下部分:特征描述接口的大小类和规模https: //developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

"iOS 8 adds new features that make dealing with screen size and orientation much more versatile."

“iOS 8 增加了新功能,使处理屏幕尺寸和方向的功能更加灵活。”

This one is a good article as well: https://carpeaqua.com/thinking-in-terms-of-ios-8-size-classes/

这也是一篇好文章:https: //carpeaqua.com/thinking-in-terms-of-ios-8-size-classes/

EDITUpdated Link: https://carpeaqua.com/2014/06/14/thinking-in-terms-of-ios-8-size-classes/(Credit: Koen)

编辑更新链接: https://carpeaqua.com/2014/06/14/thinking-in-terms-of-ios-8-size-classes/(来源:Koen)