主頁 > 移動端開發 > [Android開發學iOS系列] Auto Layout

[Android開發學iOS系列] Auto Layout

2022-11-03 08:20:11 移動端開發

[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

  • Understanding Auto Layout - Official Doc
  • High Performance Auto Layout - WWDC2018
  • Mysteries of Auto Layout, Part 1 - WWDC 2015
  • Mysteries of Auto Layout, Part 2 - WWDC 2015
  • Auto Layout Basics at codepath
  • The Auto Layout cheat sheet
  • Behind the Scenes with Auto Layout - iOS Conf SG 2019
  • AutoLayout Log分析小工具
作者: 圣騎士Wind
出處: 博客園: 圣騎士Wind
Github: https://github.com/mengdd
微信公眾號: 圣騎士Wind
微信公眾號: 圣騎士Wind

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/526066.html

標籤:iOS

上一篇:kotlin協程小記

下一篇:kotlin協程小記

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more