适用于iPad纵向和横向模式的尺寸等级

时间:2014-10-29 14:37:53

标签: objective-c ios8 size-classes adaptive-ui

我基本上希望根据iPad(纵向或横向)的方向使用xcode 6中引入的大小调整类来定位不同的子视图。我找到了许多教程,解释了如何在纵向和横向中为Iphone提供不同的大小调整类在IB上,但似乎没有任何内容涵盖IB上的iPad的个人风景或肖像模式。有人可以帮忙吗?

6 个答案:

答案 0 :(得分:173)

似乎Apple打算将两种iPad方向视为相同 - 但正如我们中的许多人所发现的那样,有非常合理的设计理由想要改变iPad Portrait的UI布局iPad风景。

不幸的是,目前的操作系统似乎并没有为这种区别提供支持......这意味着我们要回到操作代码或类似解决方法中的自动布局约束来实现我们理想情况下能够实现的目标使用自适应UI免费获得。

不是一个优雅的解决方案。

是不是有办法利用Apple已经在IB和UIKit中构建的魔法来使用我们选择的选择给定方位?

在更普遍地思考这个问题的过程中,我意识到'大小班级'只是解决存储在IB中的多个布局的简单方法,以便在运行时根据需要调用它们。

事实上,一个'大小的班级'实际上只是一对枚举值。从UIInterface.h:

typedef NS_ENUM(NSInteger, UIUserInterfaceSizeClass) {
    UIUserInterfaceSizeClassUnspecified = 0,
    UIUserInterfaceSizeClassCompact     = 1,
    UIUserInterfaceSizeClassRegular     = 2,
} NS_ENUM_AVAILABLE_IOS(8_0);

因此,无论Apple决定命名这些不同的变体,从根本上说,它们只是一对整数,用作排序的唯一标识符,以区分一种布局,存储在IB中。

现在,假设我们在IB中创建了一个替代布局(使用未使用的大小类) - 比如,对于iPad Portrait ...有没有办法让设备使用我们的选择运行时需要的大小类(UI布局)?

在尝试了几种不同(不太优雅)的问题后,我怀疑可能有办法以编程方式覆盖默认大小类。 (在UIViewController.h中):

// Call to modify the trait collection for child view controllers.
- (void)setOverrideTraitCollection:(UITraitCollection *)collection forChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);
- (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);

因此,如果您可以将视图控制器层次结构打包为“孩子”。查看控制器,并将其添加到顶级父视图控制器...然后您可以有条件地覆盖该子,使其认为它与操作系统的默认值不同。

这是一个实现此目的的示例实现,在父母'查看控制器:

@interface RDTraitCollectionOverrideViewController : UIViewController {
    BOOL _willTransitionToPortrait;
    UITraitCollection *_traitCollection_CompactRegular;
    UITraitCollection *_traitCollection_AnyAny;
}
@end

@implementation RDTraitCollectionOverrideViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setUpReferenceSizeClasses];
}

- (void)setUpReferenceSizeClasses {
    UITraitCollection *traitCollection_hCompact = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
    UITraitCollection *traitCollection_vRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];
    _traitCollection_CompactRegular = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection_hCompact, traitCollection_vRegular]];

    UITraitCollection *traitCollection_hAny = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassUnspecified];
    UITraitCollection *traitCollection_vAny = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassUnspecified];
    _traitCollection_AnyAny = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection_hAny, traitCollection_vAny]];
}

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    _willTransitionToPortrait = self.view.frame.size.height > self.view.frame.size.width;
}

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]
    _willTransitionToPortrait = size.height > size.width;
}

-(UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController {
    UITraitCollection *traitCollectionForOverride = _willTransitionToPortrait ? _traitCollection_CompactRegular : _traitCollection_AnyAny;
    return traitCollectionForOverride;
}
@end

作为一个快速演示,看看它是否有效,我在“常规/常规”中添加了自定义标签。和&#39;紧凑/常规&#39; IB中子控制器布局的版本:

enter image description here enter image description here

当iPad处于两个方向时,它的运行状态如下: enter image description here enter image description here

瞧!运行时自定义大小类配置。

希望Apple在下一版操作系统中不再需要这样做。与此同时,这可能是一种更优雅和可扩展的方法,而不是以编程方式处理自动布局约束或在代码中进行其他操作。

编辑(6/4/15):请记住,上面的示例代码基本上是演示该技术的概念证明。随意根据您的具体应用进行调整。

编辑(2015年7月24日):令人欣慰的是,上述解释似乎有助于揭开问题的神秘面纱。虽然我还没有测试过,但mohamede1945 [下面]的代码看起来像是一个有用的优化实际用途。随意测试它,让我们知道你的想法。 (为了完整起见,我将按原样保留示例代码。)

答案 1 :(得分:41)

作为RonDiamond长篇答案的总结。您需要做的只是在根视图控制器中。

目标-C

- (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController
{
    if (CGRectGetWidth(self.view.bounds) < CGRectGetHeight(self.view.bounds)) {
        return [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
    } else {
        return [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
    }
}

夫特:

override func overrideTraitCollectionForChildViewController(childViewController: UIViewController) -> UITraitCollection! {
        if view.bounds.width < view.bounds.height {
            return UITraitCollection(horizontalSizeClass: .Compact)
        } else {
            return UITraitCollection(horizontalSizeClass: .Regular)
        }
    }

然后在storyborad中使用紧凑宽度为纵向和常规宽度为横向。

答案 2 :(得分:5)

iPad具有水平和垂直尺寸的“常规”尺寸特征,不区分纵向和横向。

可以通过方法UIViewController在自定义traitCollection子类代码中覆盖这些大小特征,例如:

- (UITraitCollection *)traitCollection {
    // Distinguish portrait and landscape size traits for iPad, similar to iPhone 7 Plus.
    // Be aware that `traitCollection` documentation advises against overriding it.
    UITraitCollection *superTraits = [super traitCollection];
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
        UITraitCollection *horizontalRegular = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
        UITraitCollection *verticalRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];
        UITraitCollection *regular = [UITraitCollection traitCollectionWithTraitsFromCollections:@[horizontalRegular, verticalRegular]];

        if ([superTraits containsTraitsInCollection:regular]) {
            if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
                // iPad in portrait orientation
                UITraitCollection *horizontalCompact = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
                return [UITraitCollection traitCollectionWithTraitsFromCollections:@[superTraits, horizontalCompact, verticalRegular]];
            } else {
                // iPad in landscape orientation
                UITraitCollection *verticalCompact = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassCompact];
                return [UITraitCollection traitCollectionWithTraitsFromCollections:@[superTraits, horizontalRegular, verticalCompact]];
            }
        }
    }
    return superTraits;
}

- (BOOL)prefersStatusBarHidden {
    // Override to negate this documented special case, and avoid erratic hiding of status bar in conjunction with `traitCollection` override:
    // For apps linked against iOS 8 or later, this method returns true if the view controller is in a vertically compact environment.
    return NO;
}

这使iPad具有与iPhone 7 Plus相同的尺寸特性。请注意,其他iPhone型号通常具有“紧凑宽度”特征(而不是常规宽度),无论方向如何。

以这种方式模仿iPhone 7 Plus允许该模型在Xcode的Interface Builder中用作iPad的替身,它不知道代码中的自定义。

请注意,iPad上的分割视图可能会使用与普通全屏操作不同的大小特征。

这个答案基于this blog post中采用的方法,并做了一些改进。

更新2019-01-02:已更新以修复iPad格局中的间歇性隐藏状态栏,并可能在UITraitCollection中践踏(较新)特征。另请注意,Apple文档实际上建议不要覆盖traitCollection,因此将来可能会出现这种技术的问题。

答案 3 :(得分:3)

RonDiamond的漫长而有用的答案是理解这些原则的良好开端,但是适用于我的代码(iOS 8+)基于重写方法(UITraitCollection *)traitCollection

因此,在InterfaceBuilder中添加约束Width - Compact的变体,例如对于约束的属性Installed。所以宽度 - 任何都适用于风景,宽度 - 紧凑的肖像。

要根据当前视图控制器大小切换代码中的约束,只需将以下内容添加到UIViewController类中:

- (UITraitCollection *)traitCollection
{
    UITraitCollection *verticalRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];

    if (self.view.bounds.size.width < self.view.bounds.size.height) {
        // wCompact, hRegular
        return [UITraitCollection traitCollectionWithTraitsFromCollections:
                @[[UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact],
                  verticalRegular]];
    } else {
        // wRegular, hRegular
        return [UITraitCollection traitCollectionWithTraitsFromCollections:
                @[[UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular],
                  verticalRegular]];
    }
}

答案 4 :(得分:0)

您的横向模式与纵向模式有多大差异?如果它非常不同,那么创建另一个视图控制器并在设备处于横向

时加载它可能是个好主意

例如

    if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) 
    //load landscape view controller here

答案 5 :(得分:0)

Swift 5版本。很好。

<template>
   <div>
     <input ref='focusMe' type='text'/>
   </div>

</template>

<script>

export default {
  name: 'TextComp',
  mounted () {

    setTimeout(() => {
       this.$refs.focusMe.focus();
    }, 500);

  }
}

</script>