如何在iOS 8/Swift中处理屏幕尺寸/方向变化的NSLayoutConstraints?

cle*_*ght 2 ios nslayoutconstraint swift

如何使用IB约束和NSLayoutConstraint

我想以编程方式微调我的Interface Builder Storyboard布局,因为我无法单独使用Interface Builder中的约束来实现所需的布局.

iPhone 4S和iPhone 6 Plus对我的iOS 8布局特别具有挑战性.


问题:

  • 当UIViewController出现时,如何找到屏幕尺寸

  • 如何在iOS 8中检测方向更改和生成的屏幕尺寸?

  • 如何以编程方式修改布局约束

  • 如何以编程方式处理IB 大小类


也许通过IB中的"加权"组件,我可以补偿iPhone 6 Plus尺寸等级的布局问题.但似乎比以编程方式进行一些调整更具挑战性.

iOS 8支持的Devicea是分散屏幕大小的泥潭

iOS 4应用程序仍然需要支持iPhone 4S,其屏幕非常小巧.对于更适合大型iPhone屏幕的应用程序来说,4S上的应用程序令人满意,这对于几乎荒谬的程度来说是一个非常紧迫的挑战.

iPhone 6 Plus实际上是iPad"mini-mini",除了更紧凑的宽高比.相对庞大的iPhone 6 plus需要比其他iPhone更均匀/更宽敞的组件分布.然而,Interface Builder大小类并不能很好地覆盖iPhone 6 Plus(到目前为止).

cle*_*ght 6

这是iOS 8/Swift代码示例的一种方法

•使用IBOutlets并修改出口约束:

我发现以编程方式修改基于约束的布局的最佳方法是为我想要修改的每个约束创建一个IBOutlet,而不是产生开销编写代码以编程方式定位/添加/删除/替换约束,特别是在大小类时用于Interface Builder布局. 当使用" 大小类 "(谷歌的定义)时,替换约束是一个主要的麻烦.你真的必须知道你正在做什么才能使约束替换工作正确,而且,开销也要高得多.

•避免使用IB布局约束的乘数属性:

通过根据需要定位更大的占位符/容器视图,简化复杂的布局,将场景向下移动到更详细的视图.策略性地设计约束,因此您只需在运行时通过其出口以编程方式调整最少的约束,以实现最佳布局.如果可能的话,单独使用NSLayoutConstraint常量属性,而不是篡改NSLayoutConstraint乘数属性,因为常量可以在运行时修改,但乘数是只读的,需要约束替换来修改它.如果您使用乘数属性因为您需要约束来按比例调整其他视图的大小或位置,您可以在代码中进行乘法并在运行时将其应用于常量,而不是依赖于NSLayoutConstraint 乘数属性.

•根据IB约束使用单一大小的常数:

如果您使用的大小类,在Interface Builder中,一定要确定个人的约束,每一只用单一尺寸类特定的常量,让你指定的电源插座每个个体大小归类约束,而不是试图对多个聚集常数将大小类分为单个约束.虽然接口生成器将让你添加多个常量到一个约束定义,NSLayoutConstraint本身也只是提供了一个单一的 不变属性.这意味着多个NSLayoutConstraint实例将在运行时从单个可见的Interface Builder约束定义产生,如果它附加了多个大小类常量.它比你想象的更难以在运行时尝试找到未过时的约束并调整它们而不会遇到问题.

•注意运行时问题以编程方式解决IB限制:

如果你尝试以编程方式查找/修改/删除/在运行时替换的约束,大小班都参与时,振作起来费时混乱的冲突和错误.我发现iOS在图片中带来了"鬼"约束,这些约束是使用黑盒逻辑意外发生的,阻碍了进行布局更改的尝试.冲突警告可能由iOS生成,有时甚至是您以编程方式删除的约束(例如,为什么这些约束仍然在iOS的处理逻辑中?!!!)iOS有时能够在运行时中断并修复约束冲突,但即使iOS已经破坏了冲突,所有这些冲突看起来都很好,这些冲突(产生Xcode控制台错误消息)被Apple明确认为是用户编码错误.

Swift/iOS 8代码示例:

@IBOutlet var keypadPlaceholder      : UIView!
@IBOutlet var textualDatePlaceholder : UIView!
@IBOutlet var keypadVconstraint      : NSLayoutConstraint!
@IBOutlet var textualDateVConstraint : NSLayoutConstraint!
@IBOutlet var datePickerVConstraint  : NSLayoutConstraint!

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    adjustViewLayout(size)
}
override func viewWillAppear(animated: Bool) {
    adjustViewLayout(UIScreen.mainScreen().bounds.size)
}
func adjustViewLayout(size: CGSize) {
    println("height: \(size.height), width: \(size.width)")

    switch(size.width, size.height) {
    case (480, 320):                        // iPhone 4S in landscape
        keypadPlaceholder.hidden = false
        textualDatePlaceholder.hidden = false
    case (320, 480):                        // iPhone 4S in portrait
        keypadPlaceholder.hidden = true
        textualDatePlaceholder.hidden = true
    case (414, 736):                        // iPhone 6 Plus in portrait
        textualDateVConstraint.constant = 105
        datePickerVConstraint.constant = 50
        keypadVconstraint.constant = 50
        view.setNeedsLayout()
    case (736, 414):                        // iphone 6 Plus in landscape
        textualDateVConstraint.constant = 80
        datePickerVConstraint.constant = 3
        keypadVconstraint.constant = 3
        view.setNeedsLayout()
    default:
        break
    }
}
Run Code Online (Sandbox Code Playgroud)