为iPad纵向和横向模式调整分级

我基本上想要根据iPad(纵向或横向)的方向,使用xcode 6中介绍的尺寸类来定位我的子视图。我发现了许多教程,解释如何在IB上纵向和横向上为Iphone提供不同尺寸的类但是似乎没有任何内容涉及IB上iPad的个人风景或肖像模式。 谁能帮忙?

这似乎是苹果的意图,对待iPad的方向是一样的 – 但正如我们许多人所发现的,有一个非常合法的devise理由,想改变iPad的肖像与iPad景观的UI布局。

不幸的是,目前的操作系统似乎没有提供这种区别的支持……这意味着我们回到操纵代码中的自动布局约束或类似的解决方法来实现我们应该能够使用自适应UI 。

不是一个优雅的解决

难道没有办法利用苹果公司已经内置到IB和UIKit中的魔法来使用我们select的尺寸类别作为给定的方向吗?

在更一般地思考这个问题时,我意识到“大小类”是处理存储在IB中的多个布局的简单方法,因此可以在运行时根据需要调用这些布局。

事实上,“规模级”实际上只是一对枚举值。 从UIInterface.h:

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

所以,无论苹果决定如何命名这些不同的变体,基本上,它们只是一对整数,用来作为唯一的sorting标识符来区分一个存储在IB中的布局。

现在,假设我们在IB中创build一个备用布局(使用未使用的大小类) – 例如,对于iPad肖像…是否有一种方法可让设备在运行时根据需要使用我们所select的大小类(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 { _willTransitionToPortrait = size.height > size.width; } -(UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController { UITraitCollection *traitCollectionForOverride = _willTransitionToPortrait ? _traitCollection_CompactRegular : _traitCollection_AnyAny; return traitCollectionForOverride; } @end 

作为一个快速演示,看看它是否工作,我专门添加了自定义标签在IB的子控制器布局的“Regular / Regular”和“Compact / Regular”版本:

在这里输入图像描述在这里输入图像描述

当iPad处于两个方向时,这就是它的外观: 在这里输入图像描述在这里输入图像描述

瞧! 在运行时自定义大小类configuration。

希望苹果将在下一个版本的操作系统中使这个不必要。 与此同时,这可能是一个更优雅和可扩展的方法比编程混乱自动布局约束或在代码中执行其他操作。

编辑 (6/4/15):请记住,上面的示例代码本质上是一个展示技术的概念certificate。 随意根据需要适应您自己的具体应用。

编辑 (7/24/15):令人高兴的是,上述解释似乎有助于揭开这个问题的神秘面纱。 虽然我还没有testing过,但mohamede1945的代码看起来像是一个有用的优化实用程序。 随时testing一下,让我们知道你的想法。 (为了保持完整性,我将在上面保留示例代码。)

作为RonDiamond长期回答的总结。 所有你需要做的是在你的根视图控制器。

Objective-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中使用紧凑宽度的肖像和常规宽度的景观。

iPad具有水平和垂直尺寸的“常规”尺寸特性,纵向和横向之间没有区别。

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

 - (UITraitCollection *)traitCollection { // Distinguish portrait and landscape size traits for iPad, similar to iPhone 7 Plus. UITraitCollection *superTraits = [super traitCollection]; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { UITraitCollection *horizontalRegular = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular]; UITraitCollection *horizontalCompact = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact]; UITraitCollection *verticalRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular]; UITraitCollection *verticalCompact = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassCompact]; UITraitCollection *regular = [UITraitCollection traitCollectionWithTraitsFromCollections:@[horizontalRegular, verticalRegular]]; UITraitCollection *portrait = [UITraitCollection traitCollectionWithTraitsFromCollections:@[horizontalCompact, verticalRegular]]; UITraitCollection *landscape = [UITraitCollection traitCollectionWithTraitsFromCollections:@[horizontalRegular, verticalCompact]]; if ([superTraits containsTraitsInCollection:regular]) { if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) return portrait; else return landscape; } } return superTraits; } 

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

通过这种方式模仿iPhone 7 Plus,可以将该模型用作Xcode界面构build器中的iPad的替代品,而不需要代码中的定制。

请注意,iPad上的“分割视图”可能会使用正常全屏操作中的不同大小特征。

这个答案是基于这个博客文章采取的方法,有一些改进。

由RonDiamond提供的长时间且有用的答案是理解这些原则的一个良好的开端,但是对我来说(iOS 8+)的代码是基于重写方法(UITraitCollection *)traitCollection

因此,在InterfaceBuilder中增加了一些限制,例如对于宽度紧凑的变体,例如对于约束的属性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]]; } } 

你的风景模式比你的肖像模式有多less不同? 如果它非常不同,创build另一个视图控制器并在设备处于横向时加载它可能是一个好主意

例如

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

Swift 3.0代码@RonDiamond解决scheme

 class Test : UIViewController { var _willTransitionToPortrait: Bool? var _traitCollection_CompactRegular: UITraitCollection? var _traitCollection_AnyAny: UITraitCollection? func viewDidLoad() { super.viewDidLoad() self.upReferenceSizeClasses = null } func setUpReferenceSizeClasses() { var traitCollection_hCompact: UITraitCollection = UITraitCollection(horizontalSizeClass: UIUserInterfaceSizeClassCompact) var traitCollection_vRegular: UITraitCollection = UITraitCollection(verticalSizeClass: UIUserInterfaceSizeClassRegular) _traitCollection_CompactRegular = UITraitCollection(traitsFromCollections: [traitCollection_hCompact,traitCollection_vRegular]) var traitCollection_hAny: UITraitCollection = UITraitCollection(horizontalSizeClass: UIUserInterfaceSizeClassUnspecified) var traitCollection_vAny: UITraitCollection = UITraitCollection(verticalSizeClass: UIUserInterfaceSizeClassUnspecified) _traitCollection_AnyAny = UITraitCollection(traitsFromCollections: [traitCollection_hAny,traitCollection_vAny]) } func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) _willTransitionToPortrait = self.view.frame.size.height > self.view.frame.size.width } func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { _willTransitionToPortrait = size.height > size.width } func overrideTraitCollectionForChildViewController(childViewController: UIViewController) -> UITraitCollection { var traitCollectionForOverride: UITraitCollection = _willTransitionToPortrait ? _traitCollection_CompactRegular : _traitCollection_AnyAny return traitCollectionForOverride }}