iOS觸摸事件處理

iOS觸摸事件處理

主要是記錄下iOS的界面觸摸事件處理機(jī)制,然后用一個(gè)實(shí)例來(lái)說(shuō)明下應(yīng)用場(chǎng)景.
一、處理機(jī)制
界面響應(yīng)消息機(jī)制分兩塊,(1)首先在視圖的層次結(jié)構(gòu)里找到能響應(yīng)消息的那個(gè)視圖。(2)然后在找到的視圖里處理消息。
【關(guān)鍵】(1)的過(guò)程是從父View到子View查找,而(2)是從找到的那個(gè)子View往父View回溯(不一定會(huì)往回傳遞消息)。

1.1、尋找響應(yīng)消息視圖的過(guò)程可以借用M了個(gè)J的一張圖來(lái)說(shuō)明。



處理原理如下:
? 當(dāng)用戶點(diǎn)擊屏幕時(shí),會(huì)產(chǎn)生一個(gè)觸摸事件,系統(tǒng)會(huì)將該事件加入到一個(gè)由UIApplication管理的事件隊(duì)列中
? UIApplication會(huì)從事件隊(duì)列中取出最前面的事件進(jìn)行分發(fā)以便處理,通常,先發(fā)送事件給應(yīng)用程序的主窗口(UIWindow)
? 主窗口會(huì)調(diào)用hitTest:withEvent:方法在視圖(UIView)層次結(jié)構(gòu)中找到一個(gè)最合適的UIView來(lái)處理觸摸事件
(hitTest:withEvent:其實(shí)是UIView的一個(gè)方法,UIWindow繼承自UIView,因此主窗口UIWindow也是屬于視圖的一種)
? hitTest:withEvent:方法大致處理流程是這樣的:
首先調(diào)用當(dāng)前視圖的pointInside:withEvent:方法判斷觸摸點(diǎn)是否在當(dāng)前視圖內(nèi):
? 若pointInside:withEvent:方法返回NO,說(shuō)明觸摸點(diǎn)不在當(dāng)前視圖內(nèi),則當(dāng)前視圖的hitTest:withEvent:返回nil
? 若pointInside:withEvent:方法返回YES,說(shuō)明觸摸點(diǎn)在當(dāng)前視圖內(nèi),則遍歷當(dāng)前視圖的所有子視圖(subviews),調(diào)用子視圖的hitTest:withEvent:方法重復(fù)前面的步驟,子視圖的遍歷順序是從top到bottom,即從subviews數(shù)組的末尾向前遍歷,直到有子視圖的hitTest:withEvent:方法返回非空對(duì)象或者全部子視圖遍歷完畢:
? 若第一次有子視圖的hitTest:withEvent:方法返回非空對(duì)象,則當(dāng)前視圖的hitTest:withEvent:方法就返回此對(duì)象,處理結(jié)束
? 若所有子視圖的hitTest:withEvent:方法都返回nil,則當(dāng)前視圖的hitTest:withEvent:方法返回當(dāng)前視圖自身(self)
? 最終,這個(gè)觸摸事件交給主窗口的hitTest:withEvent:方法返回的視圖對(duì)象去處理。
拿到這個(gè)UIView后,就調(diào)用該UIView的touches系列方法。
1.2、消息處理過(guò)程,在找到的那個(gè)視圖里處理,處理完后根據(jù)需要,利用響應(yīng)鏈nextResponder可將消息往下一個(gè)響應(yīng)者傳遞。
UIAppliactionDelegate <- UIWindow <- UIViewController <- UIView <- UIView
【關(guān)鍵】:要理解的有三點(diǎn):1、iOS判斷哪個(gè)界面能接受消息是從View層級(jí)結(jié)構(gòu)的父View向子View傳遞,即樹狀結(jié)構(gòu)的根節(jié)點(diǎn)向葉子節(jié)點(diǎn)遞歸傳遞。2、hitTest和pointInside成對(duì),且hitTest會(huì)調(diào)用pointInside。3、iOS的消息處理是,當(dāng)消息被人處理后默認(rèn)不再向父層傳遞。

二、應(yīng)用實(shí)例
【需求】是:界面如下,
Window
  -ViewA
    -ButtonA
    -ViewB
      -ButtonB
層次結(jié)構(gòu):ViewB完全蓋住了ButtonA,ButtonB在ViewB上,現(xiàn)在需要實(shí)現(xiàn)1)ButtonA和ButtonB都能響應(yīng)消息 2)ViewA也能收到ViewB所收到的touches消息 3)不讓ViewB(ButtonB)收到消息。
(首先解析下,默認(rèn)情況下,點(diǎn)擊了ButtonB的區(qū)域,iOS消息處理過(guò)程。
-ViewA
  -ButtonA
  -ViewB
    -ButtonB
當(dāng)點(diǎn)擊ButtonB區(qū)域后,處理過(guò)程:從ViewA開(kāi)始依次調(diào)用hitTest
pointInside的值依次為:
ViewA:NO;
ViewB:YES;
ButtonB:YES;
ButtonB的subViews:NO;
所以ButtonB的subViews的hitTest都返回nil,于是返回的處理對(duì)象是ButtonB自己。接下去開(kāi)始處理touches系列方法,這里是調(diào)用ButtonB綁定的方法。處理完后消息就停止,整個(gè)過(guò)程結(jié)束。)
【分析】:
實(shí)現(xiàn)的方式多種,這里將兩個(gè)需求拆解開(kāi)來(lái)實(shí)現(xiàn),因?yàn)閷?shí)現(xiàn)2就可以滿足1。
2.1、需求1的實(shí)現(xiàn),ViewB蓋住了ButtonA,所以默認(rèn)情況下ButtonA收不到消息,但是在消息機(jī)制里尋找消息響應(yīng)是從父View開(kāi)始,所以我們可以在ViewA的hitTest方法里做判斷,若touch point是在ButtonA上,則將ButtonA作為消息處理對(duì)象返回。
代碼如下:

pragma mark - hitTest

  • (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
    // 當(dāng)touch point是在_btn上,則hitTest返回_btn
    CGPoint btnPointInA = [_btn convertPoint:point fromView:self];
    if ([_btn pointInside:btnPointInA withEvent:event]) {
    return _btn;
    }

    // 否則,返回默認(rèn)處理
    return [super hitTest:point withEvent:event];

}

pragma mark - hitTest

  • (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
    // 當(dāng)touch point是在_btn上,則hitTest返回_btn
    CGPoint btnPointInA = [_btn convertPoint:point fromView:self];
    if ([_btn pointInside:btnPointInA withEvent:event]) {
    return _btn;
    }

    // 否則,返回默認(rèn)處理
    return [super hitTest:point withEvent:event];

}

pragma mark - hitTest

  • (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

{

// 當(dāng)touch point是在_btn上,則hitTest返回_btn

CGPoint btnPointInA = [_btn convertPoint:point fromView:
self
];

if
([_btn pointInside:btnPointInA withEvent:event]) {

return
_btn;

}

// 否則,返回默認(rèn)處理

return
[
super
hitTest:point withEvent:event];

}

這樣,當(dāng)觸碰點(diǎn)是在ButtonA上時(shí),則touch消息就被攔截在ViewA上,ViewB就收不到了。然后ButtonA就收到touch消息,會(huì)觸發(fā)onClick方法。
2.2、需求2的實(shí)現(xiàn),上面說(shuō)到響應(yīng)鏈,ViewB只要override掉touches系列的方法,然后在自己處理完后,將消息傳遞給下一個(gè)響應(yīng)者(即父View即ViewA)。
代碼如下:在ViewB代碼里

  • (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
    NSLog(@"B - touchesBeagan..");

    // 把事件傳遞下去給父View或包含他的ViewController
    [self.nextResponder touchesBegan:touches withEvent:event];
    }

  • (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
    {
    NSLog(@"B - touchesCancelled..");
    // 把事件傳遞下去給父View或包含他的ViewController
    [self.nextResponder touchesBegan:touches withEvent:event];
    }

  • (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
    NSLog(@"B - touchesEnded..");
    // 把事件傳遞下去給父View或包含他的ViewController
    [self.nextResponder touchesBegan:touches withEvent:event];
    }

  • (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    {
    NSLog(@"B - touchesMoved..");
    // 把事件傳遞下去給父View或包含他的ViewController
    [self.nextResponder touchesBegan:touches withEvent:event];

}

pragma mark - touches

  • (
    void
    )touchesBegan:(
    NSSet
    *)touches withEvent:(UIEvent *)event

{

NSLog
(@
"B - touchesBeagan.."
);

// 把事件傳遞下去給父View或包含他的ViewController

[
self
.nextResponder touchesBegan:touches withEvent:event];

}

  • (
    void
    )touchesCancelled:(
    NSSet
    *)touches withEvent:(UIEvent *)event

{

NSLog
(@
"B - touchesCancelled.."
);

// 把事件傳遞下去給父View或包含他的ViewController

[
self
.nextResponder touchesBegan:touches withEvent:event];

}

  • (
    void
    )touchesEnded:(
    NSSet
    *)touches withEvent:(UIEvent *)event

{

NSLog
(@
"B - touchesEnded.."
);

// 把事件傳遞下去給父View或包含他的ViewController

[
self
.nextResponder touchesBegan:touches withEvent:event];

}

  • (
    void
    )touchesMoved:(
    NSSet
    *)touches withEvent:(UIEvent *)event

{

NSLog
(@
"B - touchesMoved.."
);

// 把事件傳遞下去給父View或包含他的ViewController

[
self
.nextResponder touchesBegan:touches withEvent:event];

}

然后,在ViewA上就可以接收到touches消息,在ViewA上寫:
[+ View Code](file:///Applications/WizNote.app/Contents/Resources/files/editor/index.html#)

這樣就實(shí)現(xiàn)了向父View透?jìng)飨ⅰ?br> 2.3 、不讓ViewB收到消息,可以設(shè)置ViewB.UserInteractionEnable=NO;除了這樣還可以override掉ViewB的ponitInside,原理參考上面。
在ViewB上寫:
[+ View Code](file:///Applications/WizNote.app/Contents/Resources/files/editor/index.html#)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文來(lái)自:http://www.cnblogs.com/Quains/p/3369132.html 有時(shí)間我再來(lái)整...
    小如99閱讀 295評(píng)論 0 0
  • iOS中的事件 用戶與app交互的時(shí)候會(huì)產(chǎn)生各種各樣的的事件,iOS中事件分為三大類型:1)觸摸事件 ;2)加速計(jì)...
    jason_Yun閱讀 749評(píng)論 0 3
  • 主要是記錄下iOS的界面觸摸事件處理機(jī)制,然后用一個(gè)實(shí)例來(lái)說(shuō)明下應(yīng)用場(chǎng)景. 一、處理機(jī)制 界面響應(yīng)消息機(jī)制分兩塊,...
    醉葉惜秋閱讀 306評(píng)論 0 0
  • 本文來(lái)自:http://ios.jobbole.com/84081/ 前言: 按照時(shí)間順序,事件的生命周期是這樣的...
    HackerOnce閱讀 2,941評(píng)論 1 10
  • 簡(jiǎn)介 iOS 事件分為三大類 觸摸事件 加速器事件 遠(yuǎn)程控制事件 以下我們講解觸摸事件觸摸事件是我們平時(shí)遇到最多的...
    AKsoftware閱讀 22,852評(píng)論 23 72

友情鏈接更多精彩內(nèi)容