iOS 事件處理總結(jié)與思考

UIControl

UIControl繼承自UIView。
UIControl 依賴于Target-Action設(shè)計(jì)模式弓候。即當(dāng)發(fā)生一個(gè)事件時(shí),UIControl會(huì)調(diào)用sendAction:to:forEvent:方法來(lái)將行為消息發(fā)送到UIApplication對(duì)象余境,再由UIApplication對(duì)象調(diào)用其sendAction:to:fromSender:forEvent:方法來(lái)將消息分發(fā)到指定的target上钓账。如果沒(méi)有指定target,則會(huì)將事件分發(fā)到響應(yīng)鏈上第一個(gè)想處理該消息的對(duì)象上题暖。

UIControl有不同的狀態(tài)

typedef NS_OPTIONS(NSUInteger, UIControlState) {
    UIControlStateNormal       = 0,
    UIControlStateHighlighted  = 1 << 0,                  // used when UIControl isHighlighted is set
    UIControlStateDisabled     = 1 << 1,
    UIControlStateSelected     = 1 << 2,                  // flag usable by app (see below)
    UIControlStateFocused NS_ENUM_AVAILABLE_IOS(9_0) = 1 << 3, // Applicable only when the screen supports focus
    UIControlStateApplication  = 0x00FF0000,              // additional flags available for application use
    UIControlStateReserved     = 0xFF000000               // flags reserved for internal framework use
};

通過(guò)繼承UIControl類按傅,就可以使用OC內(nèi)建的 target-action 機(jī)制以及簡(jiǎn)化版的 event-handling。主要有以下兩類方法來(lái)實(shí)現(xiàn)UIControl胧卤。

  1. 重寫 sendAction:to:forEvent:方法唯绍。這樣就可以觀察或者改寫OC的分發(fā)機(jī)制,從而達(dá)到監(jiān)聽(tīng)某個(gè)特定的對(duì)象(object)對(duì)于特定的事件(event)做了什么特定的處理(selector)枝誊。進(jìn)一步的可以攔截到這些對(duì)象的事件况芒,把它們發(fā)送到其他對(duì)象,或者讓本對(duì)象執(zhí)行其他的方法叶撒。

  2. 重寫

    beginTrackingWithTouch:withEvent:, 
        
    continueTrackingWithTouch:withEvent:, 
        
    endTrackingWithTouch:withEvent:, 
          
    cancelTrackingWithEvent: 
        

等方法绝骚。這樣就可以追蹤并獲取到control對(duì)象的狀態(tài)耐版。進(jìn)一步的,可以依據(jù)這些狀態(tài)去更新頁(yè)面上控件的狀態(tài)压汪;或者調(diào)用某些方法粪牲,執(zhí)行其他命令。

此處需要注意止剖,蘋果文檔上有一句:Always use these methods to track touch events instead of the methods defined by the UIResponder class.不知為何腺阳,蘋果要這樣寫。

UIResponder

UIResponder對(duì)象及其子類的對(duì)象都叫做響應(yīng)者滴须。繼承關(guān)系如下圖:


UIResponder.png

也就是說(shuō)UIApplication舌狗,UIViewCOntroller,UIView都是響應(yīng)者扔水,都可以接收并處理事件痛侍。
響應(yīng)者是響應(yīng)事件的。在iOS中魔市,事件分為三種主届。即觸摸事件,加速計(jì)事件待德,遠(yuǎn)程控制事件君丁。
一般開(kāi)發(fā)中觸摸事件使用最頻繁,而且其他兩種事件處理方式與觸摸事件大同小異将宪,所以只介紹觸摸事件绘闷。

觸摸事件包括:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

可以看出,觸摸事件包括用戶交互的整個(gè)過(guò)程较坛。包括觸摸開(kāi)始印蔗,用戶滑動(dòng),觸摸結(jié)束丑勤,以及觸摸因?yàn)槠渌录ū热鐏?lái)電話)被取消华嘹。

以上方法中都包含兩個(gè)參數(shù):touches和event。
touches是一個(gè)包含UITouch對(duì)象的集合法竞。

UITouch對(duì)象記錄著某個(gè)事件中手指的相關(guān)信息耙厚,比如位置,大小岔霸,運(yùn)動(dòng)狀況薛躬,手指在屏幕上的壓力(限于有3D Touch的手機(jī))等。
主要屬性有:

  1. window 觸摸所在的窗口
  2. view 觸摸所在的視圖
  3. tapCount 點(diǎn)擊屏幕的次數(shù)
  4. majorRadius 觸摸范圍半徑秉剑。錘子科技的Big-Bang就是根據(jù)觸摸半徑的大小來(lái)判斷是否“炸開(kāi)”文字的泛豪。
  5. gestureRecognizers 手勢(shì)數(shù)組。如果觸摸事件是發(fā)生在view對(duì)象上的侦鹏,給這個(gè)view對(duì)象添加的手勢(shì)UIGestureRecognizer都會(huì)在這個(gè)數(shù)組中诡曙。如果view對(duì)象沒(méi)有添加過(guò)手勢(shì),這個(gè)數(shù)組中也有一個(gè)系統(tǒng)手勢(shì):_UISystemGestureGateGestureRecognizer略水。

UIGestureRecognizer

手勢(shì)識(shí)別价卤。蘋果文檔中有這樣一段描述值得注意:

A window delivers touch events to a gesture recognizer before it delivers them to the hit-tested view attached to the gesture recognizer. Generally, if a gesture recognizer analyzes the stream of touches in a multi-touch sequence and doesn’t recognize its gesture, the view receives the full complement of touches. If a gesture recognizer recognizes its gesture, the remaining touches for the view are cancelled. The usual sequence of actions in gesture recognition follows a path determined by default values of the cancelsTouchesInView, delaysTouchesBegan, delaysTouchesEnded properties:

即,當(dāng)觸摸事件發(fā)生時(shí)渊涝,如果慎璧,手勢(shì)對(duì)象會(huì)先于view對(duì)象獲取到觸摸事件。如果這個(gè)手勢(shì)對(duì)象可以處理該事件跨释,那么view對(duì)象就不會(huì)接收到觸摸事件胸私。如果還想讓view也接收到事件,就要把手勢(shì)的cancelsTouchesInView屬性設(shè)置為NO鳖谈。
具體參見(jiàn)以下代碼:

//給一個(gè)view對(duì)象添加UIButton子控件岁疼。
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(10, 20, 200, 20)];
[btn setTitle:@"button" forState:UIControlStateNormal];
//btn對(duì)象添加 target-action
[btn addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:btn];
UITapGestureRecognizer *btnTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(buttonTapAction)];
//設(shè)置cancelsTouchesInView屬性。
btnTap.cancelsTouchesInView = NO;
[btn addGestureRecognizer:btnTap];

以上代碼缆娃,當(dāng)cancelsTouchesInView為YES時(shí)捷绒。只響應(yīng)buttonTapAction方法;當(dāng)為NO時(shí)贯要,事件可以繼續(xù)傳遞暖侨,buttonTapActionbuttonAction方法均響應(yīng)。

UIGestureRecognizer 響應(yīng)始終在主線程崇渗。測(cè)試代碼中曾把添加手勢(shì)的代碼放在子線程中字逗,結(jié)果發(fā)現(xiàn)手勢(shì)的響應(yīng)仍然是在主線程。我猜測(cè)是這樣的宅广,手勢(shì)的添加不在乎在哪個(gè)線程葫掉,只要把手勢(shì)添加到view上即可。觸摸事件的發(fā)生以及傳遞是在主線程的乘碑。所以挖息,我們的響應(yīng)方法最終在主線程被執(zhí)行。

但是兽肤,文檔中有一句我不是很明白A gesture recognizer doesn’t participate in the view’s responder chain.查了一些資料套腹,還是沒(méi)有頭緒,這個(gè)問(wèn)題先記著资铡,后續(xù)處理电禀。//TODO:find the answer.

事件傳遞

問(wèn)題來(lái)了,如果UIResponder笤休,UIGestureRecognizer尖飞,UIControl各自的對(duì)象同時(shí)出現(xiàn)一個(gè)或者多個(gè);又或者他們?nèi)齻€(gè)中不同的對(duì)象同時(shí)出現(xiàn),那響應(yīng)順序是什么樣子的呢政基?

  1. 上面分析過(guò)贞铣,UIGestureRecognizer和UIControl同時(shí)存在時(shí)。會(huì)優(yōu)先處理UIGestureRecognizer沮明,如果事件能夠響應(yīng)辕坝,則不再處理UIControl。
  2. 如果view對(duì)象在添加了UIGestureRecognizer手勢(shì)的同時(shí)荐健,也實(shí)現(xiàn)了UIResponder的方法酱畅,比如touchBegin。那響應(yīng)順序如何江场?以下是我的測(cè)試:
    如下圖的結(jié)構(gòu):
UITestView.png

UITestViewB和UITestViewA都是UIView的子類纺酸。并且都添加了單擊手勢(shì)
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
同時(shí),實(shí)現(xiàn)了- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event方法
我測(cè)試的結(jié)果是先響應(yīng)UIResponder方法址否,再響應(yīng)UIGestureRecognizer餐蔬。

另外,如果想要UIResponder繼續(xù)傳遞在张,那就直接調(diào)用super方法用含,觸摸事件就可以接著傳遞給父控件;
如果想要UIGestureRecognizer繼續(xù)傳遞帮匾,那就重寫可以同時(shí)響應(yīng)的代理方法--gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer啄骇。雖然這個(gè)方法的說(shuō)法是可以讓一個(gè)對(duì)象同時(shí)響應(yīng)多個(gè)手勢(shì),但經(jīng)過(guò)測(cè)試發(fā)現(xiàn)瘟斜,當(dāng)這個(gè)方法返回YES時(shí)缸夹,父子控件可以同時(shí)響應(yīng)一個(gè)觸摸事件。如果父子控件的這個(gè)代理方法都返回NO(或者不寫螺句,默認(rèn)是NO)虽惭,那么只有子控件響應(yīng)觸摸事件。

這里我還有一個(gè)問(wèn)題蛇尚,沒(méi)有解決芽唇。就是UIControl了類的點(diǎn)擊事件如何繼續(xù)傳遞。情景是這樣的取劫。一個(gè)view中添加一個(gè)button子控件匆笤。butoon通過(guò)addTarget添加target-action。如何在點(diǎn)擊button后谱邪,讓該點(diǎn)擊事件繼續(xù)傳遞到父控件view上炮捧。view實(shí)現(xiàn)了toucheBegin并且也添加了tap手勢(shì)。

我能想到的方法是惦银,給button再次添加target-action咆课。即在點(diǎn)擊button是發(fā)出給兩個(gè)target發(fā)送message末誓,其中一個(gè)message是view。即把觸摸事件發(fā)送給view书蚪。但是這樣只能夠?qū)崿F(xiàn)相同的效果喇澡,并不是把同一個(gè)觸摸事件傳遞給view。雖然應(yīng)該不會(huì)有這樣的需求善炫,但我只是好奇這能夠?qū)崿F(xiàn)嗎撩幽?

參考:

UIControl

UIControl補(bǔ)充

Target-Action

UIGestureRecognizer

EventHandlingiPhoneOS

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末库继,一起剝皮案震驚了整個(gè)濱河市箩艺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宪萄,老刑警劉巖艺谆,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拜英,居然都是意外死亡静汤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門居凶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)虫给,“玉大人毫玖,你說(shuō)我怎么就攤上這事搬葬。” “怎么了倦微?”我有些...
    開(kāi)封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵弄兜,是天一觀的道長(zhǎng)药蜻。 經(jīng)常有香客問(wèn)我,道長(zhǎng)替饿,這世上最難降的妖魔是什么语泽? 我笑而不...
    開(kāi)封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮视卢,結(jié)果婚禮上踱卵,老公的妹妹穿的比我還像新娘。我一直安慰自己据过,他們只是感情好惋砂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蝶俱,像睡著了一般班利。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上榨呆,一...
    開(kāi)封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天罗标,我揣著相機(jī)與錄音庸队,去河邊找鬼。 笑死闯割,一個(gè)胖子當(dāng)著我的面吹牛彻消,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宙拉,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼宾尚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了谢澈?” 一聲冷哼從身側(cè)響起煌贴,我...
    開(kāi)封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锥忿,沒(méi)想到半個(gè)月后牛郑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敬鬓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年淹朋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钉答。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡础芍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出数尿,到底是詐尸還是另有隱情仑性,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布砌创,位于F島的核電站虏缸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嫩实。R本人自食惡果不足惜刽辙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甲献。 院中可真熱鬧宰缤,春花似錦、人聲如沸晃洒。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)球及。三九已至氧骤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吃引,已是汗流浹背筹陵。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工刽锤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人朦佩。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓并思,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親语稠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宋彼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容