【iOS】關(guān)于 UIView 的 layoutSubviews 方法

UIKit 的 UIView 是一個(gè)非常重要的類涡驮,幾乎每個(gè)嘗試 iOS 開發(fā)的程序員都會(huì)用到它。UIView 本身實(shí)現(xiàn)了Composite Pattern虱黄,所以一個(gè)應(yīng)用的界面最終可以由一群樹狀組合的 UIView 來組合而成——在這棵 UIView 樹的最頂部,是繼承于 UIView 的 UIWindow 實(shí)例,然后是由 UIWindow 實(shí)例保有的 rootViewController 的根 UIView 實(shí)例临燃,然后是在該 UIView 實(shí)例上的各種各樣的子節(jié)點(diǎn) UIView。

父 UIView 可以擁有自己的子 UIView烙心,自然而然的膜廊,父 UIView 就會(huì)面對(duì)用怎樣的策略來布局、排列這些子 UIView 的問題淫茵。在 UIView 中爪瓜,UIKit 的開發(fā)者專門提供了layoutSubviews方法來解決這個(gè)問題。

官方文檔的描述

官方文檔對(duì)于該方法有如下的描述:

(This method) Lays out subviews.

Subclasses can override this method as needed to perform more precise layout of their subviews.You should override this method only if the autoresizing and constraint-based behaviors of the subviews DO NOT offer the behavior you want. You can use your implementation to set the frame rectangles of your subviews directly.

以上節(jié)選自UIView Class Reference

Whenever the size of a view changes, UIKit applies the autoresizing behaviors of that view’s subviews andTHENcalls the layoutSubviews method of the view to let it make manual changes. You can implement the layoutSubviews method in custom views when the autoresizing behaviors by themselvesDO NOTyield the results you want. Your implementation of this method can do any of the following:

Adjust the size and position of any immediate subviews.

Add or remove subviews or Core Animation layers.

Force a subview to be redrawn by calling its setNeedsDisplay or setNeedsDisplayInRect: method.

One place where applications often lay out subviews manually is when implementing a large scrollable area. Because it is impractical to have a single large view for its scrollable content, applications often implement a root view that contains a number of smaller tile views. Each tile represents a portion of the scrollable content. When a scroll event happens, the root view calls its setNeedsLayout method to initiate a layout change. Its layoutSubviews method then repositions the tile views based on the amount of scrolling that occurred. As tiles scroll out of the view’s visible area, the layoutSubviews method moves the tiles to the incoming edge, replacing their contents in the process.

以上節(jié)選自View Programming Guide for iOS

從文檔的描述可以看到匙瘪,layoutSubviews 的主要功能就是讓程序員自己實(shí)現(xiàn)子 UIViews 的布局算法铆铆,從而在需要重新布局的時(shí)候,父 UIView 會(huì)按照這個(gè)流程重新布局自己的子 UIViews丹喻。而且薄货,layoutSubviews 方法只能被系統(tǒng)觸發(fā)調(diào)用,程序員不能手動(dòng)直接調(diào)用該方法碍论。要引起該方法的調(diào)用谅猾,可以調(diào)用 UIView 的setNeedsLayout方法來標(biāo)記一個(gè) UIView。這樣一來鳍悠,在 UI 線程的下次繪制循環(huán)中税娜,系統(tǒng)便會(huì)調(diào)用該 UIView 的 layoutSubviews 方法。

使用 layoutSubviews 的實(shí)例

一個(gè)比較典型的例子來自于RDVTabBarController項(xiàng)目贼涩,它的目標(biāo)是實(shí)現(xiàn)一個(gè)提供高定制性的 TabBarController巧涧。在這個(gè)項(xiàng)目中,作者使用了 layoutSubviews 來控制 TabBar 的子 UIView——TabBarItem 的重新布局遥倦,從而達(dá)到 TabBar 發(fā)生變化時(shí) TabBarItem 的繪制與布局不會(huì)出錯(cuò)的目的:



我在代碼中的注釋解釋了這段代碼都做了什么谤绳。如果你要實(shí)現(xiàn)自己的 layoutSubviews 方法的話,可以參考這個(gè)例子的流程袒哥。

何時(shí)被調(diào)用

一個(gè)曾經(jīng)讓我比較疑惑的問題是缩筛,既然我不能手動(dòng)直接調(diào)用該方法,那在什么時(shí)候堡称、何種條件下這個(gè)方法會(huì)被調(diào)用呢瞎抛?

Stackoverflow 上已經(jīng)有相關(guān)的討論了(作者在他的博客上有更詳細(xì)的描述),并且有一位朋友給出了很不錯(cuò)的解答:

init does not cause layoutSubviews to be called (duh)

addSubview causes layoutSubviews to be called on the view being added, the view it’s being added to (target view), and all the subviews of the target

view setFrame intelligently calls layoutSubviews on the view having its frame set only if the size parameter of the frame is different

scrolling a UIScrollView causes layoutSubviews to be called on the scrollView, and its superview

rotating a device only calls layoutSubview on the parent view (the responding viewControllers primary view)

Resizing a view will call layoutSubviews on its superview

也就是說却紧,layoutSubviews 方法會(huì)在這些情況下桐臊,在這些 UIView 實(shí)例上被調(diào)用:

addSubview 被調(diào)用時(shí):target view(一定會(huì))胎撤,以及被添加的 view(第一次調(diào)用會(huì))

更改 UIView 的 frame 時(shí):被更改 frame 的 view(frame 與之前不同時(shí))

對(duì)于 UIScrollView 而言,滾動(dòng)式:scroll view

設(shè)備的 orientation 改變時(shí):涉及改變的 UIViewController 的 root view

使用 CGAffineTransformScale 改變 view 的 transform 屬性時(shí)断凶,view 的 superview:被改變的 view

然而伤提,根據(jù)我自己的實(shí)驗(yàn),上面的描述并不是很完善的认烁。我的兩點(diǎn)補(bǔ)充如下:

第一次調(diào)用 addSubview 的時(shí)候肿男,target view 和被添加到 target view 的 view 的 layoutSubviews 方法會(huì)被調(diào)用。在已經(jīng)添加完畢后却嗡,若 target view 已經(jīng)擁有該被添加 view舶沛,則只有 target view 的 layoutSubviews 方法會(huì)被調(diào)用〈凹郏“and all the subviews of the target”這句話是錯(cuò)誤的如庭。

只有 UIView 處于 key window 的 UIView 樹中時(shí),該 UIView 的 layoutSubviews 方法才有可能被調(diào)用撼港。不在樹中的不會(huì)被調(diào)用柱彻。這也是為什么 Stackoverflow 上的討論中這個(gè)答案的第二點(diǎn)會(huì)被提出。

小結(jié)

使用 layoutSubviews 可以讓應(yīng)用界面的適應(yīng)能力更強(qiáng)餐胀。如果 UIKit 默認(rèn)提供的自動(dòng)布局機(jī)制Auto Layout不能提供給你想要的 UIView 布局行為,你可以自己定制該方法來決定布局行為瘤载。



轉(zhuǎn)自?關(guān)于 UIView 的 layoutSubviews 方法 - 巴赫在編碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末否灾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鸣奔,更是在濱河造成了極大的恐慌墨技,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挎狸,死亡現(xiàn)場(chǎng)離奇詭異扣汪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锨匆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門崭别,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人恐锣,你說我怎么就攤上這事茅主。” “怎么了土榴?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵诀姚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我玷禽,道長(zhǎng)赫段,這世上最難降的妖魔是什么呀打? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮糯笙,結(jié)果婚禮上贬丛,老公的妹妹穿的比我還像新娘。我一直安慰自己炬丸,他們只是感情好瘫寝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稠炬,像睡著了一般焕阿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上首启,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天暮屡,我揣著相機(jī)與錄音,去河邊找鬼毅桃。 笑死褒纲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钥飞。 我是一名探鬼主播莺掠,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼读宙!你這毒婦竟也來了彻秆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤结闸,失蹤者是張志新(化名)和其女友劉穎唇兑,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體桦锄,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扎附,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了结耀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片留夜。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖图甜,靈堂內(nèi)的尸體忽然破棺而出香伴,到底是詐尸還是另有隱情,我是刑警寧澤具则,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布即纲,位于F島的核電站,受9級(jí)特大地震影響博肋,放射性物質(zhì)發(fā)生泄漏低斋。R本人自食惡果不足惜蜂厅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望膊畴。 院中可真熱鬧掘猿,春花似錦、人聲如沸唇跨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽买猖。三九已至改橘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間玉控,已是汗流浹背飞主。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留高诺,地道東北人碌识。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像虱而,于是被迫代替她去往敵國(guó)和親筏餐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容