自動排列子檢視
呼叫說明:
只需引入 AASubviews.h
後,呼叫下面的方法即可:
+ (void)superview:(UIView *)superview subviews:(NSArray *)subviews;
複製程式碼
使用場景:
如果你使用 frame
來佈局,這是一個很方便的工具。場景是這樣的,一個頁面由多個不同的展示元素組成,考慮到可維護性和擴充套件性,這些元素可以劃分成從上至下排列的多個分組,一個分組就是一個自定義的 subview
,每個 subview
實現各自內部的展示邏輯,並且有以下特點:
subview
的高度可能變化;subview
也可能突然隱藏或顯示;- 由於以上兩點,
superview
的高度也會跟著變化; subview
的排列順序可能變化。
演示例子:
例如需求是展示某個商家的詳情頁,對於酒店(Hotel)型別商家是下面這樣展示的,並且,為了方便管理,從上至下劃分為以下幾個部分:
還有互動上是這樣的:點選“關閉”按鈕會隱藏 DemoAdView
,點選“展開”會使 DemoDescView
變高,點選“重置”會使所有 view
重置一遍。並且對於商店(Shop)型別的商家是有不同的展示方式。
用 AASubViews
可以很方便的管理這種變化。
DemoHeaderView *headerView = [DemoHeaderView createViewFromXib];
DemoNameView *nameView = [DemoNameView createViewFromXib];
DemoAdView *adView = [DemoAdView createViewFromXib];
DemoDescView *descView = [DemoDescView createViewFromXib];
DemoCommentView *commentView = [DemoCommentView createViewFromXib];
NSArray *subviews = nil;
if (self.type == DemoTypeHotel) {
subviews = @[headerView, nameView, adView, descView, commentView];
} else {
subviews = @[adView, nameView, descView, commentView];
}
[AASubviews superview:self.scrollView subviews:subviews];
複製程式碼
要檢視更詳細的程式碼請開啟工程。
原理分析:
從上至下排列 subviews
,使用 KVO
原理檢測 subview
的 frame
和 hidden
變化,從而自動調整佈局。
難點是,如果只是簡單利用 KVO
用 superview
來觀察 subview
的變化,會有很多問題,例如:
- 如果重複
addObserver
會多次觸發observeValueForKeyPath:ofObject:change:context
方法; - 如果重複
removeObserver
會導致閃退; - 如果被觀察者釋放了而還沒有
removeObserver
的話,也會導致閃退,模擬器不會發生,但是真機會發生; superview
觀察subview
,實現observeValueForKeyPath:ofObject:change:context
方法,最初想到有兩種方式,第一種是自定義一個UIView
的子類,所有的superview
繼承於這個類,這種方式改動太大了,不可取;第二種是用category
的方式實現這個方法,這種方式會影響到整個工程所有的view
,這種方式也不太好。
為了解決問題 1 和 2,可以每個 subview
做一個標記是否被觀察了。為了解決問題 3 和 4,可以引入一個專門觀察 subview
的類 AAObserver
,然後 superview
持有這個類的例項。
以下是物件的引用關係,實線代表強引用,虛線代表弱引用:
當 addSubview
的時候 addObserver
,當 removeFromSuperview
的時候 removeObserver
。但是當 controller
退出時 superview
自動釋放時,導致 subviews
也會自動 removeFromSuperview
, 因為我們無法獲取 removeFromSuperview
的回撥,這時候我們怎麼 removeObserver
呢?看上面的引用關係圖,因為 observer
是對 subviews
強引用的,所以 observer
的釋放肯定在 subviews
的釋放之前, superview
釋放會導致 observer
釋放,我們可以在 obserview
的 dealloc
方法裡做 removeObserver
。 這就巧妙地解決了沒移除觀察者的問題。
github地址:https://github.com/qhd/AASubviews