[Android開發學iOS系列] Auto Layout

来源:https://www.cnblogs.com/mengdd/archive/2022/11/02/iOS-AutoLayout-basics.html
-Advertisement-
Play Games

[Android開發學iOS系列] Auto Layout 內容: 介紹什麼是Auto Layout. 基本使用方法 在代碼中寫約束的方法 Auto Layout的原理 尺寸和優先順序 Auto Layout的使用細則 重要的屬性 StackView Layout Guide Performance ...


[Android開發學iOS系列] Auto Layout

內容:

  • 介紹什麼是Auto Layout.
  • 基本使用方法
    • 在代碼中寫約束的方法
  • Auto Layout的原理
  • 尺寸和優先順序
  • Auto Layout的使用細則
    • 重要的屬性
    • StackView
    • Layout Guide
  • Performance
  • Debugging

What is Auto Layout

Auto Layout會根據constraints(約束)動態計算出view hierarchy中所有View的位置和大小.

對於Android開發者來說, Auto Layout很容易上手, 它非常像ConstraintLayoutRelativeLayout: 給View規定它上下左右和誰對齊, 決定UI的位置和大小.

Auto Layout的約束更寬泛一些, 不僅僅是兩個View之間的關係, 還有寬高, 比率等設置, 並且可以有一些大於小於等的範圍設置.

Auto Layout不是一個View

開始學Auto Layout我還以為它是一個叫AutoLayout的View, 把其他子View包進去然後設置一些放置規則, 就類似於Android的ConstraintLayout或者RelativeLayout.

但是其實不是, AutoLayout不是一個具體的View, 它代表的是一種計算引擎. 因為在代碼里你從來不需要寫AutoLayout這個關鍵字, 寫的從來都是Constraints.

開發者為View設置足夠多的約束, 規定和這個View位置和大小相關的因素, 這個引擎就可以為我們計算出View的位置和大小.

AutoLayout為瞭解決什麼問題

不同屏幕適配; 可以合理應對變化的responsive UI.

改變佈局有內外兩種因素, 除了屏幕尺寸, 屏幕旋轉, 視窗大小改變等外部因素.

內部因素還包含了內容的動態變化, 國際化的支持, 字體的調整等.

和Auto Layout平行的解決方案是什麼

擺放UI有三種主要的方法:

  • 在程式里給每個View設置frame.
  • 設置frame, 結合使用autoresizing masks來應對外部變化. (autoresizing mask定義了一個view的frame在它的superview frame變化時應該如何變化.)
  • 使用Auto Layout.

可以看出第二種只是在基於frame的方式上做出了一點改進, 所能應對的也僅僅是外部變化, 有一定的局限性. 所以可以把前兩種歸類為一種.

這也正是Auto Layout出現之前的解決方案, 即基於frame的佈局方式.

Auto Layout的思考點不再著眼於view frame, 而是view的relationship.

如何使用Auto Layout

寫iOS的UI有多種方式, Auto Layout屬於UIKit, 在寫的時候, 可以用storyboard, 也可以直接在代碼中寫約束.

在storyboard裡面有一些好處, 比如所見即所得, 而且ide會給出一些warnings, 比如控制項在storyboard上的位置與約束不一致, 會提示, 並且可以選擇方式修複.
在storyboard裡面寫約束確實是不容易出錯的一種方式, xcode的操作也很直觀, 這裡不做演示了.

之前我們也討論過, 用storyboard寫UI存在閱讀性差, 代碼版本管理和團隊合作都有問題等.
所以具體使用需要看實際情況.

關於約束, location和size的約束不能混著用, 這個也是從邏輯上就可以理解的.
比如讓某個view的top和parent的top對齊(或者再offset個常量)是可以的, 但是讓top等於某個size就不能理解了.

在代碼中創建約束

如果不用Interface Builder, 而是選擇在代碼中創建約束, 那麼仍然有多種選擇:

  • 使用layout anchor.
  • 使用NSLayoutConstraint類.
  • 使用Visual Format Language.

我們在改變約束的時候通常不會add/remove constraints, 而是active/deactivate.

使用Layout anchor

這個方法可能是最直觀的一種方法.

// Get the superview's layout
let margins = view.layoutMarginsGuide
 
// Pin the leading edge of myView to the margin's leading edge
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
 
// Pin the trailing edge of myView to the margin's trailing edge
myView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
 
// Give myView a 1:2 aspect ratio
myView.heightAnchor.constraint(equalTo: myView.widthAnchor, multiplier: 2.0).isActive = true

這裡我們把每一條約束設置了isActive = true.

也可以直接放在一個數組裡一起activate, 會有性能優勢:

NSLayoutConstraint.activate([
    myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
    myView.trailingAnchor.constraint(equalTo: margins.trailingAnchor),
    myView.heightAnchor.constraint(equalTo: myView.widthAnchor, multiplier: 2.0)
])

使用NSLayoutConstraint

使用NSLayoutConstraint寫起來比較啰嗦, 必須給每個參數都指定值:

NSLayoutConstraint(item: myView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leadingMargin, multiplier: 1.0, constant: 0.0).isActive = true
 
NSLayoutConstraint(item: myView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailingMargin, multiplier: 1.0, constant: 0.0).isActive = true
 
NSLayoutConstraint(item: myView, attribute: .height, relatedBy: .equal, toItem: myView, attribute:.width, multiplier: 2.0, constant:0.0).isActive = true

這個不但寫起來麻煩, 可讀性也很差.

Visual Format Language (VFL)

let views = ["myView" : myView]
let formatString = "|-[myView]-|"
 
let constraints = NSLayoutConstraint.constraints(withVisualFormat: formatString, options: .alignAllTop, metrics: nil, views: views)
 
NSLayoutConstraint.activate(constraints)

用一些鍵盤符號來表達這個佈局的. (like a way of drawing the layout you want with a series of keyboard symbols)

管道符號代表parent view的邊邊.

Auto Layout的工作原理

Auto Layout Internal

圖來自於: https://developer.apple.com/videos/play/wwdc2018/220

Render loop包含如上三個階段:

  • update constraints從葉子節點向上.
  • layout從parent節點向下執行.
  • display即最後的繪製階段.

這三個階段對應的方法:
Auto Layout methods

Update Constraints

它的工作是:

  • 把每個公式(約束)加入計算引擎Engine里.
  • 計算引擎負責解出變數: 最後的frame.
  • 通知View: Superview: setNeedsLayout().

engine這裡扮演一個layout cache和tracker. 收到變化時它會重新計算.

Layout

從engine得到信息後, Subview setBounds(), subview setCenter().

尺寸和優先順序

瞭解了Auto Layout的原理之後, 看尺寸和優先順序的部分就很好理解.

Intrinsic content size

有一些View有固有內容尺寸, 對於AutoLayout來說, 會預設使用intrinsic content size, 這樣開發者就不用非得提供尺寸信息.

預設使用: intrinsic content size. 固有內容尺寸.

  • UIImageView: image size.
  • UILabel: text size.

優先順序

優先順序的值可以從1到1000, 預設是1000.

  • Required: 1000
  • Default High: 750
  • Default Low: 250

有優先順序是因為多個constraints之間可能會有衝突, 那麼約束的要求可能不能完全100%滿足, 計算引擎會在在不能滿足的情況下, 儘量地減少偏差.

約束的優先順序就用來表示哪條約束我們更加關心, 更想滿足, 優先考慮.

優先順序相關的變數

  • content hugging priority: 尺寸比固有內容更大的可能性. 預設250. 值越小表示View更願意擴張來滿足約束了; 值越大表示View希望儘可能地接近固有尺寸.
  • content compression resistance priority: 尺寸比固有內容尺寸更小的阻力程度. 預設750. 值越大表示這個View壓縮內容的可能性越小.

Auto Layout的使用細則

Properties & Functions

有個重要的屬性要提一下:

  • translatesAutoresizingMaskIntoConstraints

這個屬性是為了相容Auto Layout出現之前的基於frame佈局的legacy layout系統, 幫助View在Auto Layout的世界里, 以legacy layout system的方式運作.

當這個屬性為true, 並且設置了frame時, 引擎會自動生成constraints來滿足這個frame.

這個View的屬性預設為true. 當我們要用constraints時需要設置為false.

  • 當在storyboard中開始為View設置constraints時, 會自動設置為false.
  • 當我們在代碼中給view設置約束之前, 需要自己顯式地把這個屬性設置為false.

如果還是用frame佈局, 這個屬性不用設置成false. 比如在迴圈里生成很多view的時候, 可能想有一些尺寸和位置用frame設置.

  • sizeToFit(): 剛好包裹內容的大小.

Stack View

Stack View是在Auto Layout的基礎上的, 幫助我們做一些水平或者垂直的佈局, 不用寫內部元素間的constraints. (類似於Android中的LinearLayout.)

往Stack View裡加需要疊放的元素用的是addArrangedSubview()這個方法.

與此同時, addSubview()方法可以用來加一些別的View.

幾個屬性:

  • axis: 主軸方向.
  • alignment: 對齊方式.
  • distribution: 沿著主軸的分佈.

Stack View是比較輕量的, 所以官方會建議儘量多使用Stack View, 只在有必要的時候寫約束.
確實方便很多.

Layout Guide

很多時候為了佈局的需要我們可能要包裹View或者是添加一下輔助View, 每個View都有自己的layer, 所以為了改進性能, 我們可以使用Layout Guide.

View自帶一個layoutMarginsGuide.

還挺方便的. (看了這個視頻: https://www.youtube.com/watch?v=4qPcMGiSADA)

Performance & Building Efficient Layouts

iOS12對AutoLayout的性能做了很多改進, 這個WWDC的talk有講.

關於有效率的佈局, 簡而言之就是少做無用功.

Constraint Churn

constraint churning是個典型的性能問題.
churn: 攪動.

constraint churn是指更新了constraints, 但實際上view並不需要移動.

這樣是給engine發送了額外的信息, 達到一定數量之後, 就會影響性能.

需要註意的是:

  • 不要remove all constraints然後又add all. 可以把它們分組, 哪些是固定不變的, 那麼addView的時候就加上, 然後activate; 對於需要動態變化的部分可以分兩組(比如一個根據內容動態決定是否需要顯示圖片的例子, 可以有兩個數組: imageConstraints和noImageConstraints), 單獨activate/deactivate這兩組約束.
  • 使用isHidden可以提高效率. 比起add/remove Subview來說.

也是WWDC2018/220里提到的, 如何避免Constraint Churn:

  • Avoid removing all constraints
  • Add static constraints once
  • Only change the constraints that need changing
  • Hide views instead of removing them

Size

可以選擇性地override一些尺寸, 減少text measure計算的過程:

  • Return size if known without text measurement
  • Use UIView.noIntrinsicMetric and constraints.

System Layout Size Fitting Size

intrinsic content size是view傳給engine的.

而這個system layout size fitting size, 是從engine取出來的.

但是它有想不到的性能消耗. (every time you call the method, an engine is created and discarded.)

Debugging

Auto Layout中由約束引起的錯誤可能會有:

  • 約束自相矛盾(衝突), 不能滿足, 無解. (比如一個寬度即等於100又等於200, ???)
  • 約束不足導致有很多可能的解. (Engine會給出一個解, 但可能不是你想要的.)

關於怎麼debug可以看: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/TypesofErrors.html#//apple_ref/doc/uid/TP40010853-CH17-SW1

大體上是根據Log還有一些可能有幫助的view的屬性和方法(供debug用).

這個視頻(https://developer.apple.com/videos/play/wwdc2015/219/)的後半段有講debug.

這裡還有一個小工具網站: https://www.wtfautolayout.com/

Summary

Auto Layout是線性代數的應用實例.

有時候搬磚搬久了是不是應該慢下來欣賞一下數學的美.

References

作者: 聖騎士Wind
出處: 博客園: 聖騎士Wind
Github: https://github.com/mengdd
微信公眾號: 聖騎士Wind
微信公眾號: 聖騎士Wind
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 前文回顧 實現一個簡單的Database1(譯文) 實現一個簡單的Database2(譯文) 實現一個簡單的Database3(譯文) 實現一個簡單的D ...
  • 前面說到了redis在單機的模式下是可以數據持久化的,但是不可以解決單點失敗的問題,當單台redis伺服器出現問題時,就可能會造成數據的丟失;想要解決這個問題的話我們可以使用Redis的主從模式這也是Redis集群最簡單的實現方式,這篇文章我就來簡單部署一個Redis主從架構,我準備了3台ubunt ...
  • 摘要:本文通過對ETCD服務異常問題分析,代碼展示解決方案。 本文分享自華為雲社區《【實例狀態】GaussDB ETCD服務異常》,作者:酷哥。 首先確認是否是虛擬機、網路故障 虛擬機故障導致ETCD服務異常告警 問題現象 管控面上報etcd服務異常告警,虛擬機發生重啟,熱遷移、冷遷移,HA等動作。 ...
  • 資料庫連接 鏈接資料庫:代表連接資料庫管理系 統 <!--一個應用程式可以對應一個資料庫,一個資料庫管理系統可以管理多個資料庫--> <!--表是用來存儲數據的--> -- 連接資料庫管理系統 mysql -u root -p -- -u 代表用戶 -p代表用密碼登錄 定義資料庫 -- 創建資料庫 ...
  • Redis雖然是一個記憶體級別的緩存程式,也就是redis是使用記憶體進行數據的緩存的,但是其可以將記憶體的數據按照一定的策略保存到硬碟中,這樣的話就可以實現持久保存的目的;目前的話redis支持的兩種不同方式的數據持久化保存機制,分別是RDB和AOF,這兩種方式的話很類似於MySQL資料庫的dump和二 ...
  • PostgreSQL 高可用資料庫的常見搭建方式主要有兩種,邏輯複製和物理複製,上周已經寫過了關於在Windows環境搭建PostgreSQL邏輯複製的教程,這周來記錄一下 物理複製的搭建方法。 首先介紹一下邏輯複製和物理複製的一些基本區別: 物理複製要求多個實例之間大版本一致,並且操作系統平臺一致 ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 介紹 從 MySQL 8.0.4 開始,MySQL 預設身份驗證插件從 mysql_native_password 改為 caching_sha2_pa ...
  • 首發微信公眾號:SQL資料庫運維 原文鏈接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247485212&idx=1&sn=450e9e94fa709b5eeff0de371c62072b&chksm=ea37536cdd40da7 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...