iOS自定義控件教程(二)關於響應鏈的那些事

上一次咱們一塊兒作一個多段選擇的自定義控件,順便學習UIView的基本屬性和方法。在iOS自定義控件教程(一)中咱們完成了UILabel佈局的工做,接下來咱們一塊兒研究一下觸摸響應鏈原理。
最終實現的效果:Github下載源碼git

鏈式響應原理

先簡單普及一下響應鏈原理,咱們能夠簡單地認爲iPhone屏幕就是一個容器,咱們看到的各類控件(UIView和UIView子類)都是屏幕(UIWindow)這個容器中的子容器,最外層的容器是應用委託(AppDelegate)的屬性keyWindow,其實UIWindow也是UIView的子類。github

這些容器的相互關係,就是咱們最先學數據結構接觸的多叉樹關係,keyWindow就是這棵樹的Root,其它它的子View都是分支。例如上面的例子,咱們用xcode進行調試能夠獲得下圖。注意在調試過程當中,纔有這排功能:xcode

12.png

獲得下面的層級結構數據結構

屏幕快照 2016-01-04 11.13.53.png

在咱們的Demo裏面,總共有兩個XXXSegmentView,第一個XXXSegmentView有四個子UILabel,而他的父View是當前ViewContoller的主View,一個背景是純白色的全屏View佈局

觸摸事件被iPhone硬件接收到時,一個鏈式的觸摸信號就被開啓了。最早接收到觸摸事件的是Root,也就是咱們應用程序的keyWindowkeyWindow再將觸摸事件傳遞給它的一級子View們。這個傳遞過程不須要開發者用代碼實現,若是開發者有須要重寫傳遞,須要使用的是UIView- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; 方法。學習

這個方法返回的UIView,就是父View決定讓哪一個孩子做爲這個觸摸的響應View。這樣,觸摸事件就不斷往下級傳遞,對應的View才能根據觸摸事件改變樣式。spa

新人在開發時,常常會遇到不響應觸摸的狀況。有兩種常見的可能打斷觸摸鏈調試

1. 目標View到Root的分支中,有View沒有開啓觸摸事件,打斷了響應鏈傳遞。code

這裏須要知道UIView有一個基本屬性叫作userInteractionEnabled,這個屬性爲YES時才能響應觸摸事件。因此檢查一下分支中是否有View的觸摸開關被關閉。例如,UIImageView這個展現圖片的類,默認就是不響應觸摸鏈的,若是你須要繼承它寫一個新View,而且須要觸摸,或者給他添加了須要觸摸的子View,請修改他的userInteractionEnabled屬性。繼承

2. 目標View的觸摸事件,由於層級關係被上層覆蓋的View劫走,哪怕這個View多是透明的

根據鏈式響應原理,父View會將觸摸鏈傳給符合相應條件,而且層級關係最上層的View,因此下層的View接收不到觸摸事件,遇到這種狀況,你能夠根據須要進行處理。若是覆蓋在上層View層級順序有誤,經過調用他們父View的兩個方法能夠輕鬆交換他們層級覆蓋關係。

- (void)bringSubviewToFront:(UIView *)view;
- (void)sendSubviewToBack:(UIView *)view;

若是上層View就是要放在上面,那就關掉上層View的響應鏈,userInteractionEnabled設爲NO,這樣父View在進行hitTest時就會拋棄這個View,選擇層級更低的View傳遞。

關於透明

這裏請注意UIView的幾個不一樣的屬性。

backgroundColor 背景顏色,UIColor類
alpha 顏色中的alpha通道值,這個值範圍0-1,等於0時徹底透明,等於1時,徹底不透明
hidden BOOL型,是否隱藏

這三個屬性,均可以實現UIView隱身的效果。例如一個空的UIView,backgroundColor設爲[UIColor clearColor]時,背景色是透明的;alpha設爲0時,UIView也是透明的;hidden設爲YES時,UIView一樣不可見。

但他們的區別也很明顯,backgroundColor屬性的修改,不影響子View,因此子View不會由於父View的backgroundColor設爲[UIColor clearColor]而隱藏,同時,父View一樣響應觸摸鏈。

而alpha值則不一樣,渲染時alpha值時一個疊加屬性,例如父View透明度爲0.5,子View透明度爲0.5,這時渲染出來的真實效果,子View的透明的應該爲0.5*0.5 = 0.25,因此當父View的alpha爲0時,子View也是徹底不可見的。另外,alpha爲0的View,是不響應觸摸鏈的。

最後這個hidden屬性,直接從根源,決定要不要渲染這個View,不像alpha屬性,是不須要渲染時計算最終的渲染效果的,由於這個屬性爲YES時,根本不進行渲染,因此渲染鏈都斷了,子View也不會渲染了,更不會響應觸摸了。

響應鏈hitTest的三個條件,就是userInteractionEnabled = YES,而且alpha != 0, 且hidden = NO。

小結

樹型結構,既是UIView的觸摸響應結構,也是渲染鏈結構,觸摸鏈經過下面四個方法傳遞,UIViewUIResponder的子類,下面四個方法是觸摸鏈傳遞的會調用的方法。分別對應開始移動結束取消四種狀態。

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

渲染樹型結構要更加簡單一點,調用的是- (void)setNeedsDisplay;方法,也就是在父View的setNeedsDisplay方法中,調用子View的setNeedsDisplay方法,這個方法會通知View在下一個cpu時間裏,進行從新渲染,也就是調用他們的- (void)drawRect:(CGRect)rect;方法實現繪製。

可是自從手勢(UIGesture)加入以後,觸摸鏈開發用的愈來愈少,但咱們的Demo仍是使用了簡單的觸摸鏈。後面的章節咱們將介紹功能更爲便捷和強大手勢(UIGesture)功能。

相關文章
相關標籤/搜索