自動排列子視圖
調用說明:
只需引入 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