【iOS】關于 UIView 的 layoutSubviews 方法

UIKit 的 UIView 是一個非常重要的類,幾乎每個嘗試 iOS 開發的程序員都會用到它。UIView 本身實現了Composite Pattern,所以一個應用的界面最終可以由一群樹狀組合的 UIView 來組合而成——在這棵 UIView 樹的最頂部,是繼承于 UIView 的 UIWindow 實例,然后是由 UIWindow 實例保有的 rootViewController 的根 UIView 實例,然后是在該 UIView 實例上的各種各樣的子節點 UIView。

父 UIView 可以擁有自己的子 UIView,自然而然的,父 UIView 就會面對用怎樣的策略來布局、排列這些子 UIView 的問題。在 UIView 中,UIKit 的開發者專門提供了layoutSubviews方法來解決這個問題。

官方文檔的描述

官方文檔對于該方法有如下的描述:

(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.

以上節選自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.

以上節選自View Programming Guide for iOS

從文檔的描述可以看到,layoutSubviews 的主要功能就是讓程序員自己實現子 UIViews 的布局算法,從而在需要重新布局的時候,父 UIView 會按照這個流程重新布局自己的子 UIViews。而且,layoutSubviews 方法只能被系統觸發調用,程序員不能手動直接調用該方法。要引起該方法的調用,可以調用 UIView 的setNeedsLayout方法來標記一個 UIView。這樣一來,在 UI 線程的下次繪制循環中,系統便會調用該 UIView 的 layoutSubviews 方法。

使用 layoutSubviews 的實例

一個比較典型的例子來自于RDVTabBarController項目,它的目標是實現一個提供高定制性的 TabBarController。在這個項目中,作者使用了 layoutSubviews 來控制 TabBar 的子 UIView——TabBarItem 的重新布局,從而達到 TabBar 發生變化時 TabBarItem 的繪制與布局不會出錯的目的:



我在代碼中的注釋解釋了這段代碼都做了什么。如果你要實現自己的 layoutSubviews 方法的話,可以參考這個例子的流程。

何時被調用

一個曾經讓我比較疑惑的問題是,既然我不能手動直接調用該方法,那在什么時候、何種條件下這個方法會被調用呢?

Stackoverflow 上已經有相關的討論了(作者在他的博客上有更詳細的描述),并且有一位朋友給出了很不錯的解答:

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 方法會在這些情況下,在這些 UIView 實例上被調用:

addSubview 被調用時:target view(一定會),以及被添加的 view(第一次調用會)

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

對于 UIScrollView 而言,滾動式:scroll view

設備的 orientation 改變時:涉及改變的 UIViewController 的 root view

使用 CGAffineTransformScale 改變 view 的 transform 屬性時,view 的 superview:被改變的 view

然而,根據我自己的實驗,上面的描述并不是很完善的。我的兩點補充如下:

第一次調用 addSubview 的時候,target view 和被添加到 target view 的 view 的 layoutSubviews 方法會被調用。在已經添加完畢后,若 target view 已經擁有該被添加 view,則只有 target view 的 layoutSubviews 方法會被調用。“and all the subviews of the target”這句話是錯誤的。

只有 UIView 處于 key window 的 UIView 樹中時,該 UIView 的 layoutSubviews 方法才有可能被調用。不在樹中的不會被調用。這也是為什么 Stackoverflow 上的討論中這個答案的第二點會被提出。

小結

使用 layoutSubviews 可以讓應用界面的適應能力更強。如果 UIKit 默認提供的自動布局機制Auto Layout不能提供給你想要的 UIView 布局行為,你可以自己定制該方法來決定布局行為。



轉自?關于 UIView 的 layoutSubviews 方法 - 巴赫在編碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容