AASubviews - 自動排列子檢視

qhd發表於2017-12-25

自動排列子檢視

演示圖

呼叫說明:

只需引入 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 原理檢測 subviewframehidden 變化,從而自動調整佈局。

難點是,如果只是簡單利用 KVOsuperview 來觀察 subview 的變化,會有很多問題,例如:

  1. 如果重複 addObserver 會多次觸發 observeValueForKeyPath:ofObject:change:context 方法;
  2. 如果重複 removeObserver 會導致閃退;
  3. 如果被觀察者釋放了而還沒有 removeObserver 的話,也會導致閃退,模擬器不會發生,但是真機會發生;
  4. 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 釋放,我們可以在 observiewdealloc 方法裡做 removeObserver。 這就巧妙地解決了沒移除觀察者的問題。

github地址:https://github.com/qhd/AASubviews

相關文章