簡體   English   中英

以編程方式創建視圖時,我應該在哪里設置自動布局約束

[英]Where should I be setting autolayout constraints when creating views programmatically

我看到設置約束的不同示例。 有些在viewDidLoad / loadView設置它們(在添加子視圖之后)。 其他人在方法updateViewConstraints設置它們,該方法由viewDidAppear調用。

當我嘗試在updateViewContraints設置約束時, updateViewContraints可能會出現updateViewContraints ,例如在視圖出現之前有輕微的延遲。 另外,如果我使用這種方法,我應該先清除現有的約束,即[self.view [removeConstraints:self.view.constraints]嗎?

我在viewDidLoad / loadView設置了我的約束(我的目標是 iOS >= 6)。 updateViewConstraints對於更改約束的值很有用,例如,如果某些約束依賴於屏幕的方向(我知道,這是一種不好的做法),您可以在此方法中更改其constant

從 39:22 開始,在“iOS 和 OS X 自動布局簡介” (WWDC 2012)會話期間顯示了在viewDidLoad添加約束。 我認為這是在講座中說過但沒有出現在文檔中的事情之一。

更新:我注意到在視圖控制器中的資源管理中提到了設置約束:

如果您更喜歡以編程方式創建視圖,而不是使用故事板,您可以通過覆蓋視圖控制器的loadView方法來實現。 此方法的實現應執行以下操作:

(……)

3.如果您使用自動布局,請為您剛剛創建的每個視圖分配足夠的約束以控制您的視圖的位置和大小 否則,實現viewWillLayoutSubviewsviewDidLayoutSubviews方法來調整視圖層次結構中子視圖的框架。 請參閱“調整視圖控制器視圖的大小”。

更新 2 :在 WWDC 2015 期間, AppleupdateConstraintsupdateViewConstraints推薦用法進行了新的解釋

實際上,所有這些都是視圖有機會在下一次布局傳遞之前及時更改約束的一種方式,但實際上通常不需要它。

理想情況下,所有初始約束設置都應在 Interface Builder 中進行。

或者如果你真的發現你需要以編程方式分配你的約束,像 viewDidLoad 這樣的地方會好得多。

更新約束實際上僅適用於需要定期重復的工作。

此外,當您發現需要這樣做時,更改約束非常簡單; 然而,如果您將該邏輯與與其相關的其他代碼分開,並將其移動到稍后執行的單獨方法中,您的代碼將變得更加難以遵循,因此您將更難維護,其他人會很難理解。

那么什么時候需要使用更新約束呢?

好吧,這歸結為性能。

如果您發現僅僅在適當的位置更改約束太慢,那么更新約束可能會幫助您解決問題。

事實證明,在更新約束中更改約束實際上比在其他時間更改約束要快。

這樣做的原因是因為引擎能夠將在此傳遞中發生的所有約束更改視為批處理。

我建議創建一個BOOL和設置他們在-updateConstraints的UIView(或-updateViewConstraints ,為的UIViewController)。

-[UIView updateConstraints] :(蘋果文檔)

自己設置約束的自定義視圖應該通過覆蓋此方法來實現。

無論-updateConstraints-updateViewConstraints可視圖的一生中多次調用。 (例如,在視圖上調用setNeedsUpdateConstraints會觸發這種情況。)因此,您需要確保防止創建和激活重復的約束——要么使用 BOOL 只執行一次特定的約束設置,要么通過使在創建和激活新約束之前,一定要停用/刪除現有約束。

例如:

  - (void)updateConstraints {  // for view controllers, use -updateViewConstraints

         if (!_hasLoadedConstraints) {
              _hasLoadedConstraints = YES;
             // create your constraints
         }
         [super updateConstraints];
    }

在評論中為 @fresidue 干杯,指出 Apple 的文檔建議將super作為最后一步調用。 如果您在更改某些約束之前調用super ,您可能會遇到運行時異常(崩潰)。

根據 Apple 的 WWDC 視頻和文檔,這應該在 ViewDidLoad 中完成。

不知道為什么人們推薦 updateConstraints。 如果您在 updateConstraints 中這樣做,您將遇到 NSAutoresizingMaskLayoutConstraint 自動調整大小的問題,因為您的視圖已經考慮了自動掩碼。 您需要在 updateConstraints 中刪除它們才能工作。

UpdateConstraints 應該就是這樣,當您需要“更新”它們時,從初始設置中進行更改等。

在視圖中執行布局子視圖方法

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
}

我有這個解決方案可以在加載故事板中的約束之前更改約束。 此解決方案消除了加載視圖后的任何滯后。

-(void)updateViewConstraints{

    dispatch_async(dispatch_get_main_queue(), ^{

            //Modify here your Constraint -> Activate the new constraint and deactivate the old one

            self.yourContraintA.active = true;
            self.yourContraintB.active= false;
            //ecc..
           });

    [super updateViewConstraints]; // This must be the last thing that you do here -> if! ->Crash!
}

您也可以在viewWillLayoutSubviews 中設置它們:

 override func viewWillLayoutSubviews() {

    if(!wasViewLoaded){
        wasViewLoaded = true

        //update constraint

        //also maybe add a subview            
    }
}

這對我有用:

斯威夫特 4.2

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

// Modify your constraints in here

  ...

}

雖然老實說我不確定這是否值得。 加載似乎比 viewDidLoad() 慢一點。 我只是想將它們從后者中移出,因為它變得越來越龐大。

以下示例是將任何視圖傳遞給另一個類。 從故事板創建我的視圖

斯威夫特 5.0

    override func viewWillAppear(_ animated: Bool) {
        
      super.viewWillAppear(animated) 
        DispatchQueue.main.async {
            self.abcInstance = ABC(frame: self.myView.frame)
          } 
      }

 

如果您錯過了DispatchQueue.main.async ,則需要時間來更新viewWillAppear約束。 在故事板中創建 myView 並給出與屏幕寬度和高度相同的約束,然后嘗試打印 myView 的框架。 它將在DispatchQueue.main.asyncviewDidAppear給出准確的值,但在沒有DispatchQueue.main.async情況下不會在viewWillAppear給出准確的值。

viewWillLayoutSubviews()中添加約束以編程方式添加約束

請參閱自定義布局部分中的Apple 文檔

如果可能,請使用約束來定義所有布局。 生成的布局更健壯且更易於調試。 當您需要創建無法單獨使用約束來表達的布局時,您應該只覆蓋 viewWillLayoutSubviews 或 layoutSubviews 方法。

暫無
暫無

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

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