簡體   English   中英

如何在iOS 8 / Swift中通過屏幕尺寸/方向更改來處理NSLayoutConstraints?

[英]How to handle NSLayoutConstraints with screen size/orientation changes in iOS 8/Swift?

如何使用IB約束和NSLayoutConstraint

我想以編程方式微調我的Interface Builder故事板布局,因為僅靠Interface Builder不能滿足約束條件。

對於我的iOS 8布局,iPhone 4S和iPhone 6 Plus尤其具有挑戰性。


問題:

  • 出現UIViewController時如何查找屏幕尺寸

  • 如何在iOS 8中檢測方向變化和屏幕尺寸?

  • 如何以編程方式修改布局約束

  • 如何以編程方式處理IB 大小類


也許通過“加權” IB中的組件,我可以彌補iPhone 6 Plus尺寸等級的布局問題。 但是,這似乎比以編程方式進行一些調整更具挑戰性。

iOS 8支持的Devicea是不同屏幕尺寸的困擾

iPhone 4S的屏幕小得離譜,但仍需要iOS 8應用程序支持。 對於4S來說,更適合大型iPhone屏幕的應用程序要滿足要求,這是一個很大的挑戰,而且幾乎是荒謬的。

iPhone 6 Plus實際上是iPad的“迷你-迷你”,除了其更窄的縱橫比。 與其他iPhone相比,相對龐大的iPhone 6 plus需要更均勻/寬敞的組件分布。 然而,到目前為止,Interface Builder尺寸等級不能很好地覆蓋iPhone 6 Plus。

這是iOS 8 / Swift代碼示例的一種方法

•使用IBOutlets並適當修改出口限制

我發現以編程方式修改基於約束的布局的最好方法是為要修改的每個約束創建一個IBOutlet ,而不是花費大量代碼編寫代碼來以編程方式定位/添加/刪除/替換約束,特別是在使用大小類時在Interface Builder布局中使用。 當使用“ size classes ”(谷歌定義)時, 替換約束是一個主要的麻煩。 此外,您確實必須知道要做什么才能正確執行約束替換,而且開銷要大得多。

•避免使用IB布局約束的multiplier屬性

通過根據需要放置較大的占位符/容器視圖來簡化復雜的布局,將場景下移到更詳細的視圖。 從戰略上設計約束,因此您僅需在運行時通過其出口以編程方式調整盡可能少的約束即可獲得最佳布局。 如果可能的話,請單獨使用NSLayoutConstraintconstant屬性,而不要篡改NSLayoutConstraintmultiplier屬性,因為可以在運行時修改常量 ,但是multiplier是只讀的,需要替換約束才能對其進行修改。 如果由於需要約束來按比例調整其他視圖的大小或位置而使用multiplier屬性,則可以在代碼中進行乘法並將其應用於運行時常量 ,而不是依賴於NSLayoutConstraint 乘法器屬性。

•對每個IB約束使用單個大小分類的常量

如果您使用的大小類,在Interface Builder中,一定要確定個人的約束,每個只有一個尺寸類特定的常數 ,讓您分配一個出口到每個個體大小歸類約束,而不是試圖對多個聚集常數將類划分為單個約束。 雖然界面生成器可以讓你添加多個常量到一個約束定義,NSLayoutConstraint本身只只提供了一個 不變的性質。 這意味着,如果有多個NSLayoutConstraint實例附加了多個size類常量,則它們將在運行時從單個可見的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
    }
}

本示例調整了“中心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()
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM