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

时间:2015-01-26 23:07:08

标签: ios swift nslayoutconstraint

如何使用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及其可爱的 tiny 屏幕。对于更适合大型iPhone屏幕的应用程序来说,它在4S上是令人满意的,并且对于几乎荒谬的程度具有挑战性。

iPhone 6 Plus几乎是一款iPad" mini-mini",除了更紧密的宽高比。相对庞大的iPhone 6 plus需要比其他iPhone更均匀/更宽敞的组件分布。然而,Interface Builder大小的课程并不能很好地覆盖iPhone 6 Plus(到目前为止)。

2 个答案:

答案 0 :(得分:6)

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

•使用IBOutlets并修改出口限制

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

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

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

•对每个IB约束使用单个大小的分类常量

如果您正在使用大小类,请在Interface Builder中确保定义单个约束,每个约束仅包含单个大小类常量< / strong>以便为每个单个大小分类约束分配一个出口,而不是尝试将多个大小类的常量聚合到一个约束中。虽然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
    }
}

答案 1 :(得分:0)

此示例调整中心Y约束,该约束在设备处于纵向模式时将日期选择器向上移动一定量,该量是屏幕尺寸的一部分,但在横向模式下将其恢复到中心。

@IBOutlet var datePickerYCenterConstraint      : NSLayoutConstraint!

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    adjustViewLayout(size)
}

override func viewWillAppear(animated: Bool) {
    adjustViewLayout(UIScreen.mainScreen().bounds.size)
}
func adjustViewLayout(size: CGSize) {

    // calculate picker's center using object heights (that don't change
    // as we adjust their positions)

    var dy = size.height / 2 - datePicker.frame.size.height / 2

    if (size.height > size.width) { // Device in portrait mode
        datePickeryCenterConstraint.constant = -0.5 * dy - 0.1 * dy
    } else { // Device in landscape mode
        datePickeryCenterConstraint.constant = 0 // reset to center
    }
    view.setNeedsLayout()
}