首页 文章

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

提问于
浏览
96

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

6 回答

  • 41

    看起来苹果打算将两种iPad方向都视为一样 - 但正如我们中的许多人所发现的那样,有非常合理的设计理由想要改变iPad Portrait与iPad Landscape的UI布局 .

    不幸的是,当前的操作系统似乎并没有为这种区别提供支持......这意味着我们又回到了在代码或类似的变通方法中操纵自动布局约束,以实现我们理想情况下可以使用Adaptive UI免费获得的内容 .

    不是优雅的解决方案 .

    已经内置到IB和UIKit中的Isn 't there a way to leverage the magic that Apple'使用我们选择的尺寸等级给定方向吗?

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

    事实上,'size class'实际上只是一对枚举值 . 从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布局) ?

    在尝试了几种不同(不太优雅)的问题后,我怀疑可能有一种方法可以通过编程方式覆盖默认的size类 . 还有(在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
    

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

    enter image description here

    enter image description here

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

    enter image description here

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

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

    EDIT (6/4/15):请记住,上面的示例代码基本上是证明该技术的概念证明 . 您可以根据自己的具体应用需求进行调整 .

    EDIT (7/24/15):它测试了它,mohamede1945 [下面]的代码看起来像是一个有用的优化用于实际目的 . 随意测试它,让我们知道你的想法 . (为了完整起见,我将保留上面的示例代码 . )

  • 6

    作为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中使用紧凑宽度为纵向和常规宽度为横向 .

  • 3

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

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

    - (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,有一些改进 .

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

  • 0

    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]];
        }
    }
    
  • 173

    您的横向模式与纵向模式有多大不同?如果它非常不同,那么创建另一个视图控制器并在设备处于横向时加载它可能是个好主意

    例如

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

    @RonDiamond解决方案的Swift 3.0代码

    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
    }}
    

相关问题