复杂的多设备方向处理

Complex Multi-Device Orientation Handling

我在 iPads 和 iPhones 上都有一个通用应用程序 运行。该应用程序以一个 .xib 文件开始,该文件内置于界面构建器中,充当启动图像。应用启动后,它会根据应用委托中设置的设备尺寸切换到适当的视图控制器:

CGSize screenSize = [[UIScreen mainScreen] bounds].size;

if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
    if (screenSize.height <= 568.0f) {
        // iPhone 4, 4S, 5, 5C, 5S, SE
        self.viewController = [[iPhoneSmallViewController alloc] init];
    } else {
        // All other iPhones
        self.viewController = [[iPhoneLargeViewController alloc] init];
    }
} else {
    // All iPad models
    self.viewController = [[iPadViewController alloc] init];
}

iPad 视图控制器支持所有界面方向(在应用 targets/main 设置页面中设置),但在 iPhones 上我只允许在视图控制器中限制纵向模式:

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}

这个方法有两个问题:

  1. 如果 iPhone 水平放置,应用程序仍会以纵向模式加载(根据限制,这很好),但所有测量值在初始化时都是横向的。 UI 元素在侧面突出,因为它们是为横向视图测量的,但放置在纵向视图上。

我使用 window 的大小通过在 ViewDidLoad 方法中初始化以下变量来设置视图内的所有内容:

windowSize = [[UIScreen mainScreen] bounds].size;

Tt 给我横向尺寸 phone 水平放置,即使横向模式是不允许的。

  1. 如果应用程序最初加载横向尺寸,我在应用程序委托中对屏幕尺寸的所有排序都将关闭,因为我通过测量屏幕宽度来识别 iPhone 型号,这仅适用于纵向模式。

问题:有没有人有办法优雅而简单地处理这个复杂的问题?

一些附加信息:我使用 Xcode 10,一直支持 iOS9 并在 Objective C.

中以编程方式执行所有操作

p.s:我认为这种方法以前有效,但在 iOS 12 中不再适用。但我可能错了...

编辑:我提供了一张我想要完成的图片,所有帮助将不胜感激。正如我所说,这以前有效(该应用程序很旧),但在最近的 iOS 版本中,错误越来越多,迫切需要清理,这正是我需要帮助的地方。

可能会解决我的问题的一件事是,如果我可以根据 launchScreen.xib 中的设备类型以某种方式限制界面方向,因为我相信这就是导致 iPhones 上的错误行为的原因.

也许这个 SO 会有所帮助, 他们正在检测设备方向然后旋转视图,看第一个答案:

UIInterfaceOrientation currentOrientation = [UIApplication         sharedApplication].statusBarOrientation;
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];    
[UIViewController attemptRotationToDeviceOrientation];

此处介绍了一种更好的检测设备的方法: iOS detect if user is on an iPad

if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
{
    return YES; /* Device is iPad */
}

我已经试验了好几天了,并找到了解决方案。虽然这可能不是最优雅的方法,所以如果有人有更好的解决方案,请随时post。

  1. 允许 info.plist 中的所有界面方向很重要,因为我无法根据 launchScreen.xib 中的设备大小限制它们。

  2. 创建同时支持 iPad 和 iPhones 的通用启动屏幕。因为在info.plist中允许所有界面方向,所以这将没有限制。

  3. 下面是我在应用委托中的当前方法。这不是识别较小的 iPhones 的最佳方法(出于某些原因我需要它...:),但由于尺寸差异,这种方法效果很好。

此时phone可以在info.plist中设置的四种界面方向中的任何一种,但是因为只有小手机才有320的宽度所以很容易抓到:

// Get screen size
CGSize screenSize = [[UIScreen mainScreen] bounds].size;

// Determine device based on screen dimensions
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
    if ( (screenSize.height == 320.0f) || (screenSize.width == 320.0f) ){
        // iPhone 4, 4S, 5, 5C, 5S, SE
        self.viewController = [[iPhoneSmallViewController alloc] init];
    } else {
        // iPhone 6, 6S, 6P, 6SP, 7, 7P, 8, 8P X, XS, XM, XR
        self.viewController = [[iPhoneLargeViewController alloc] init]; //Same as previous
    }
} else {
    // All iPad models
    self.viewController = [[iPadViewController alloc] init];
}
  1. 限制 iPhone 视图控制器中的界面方向(所有其他视图控制器将继承我们在 info.plist 中设置的)。

这样做:

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return (UIInterfaceOrientationMaskPortrait);
}
  1. 为了使上面的方法起作用,我们还需要做另一个技巧。当 window 第一次加载时,它不会考虑我们添加到视图控制器的限制。这意味着,如果我们在 viewDidLoad 方法中进行设置,如果设备水平放置(即使不允许这种方向),我们将收到横向屏幕尺寸。一旦 viewDidLoad 方法结束,将应用限制。

因此,为了防止错误行为,您需要创建一个单独的方法来进行设置(例如 postViewDidLoad),并在真正的 viewDidLoad 完成后调用它:

- (void) viewDidLoad
{
    [super viewDidLoad];

    [self performSelector:@selector(postViewDidLoad) withObject:nil afterDelay:0.0f];
}

在此方法中,您将根据您在 supportedInterfaceOrientations 方法中设置的限制访问实际屏幕尺寸。

基本上就是这样。如果您有多个视图,每个视图都有不同的限制,只需按照每个视图中的步骤 4 和 5 正确设置您的工作流程。