iOS自定義控件教程(一)UIKit入門痢掠,布局入門
iOS自定義控件教程(二)響應鏈原理
iOS自定義控件教程(三)觸摸事件和簡單動畫
iOS自定義控件教程(四)Target-Action響應模式
上一次我們一起做一個多段選擇的自定義控件叔壤,順便學習UIView的基本屬性和方法试浙。在iOS自定義控件教程(一)中我們完成了UILabel布局的工作匆篓,接下來我們一起研究一下觸摸響應鏈原理惨奕。
最終實現(xiàn)的效果:Github下載源碼
鏈式響應原理
先簡單普及一下響應鏈原理火架,我們可以簡單地認為iPhone屏幕就是一個容器,我們看到的各種控件(UIView和UIView子類)都是屏幕(UIWindow)這個容器中的子容器涝焙,最外層的容器是應用委托(AppDelegate)的屬性keyWindow卑笨,其實UIWindow也是UIView的子類。
這些容器的相互關系仑撞,就是我們最早學數(shù)據(jù)結(jié)構(gòu)接觸的多叉樹關系赤兴,keyWindow就是這棵樹的Root,其它它的子View都是分支隧哮。例如上面的例子桶良,我們用xcode進行調(diào)試可以得到下圖。注意在調(diào)試過程中沮翔,才有這排功能:
得到下面的層級結(jié)構(gòu)
在我們的Demo里面陨帆,總共有兩個XXXSegmentView
,第一個XXXSegmentView
有四個子UILabel
,而他的父View
是當前ViewContoller
的主View
疲牵,一個背景是純白色的全屏View
承二。
當觸摸事件被iPhone硬件接收到時,一個鏈式的觸摸信號就被開啟了纲爸。最先接收到觸摸事件的是Root
亥鸠,也就是我們應用程序的keyWindow
,keyWindow
再將觸摸事件傳遞給它的一級子View們识啦。這個傳遞過程不需要開發(fā)者用代碼實現(xiàn)负蚊,如果開發(fā)者有需要重寫傳遞,需要使用的是UIView
的- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
方法颓哮。
這個方法返回的UIView家妆,就是父View決定讓哪個孩子作為這個觸摸的響應View。這樣冕茅,觸摸事件就不斷往下級傳遞伤极,對應的View才能根據(jù)觸摸事件改變樣式。
新人在開發(fā)時嵌赠,經(jīng)常會遇到不響應觸摸的情況塑荒。有兩種常見的可能打斷觸摸鏈
1. 目標View到Root的分支中熄赡,有View沒有開啟觸摸事件姜挺,打斷了響應鏈傳遞。
這里需要知道UIView有一個基本屬性叫做userInteractionEnabled
彼硫,這個屬性為YES
時才能響應觸摸事件炊豪。所以檢查一下分支中是否有View的觸摸開關被關閉。例如拧篮,UIImageView
這個展示圖片的類词渤,默認就是不響應觸摸鏈的,如果你需要繼承它寫一個新View串绩,并且需要觸摸缺虐,或者給他添加了需要觸摸的子View,請修改他的userInteractionEnabled
屬性礁凡。
2. 目標View的觸摸事件高氮,因為層級關系被上層覆蓋的View劫走,哪怕這個View可能是透明的
根據(jù)鏈式響應原理顷牌,父View會將觸摸鏈傳給符合相應條件剪芍,并且層級關系最上層的View,所以下層的View接收不到觸摸事件窟蓝,遇到這種情況罪裹,你可以根據(jù)需要進行處理。如果覆蓋在上層View層級順序有誤,通過調(diào)用他們父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型裤翩,是否隱藏
這三個屬性资盅,都可以實現(xiàn)UIView隱身的效果。例如一個空的UIView踊赠,backgroundColor
設為[UIColor clearColor]時呵扛,背景色是透明的;alpha設為0時筐带,UIView也是透明的今穿;hidden設為YES時,UIView同樣不可見伦籍。
但他們的區(qū)別也很明顯蓝晒,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。
小結(jié)
樹型結(jié)構(gòu)沉桌,既是UIView
的觸摸響應結(jié)構(gòu)谢鹊,也是渲染鏈結(jié)構(gòu),觸摸鏈通過下面四個方法傳遞留凭,UIView
是UIResponder
的子類佃扼,下面四個方法是觸摸鏈傳遞的會調(diào)用的方法。分別對應開始
蔼夜,移動
兼耀,結(jié)束
,取消
四種狀態(tài)挎扰。
- (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;```
渲染樹型結(jié)構(gòu)要更加簡單一點翠订,調(diào)用的是`- (void)setNeedsDisplay;`方法,也就是在父View的setNeedsDisplay方法中遵倦,調(diào)用子View的setNeedsDisplay方法,這個方法會通知View在下一個cpu時間里官撼,進行重新渲染梧躺,也就是調(diào)用他們的`- (void)drawRect:(CGRect)rect;`方法實現(xiàn)繪制。
但是自從手勢(UIGesture)加入之后傲绣,觸摸鏈開發(fā)用的越來越少掠哥,但我們的[Demo](https://github.com/zsy78191/XXXSegmentView)還是使用了簡單的觸摸鏈。后面的章節(jié)我們將介紹功能更為便捷和強大手勢(UIGesture)功能秃诵。
[iOS自定義控件教程(一)UIKit入門续搀,布局入門](http://www.reibang.com/p/fd495346402f)
[iOS自定義控件教程(二)響應鏈原理](http://www.reibang.com/p/b3ce217eca5d)
[iOS自定義控件教程(三)觸摸事件和簡單動畫](http://www.reibang.com/p/58d9afb2b3d0)
[iOS自定義控件教程(四)Target-Action響應模式](http://www.reibang.com/p/6ac4ebe84e60)