03.subview_and_superview

Subview and Superview

作者:PMST
文章:Views - Subview and Superview
系列:The Swift Beginner
寫於:2015.04.27

正文

在很久以前,蘋果公司的視圖繪製機制和現在是大相徑庭。那時任何一個視圖擁有自己的“私人領地”——精確來說就是一個矩形。假如某個非該視圖的subview想要呈現其中,這是不被允許的!因爲當視圖重繪(redraw)矩形區域時,會擦除所有不屬於自己的內容;相應地,屬於該視圖的subview內容只允許在指定的矩形區域內繪製,想要“越獄”(將內容繪製到矩形框外)出去,沒門!

而從OS X 10.5開始,這些條條框框都已經被廢除了。目前蘋果公司給出的視圖繪製機制,完全摒棄了先前的那些限制。而值得慶幸的是iOS繪圖就是基於新的架構!在iOS中,任何一個subview都允許從superview“越獄出去”,這意味着,打破了矩形區域的限制,子視圖繪畫面積不僅僅只是那麼一小片區域,而是整個!此外其他視圖(不屬於superviewviews)能夠覆蓋別的視圖,這在以前是不被允許的。

下面給出一個小demo,下圖顯示了三個相互重疊的視圖。爲了更直觀地觀看,給三個視圖的背景上了色。現在問題來了,你無從得知三個視圖之間的關係,比如:三個視圖是相互獨立?誰是誰的子視圖?誰和誰是同級的?

figure1-1

事實上,三者的關係是這樣的:

  • 粉色視圖和紅色視圖是同級關係
  • 綠色視圖是粉色視圖的子視圖

storyboard中,拖拉兩個viewmain view中,分別設置背景顏色爲粉色和紅色;然後再次拖拽一個view到粉色視圖中,設其背景顏色爲綠色;現在打開nib editor查看視圖層級設置。

figure1-2

小技巧:當你的應用程序在運行時想要查看視圖層級關係,選擇 Debug->View Debugging-> Capture View Hierarchy

關於視圖在視圖層級中的擺放位置是非常有講究了,這也決定了視圖的繪製順序!

  • 相同層級的視圖(屬於同一個superView)繪製順序是固定的:自上而下,只有當前者繪製完畢,才進行下一個視圖繪製。特殊情況:假如後者繪製區域和前者有重疊,那麼就會造成覆蓋現象,前者視圖呈現在後者視圖之後。

  • 先繪製superview,在繪製屬於父視圖的subview

結合本文第一幅圖進行繪製順序講解,粉色視圖和紅色視圖爲同一級,同屬於父視圖main view;粉色視圖在前,紅色視圖爲後,因此首先繪製粉色視圖;粉色視圖包含綠色子視圖,當粉色視圖繪製完畢,接下來是繪製綠色視圖(注意:同級視圖之間,前者所有視圖,包括子視圖全部繪製完畢,才進行下一個視圖);最後是繪製紅色視圖,顯然它會覆蓋粉色以及綠色視圖。

storyboard中設置層級關係

選中main.storyboard,左側會羅列出所有視圖場景,各視圖層級關係一覽無餘。現在選中一個粉色視圖,Editor-> Arrange -> Send Forward(其他還有 Send to Front,Send to Back,Send Backward),操作結果會把粉色視圖下移到紅色視圖之下,意味着粉色視圖在最前面,紅色視圖放置到最底下被遮蓋。

補充一點視圖層級的知識。

  • 當子視圖從父視圖中移除,子視圖下的所有視圖也將被移除;當子視圖在父視圖中移動位置,其下所有視圖也跟着移動。

  • 子視圖會繼承父視圖的透明程度(degree of transparency),這個很有意思。

  • 子視圖繪畫區域可以超出父視圖限定的矩形區域,但是!父視圖有權利選擇顯示還是隱藏那些超出範圍的內容!!即clipping,我們可以通過設置視圖的clipsToBounds屬性來決定顯示還是隱藏。

  • 父視圖可以擁有多個子視圖,從內存管理意義上來說,更像是一組數組;採用引用方式關聯每一個子視圖;視圖數組負責一些添加或移除的工作,當有新的子視圖加入進來,相應地數組新增一個元素,相反有子視圖從父視圖移除,數組要刪除對應的元素。

  • 父視圖的尺寸大小改變時,在它其中的子視圖將會自動調整大小。

繼續說說視圖(UIView),前文談及它一個特性,只有一個父視圖(superview)和多個子視圖(an array of UIView,注:數組是有序的!),允許你從中追蹤視圖層級關係;從方法上來說,isDescendantOfView:方法能夠確定一個視圖是否是另外一個視圖的子視圖;當然你也可以選擇使用引用方式來獲得某個視圖,比如通過oulet;最後,每一個視圖都能夠設置一個獨一無二的標籤——tag屬性,通過數值大小來決定層級關係,這都取決你!

代碼設置層級關係

使用代碼手工設置視圖層級關係非常簡單。最爲熟悉的便是addSubview:,爲一個視圖添加子視圖;與之對應的removeFromSuperview:,則是從父視圖中移除子視圖,記住一旦移除意味着釋放(released),假如你打算好之後還要重用它,那麼就別移除掉,繼續保持住!

iOS還提供了各種事件(Events)來通知視圖動態變化。當然想要使用還是有條件限制的:一.視圖必須有子視圖;二.使用override來重寫方法,具體有:

  • didAddSubview:, willRemoveSubview:

  • didMoveToSuperview, willMoveToSuperview:

  • didMoveToWindow, willMoveToWindow:

一旦addSubview:被調用,視圖(將要添加爲子視圖的view)會被放置到superview的子視圖中的最新一個,從層級關係上來說,自上而下,它是最下面一個!因此在繪製時,它作爲壓軸最後繪圖,所有其他視圖都是在它之下。

前面說到一個視圖的子視圖可以看做一個數組(array of UIView),它的索引號自然就是從0開始。當然iOS也提供了一系列方法允許你從一個指定的索引號插入視圖,至於放在前面還是後面也是可以選擇的!另外還提供了方法能夠交換兩個同級關係的視圖。

  • insertSubview:atIndex:

  • insertSubview:belowSubview:,insterSubview:aboveSubview

  • exchangeSubviewAtIndex:withSubviewAtIndex:

  • bringSubviewToFront:,sendSubviewToBack:

不過iOS貌似沒有提供移除所有子視圖的方法,這個倒是有點讓人詫異的,因此我們只能自己遍歷視圖數組,逐個刪除。

for v in myView.subviews as UIView{
    v.removeFromSuperview()
}

恩…貌似這麼寫好low。那麼換種方式:

    (myView.subviews as [UIView]).map{$0.removeFromSuperview()}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章