iOS 事件傳遞以及響應(yīng)

一帆竹、事件傳遞



  • 1绕娘、事件傳遞

2尋找最合適的View.png

驗(yàn)證一下是不是這個(gè)流程!

  • 1栽连、建立一個(gè)工程险领!搭建如下界面。


    圖片

在修改View背景

blue

  • 2秒紧、建立一個(gè)自己的Window绢陌,并且在AppDelegate中替換為自己Window
//使用自己的Window
    self.window = [[PQWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    //加載界面
    UIStoryboard * board = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController * viewController = [board instantiateInitialViewController];
    self.window.rootViewController = viewController;
    [self.window makeKeyAndVisible];
  • 3、重寫touchesbegan...方法:在包括Window和其他的View熔恢、VC
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    NSLog(@"%s",__func__);
}
  • 4脐湾、分別依次點(diǎn)擊
  • blue view
2016-08-27 09:41:06.116 PQHitTest[1647:58543] -[PQWindow touchesBegan:withEvent:]
2016-08-27 09:41:06.116 PQHitTest[1647:58543] -[ViewController touchesBegan:withEvent:]
  • green view
2016-08-27 09:42:22.891 PQHitTest[1647:58543] -[PQWindow touchesBegan:withEvent:]
2016-08-27 09:42:22.891 PQHitTest[1647:58543] -[ViewController touchesBegan:withEvent:]
2016-08-27 09:42:22.891 PQHitTest[1647:58543] -[GreenView touchesBegan:withEvent:]
  • pink view
2016-08-27 09:43:08.274 PQHitTest[1647:58543] -[PQWindow touchesBegan:withEvent:]
2016-08-27 09:43:08.275 PQHitTest[1647:58543] -[ViewController touchesBegan:withEvent:]
2016-08-27 09:43:08.275 PQHitTest[1647:58543] -[PinkView touchesBegan:withEvent:]
  • yellow view
2016-08-27 09:43:47.010 PQHitTest[1647:58543] -[PQWindow touchesBegan:withEvent:]
2016-08-27 09:43:47.010 PQHitTest[1647:58543] -[ViewController touchesBegan:withEvent:]
2016-08-27 09:43:47.011 PQHitTest[1647:58543] -[PinkView touchesBegan:withEvent:]
2016-08-27 09:43:47.011 PQHitTest[1647:58543] -[YellowView touchesBegan:withEvent:]
  • brown view
2016-08-27 09:44:37.802 PQHitTest[1647:58543] -[PQWindow touchesBegan:withEvent:]
2016-08-27 09:44:37.802 PQHitTest[1647:58543] -[ViewController touchesBegan:withEvent:]
2016-08-27 09:44:37.802 PQHitTest[1647:58543] -[PinkView touchesBegan:withEvent:]
2016-08-27 09:44:37.802 PQHitTest[1647:58543] -[BrownView touchesBegan:withEvent:]
  • black view
2016-08-27 09:44:49.026 PQHitTest[1647:58543] -[PQWindow touchesBegan:withEvent:]
2016-08-27 09:44:49.026 PQHitTest[1647:58543] -[ViewController touchesBegan:withEvent:]
2016-08-27 09:44:49.026 PQHitTest[1647:58543] -[PinkView touchesBegan:withEvent:]
2016-08-27 09:44:49.026 PQHitTest[1647:58543] -[BrownView touchesBegan:withEvent:]
2016-08-27 09:44:49.026 PQHitTest[1647:58543] -[BlackView touchesBegan:withEvent:]
  • orange view
2016-08-27 09:45:02.546 PQHitTest[1647:58543] -[PQWindow touchesBegan:withEvent:]
2016-08-27 09:45:02.546 PQHitTest[1647:58543] -[ViewController touchesBegan:withEvent:]
2016-08-27 09:45:02.546 PQHitTest[1647:58543] -[OrangeView touchesBegan:withEvent:]
  • 2、如何尋找最合適的View

  • 1绩聘、檢查自己是能否接受觸摸事件
  • 2沥割、檢測(cè)觸摸點(diǎn)是否在自己身上
  • 3耗啦、從后往前遍歷子控件凿菩,重復(fù)1 2
  • 4机杜、如果沒(méi)有符合的子控件,那么自己就是合適的View

eg:點(diǎn)擊green View

window → blue view → orange view → pink view → green view

  • 1衅谷、首先會(huì)響應(yīng)window的hitTest方法

  • 2椒拗、在響應(yīng)blueView的hitTest方法,從后往前遍歷获黔,先找到blue view.subviews的最后一個(gè)View蚀苛,也就是orange view

  • 3、判斷點(diǎn)是不是在orange view上玷氏,不在的話返回nil堵未,并且繼續(xù)從blue view.subviews繼續(xù)從后往前遍歷

  • 4、判斷點(diǎn)是不是在pink view上盏触,不在的話返回nil渗蟹,并且繼續(xù)從blue view.subviews繼續(xù)從后往前遍歷

  • 5、當(dāng)遍歷的到green view時(shí)赞辩,發(fā)現(xiàn)點(diǎn)在green view上雌芽,然后繼續(xù)遍歷 green view.subviews,發(fā)現(xiàn)并沒(méi)有子控件辨嗽。

  • 6世落、這個(gè)時(shí)候就返回green view,從而使得 green view變成了最合適的View

使用程序驗(yàn)證:把touchesBegan...方法全部注釋掉糟需,并且在blue / green / pink / orange view以及window中添加下面方法

2016-08-27 10:36:23.747 PQHitTest[2603:89650] -[PQWindow hitTest:withEvent:]
2016-08-27 10:36:23.747 PQHitTest[2603:89650] -[OrangeView hitTest:withEvent:]
2016-08-27 10:36:23.747 PQHitTest[2603:89650] -[PinkView hitTest:withEvent:]
2016-08-27 10:36:23.748 PQHitTest[2603:89650] -[GreenView hitTest:withEvent:]

通過(guò)輸出我們可以看到屉佳,系統(tǒng)查找過(guò)程確實(shí)和我們預(yù)想的一樣。



eg:點(diǎn)擊black View:把所有的View都添加hitTest方法

window → blue view → orange view → pink view → brown view → black view


2016-08-27 10:42:56.865 PQHitTest[2818:95834] -[PQWindow hitTest:withEvent:]
2016-08-27 10:42:56.865 PQHitTest[2818:95834] -[OrangeView hitTest:withEvent:]
2016-08-27 10:42:56.865 PQHitTest[2818:95834] -[PinkView hitTest:withEvent:]
2016-08-27 10:42:56.865 PQHitTest[2818:95834] -[BrownView hitTest:withEvent:]
2016-08-27 10:42:56.865 PQHitTest[2818:95834] -[BlackView hitTest:withEvent:




二洲押、事件響應(yīng)



1事件響應(yīng).png

發(fā)生觸摸事件后武花,系統(tǒng)會(huì)自動(dòng)將該事件加入到一個(gè)有UIApplication管理的事件隊(duì)列中,UIApplication會(huì)從事件隊(duì)列中取出最前面的事件诅诱,并將事件分發(fā)下去以便處理髓堪,通常先發(fā)送事件給應(yīng)用程序的主窗口。
在主窗口的視圖中會(huì)找到一個(gè)最合適的視圖來(lái)處理觸摸事件娘荡,這也是整個(gè)事件處理過(guò)程的第一步干旁。
當(dāng)找到后,則會(huì)調(diào)用touches中的:

  • touchesBegan...
  • touchesMoved...
  • touchesEnded...

1炮沐、先把所有的hitTest方法給注釋掉争群!

然后開(kāi)啟VC中和WIndows中的touchesBegan方法,運(yùn)行程序

2016-08-27 10:49:32.998 PQHitTest[2972:100633] -[PQWindow touchesBegan:withEvent:]
2016-08-27 10:49:32.999 PQHitTest[2972:100633] -[ViewController touchesBegan:withEvent:]

如果沒(méi)有把其他的View的touchesbegan注釋掉大年,則會(huì)依次找到View的touchesBegan方法换薄。




三玉雾、練習(xí)



1、不管點(diǎn)擊那個(gè)View轻要,只響應(yīng)blue view的touchesBegan...

解析:

現(xiàn)在我們知道hitTest方法就是用來(lái)查找最合適的View的复旬,并且需要一個(gè)返回值,如果返回nil:表示不是合適的View冲泥,返回不為空:表示是合適的View驹碍。
通過(guò)這個(gè)一個(gè)特性我們可以把BaseView中的hitTest方法中直接返回nil

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    return nil;
}




2、不管點(diǎn)擊那個(gè)View凡恍,只響應(yīng)green view的touchesBegan...

解析:

hitTest方法是從后往前遍歷子控件志秃,所以我們把pink/orange view中的hitTest方法返回nil,把green view中的hitTest方法返回self來(lái)達(dá)到效果,這里要記得把BaseView中的hitTest方法注釋或者super,把VC中的touchesBegan..注釋掉,打開(kāi)green view中的touchesBegan...

// pink and orange
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    return nil;
}
//green 
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    return self;
}



打印結(jié)果:

2016-08-27 11:09:59.757 PQHitTest[3437:115811] -[GreenView touchesBegan:withEvent:]
2016-08-27 11:10:00.052 PQHitTest[3437:115811] -[GreenView touchesBegan:withEvent:]
2016-08-27 11:10:00.268 PQHitTest[3437:115811] -[GreenView touchesBegan:withEvent:]
2016-08-27 11:10:00.444 PQHitTest[3437:115811] -[GreenView touchesBegan:withEvent:]
2016-08-27 11:10:00.660 PQHitTest[3437:115811] -[GreenView touchesBegan:withEvent:]
2016-08-27 11:10:00.876 PQHitTest[3437:115811] -[GreenView touchesBegan:withEvent:]


3嚼酝、添加一個(gè)導(dǎo)航欄浮还,然后在工程中新建一個(gè)VC,搭建如下界面

搭建界面2



然后

搭建界面1

要求:點(diǎn)擊紅色按鈕是響應(yīng)紅色按鈕點(diǎn)擊事件

  • 3.1在然后創(chuàng)建一個(gè)button class闽巩、一個(gè)View class
  • 3.2并且連接好
  • 3.3添加touchesBegan方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    NSLog(@"%s",__func__);
}
  • 3.4運(yùn)行后發(fā)現(xiàn)只要是點(diǎn)擊橙色View钧舌,哪怕點(diǎn)同時(shí)也在按鈕上,并不會(huì)觸發(fā)按鈕事件又官。
如何達(dá)到這一效果延刘?

1、先明白為什么不會(huì)觸發(fā)按鈕事件:

因?yàn)樵趯ふ易詈线m的View的時(shí)候是從后往前遍歷
Application → window → VC.view → 橙色View
這個(gè)時(shí)候不會(huì)繼續(xù)去尋找了六敬,因?yàn)槌壬呀?jīng)滿足了條件碘赖。

所以我要在橙色的hitTest方法中動(dòng)手腳。



2外构、如何動(dòng)手腳

思想:當(dāng)你在查找最合適的View的時(shí)候是根據(jù)point點(diǎn)去判斷的普泡,我只需要判斷當(dāng)你這個(gè)點(diǎn),恰好也在我的Button上的時(shí)候审编,我就返回button撼班,而不是返回橙色View
需要用到的方法:

  • convertPoint:toView: 把點(diǎn)轉(zhuǎn)化為View中的點(diǎn)
  • pointInside:withEvent: 用與判斷點(diǎn)在不在VIew上
#import "PQViewTwo.h"
#import "PQButtonTwo.h"
@interface PQViewTwo ()

@property (nonatomic,weak) IBOutlet PQButtonTwo *button;

@end

@implementation PQViewTwo

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    CGPoint p = [self convertPoint:point toView:self.button];
        
    if ([self pointInside:p withEvent:event]) {
        return self.button;
    }
    return [super hitTest:point withEvent:event];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    NSLog(@"%s",__func__);
}

@end


4、添加一個(gè)導(dǎo)航欄垒酬,然后在工程中新建一個(gè)VC砰嘁,搭建如下界面

界面效果



然后新建一個(gè)類,和button關(guān)聯(lián)

關(guān)聯(lián)后


最終效果:
最終效果


1勘究、重寫touchesBegan...touchesMoved....方法完成拖動(dòng)效果

#import "ThreeButton.h"



@implementation ThreeButton{
    CGPoint _startP;
}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    UITouch * touce = [touches anyObject];
    _startP = [touce locationInView:self];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    UITouch * touce = [touches anyObject];
    CGPoint curP = [touce locationInView:self];
    
    CGFloat x = self.frame.origin.x + curP.x - _startP.x;
    CGFloat y = self.frame.origin.y + curP.y - _startP.y;
    
    self.frame = CGRectMake(x, y, self.frame.size.width, self.frame.size.height);
}


@end


2矮湘、添加一個(gè)超出button大小范圍的View

- (void)awakeFromNib{
    UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(80, -200, 200, 200);
    [button setImage:[UIImage imageNamed:@"222"] forState:UIControlStateNormal];
    [button setImage:[UIImage imageNamed:@"bc"] forState:UIControlStateHighlighted];
    [self addSubview:button];
}

3、此時(shí)運(yùn)行程序如下:

效果圖1

鼠標(biāo)點(diǎn)擊無(wú)效:并沒(méi)有切換圖片


log打印如下

2016-08-27 14:30:58.386 PQHitTest[6501:209375] -[ThreeViewController touchesBegan:withEvent:]
2016-08-27 14:30:58.809 PQHitTest[6501:209375] -[ThreeViewController touchesBegan:withEvent:]
2016-08-27 14:30:59.050 PQHitTest[6501:209375] -[ThreeViewController touchesBegan:withEvent:]
2016-08-27 14:30:59.282 PQHitTest[6501:209375] -[ThreeViewController touchesBegan:withEvent:]
2016-08-27 14:30:59.521 PQHitTest[6501:209375] -[ThreeViewController touchesBegan:withEvent:]
2016-08-27 14:30:59.721 PQHitTest[6501:209375] -[ThreeViewController touchesBegan:withEvent:]
2016-08-27 14:30:59.906 PQHitTest[6501:209375] -[ThreeViewController touchesBegan:withEvent:]
2016-08-27 14:31:00.154 PQHitTest[6501:209375] -[ThreeViewController touchesBegan:withEvent:]
2016-08-27 14:31:00.377 PQHitTest[6501:209375] -[ThreeViewController touchesBegan:withEvent:]


4口糕、修改hitTest方法

  • 1缅阳、先把點(diǎn)轉(zhuǎn)化
  • 2、判斷點(diǎn)在不在圖片按鈕上
  • 3景描、做出對(duì)應(yīng)處理
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    CGPoint isSub = [self convertPoint:point toView:self.subviews[0]];
    if ([self.subviews[0] pointInside:isSub withEvent:event]) {
        return self.subviews[0];
    }else {
        return [super hitTest:point withEvent:event];
    }
}


5十办、最終效果

最終效果

四秀撇、hitTest實(shí)現(xiàn)原理:

  • 1、檢查自己是能否接受觸摸事件
  • 2向族、檢測(cè)觸摸點(diǎn)是否在自己身上
  • 3呵燕、從后往前遍歷子控件,重復(fù)1 2
  • 4炸枣、如果沒(méi)有符合的子控件虏等,那么自己就是合適的View

實(shí)現(xiàn)代碼

重寫B(tài)aseView的hitTest方法弄唧,如果能準(zhǔn)確的找到子控件(最合適的View)适肠,那么久是正確的代碼
//實(shí)現(xiàn)原理
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
//   1、檢查自己是能否接受觸摸事件
    //不接受事件或者隱藏或者透明候引,都返回nil侯养,不是最合適的View
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha == 0) {
        return nil;
    }
//   2、檢測(cè)觸摸點(diǎn)是否在自己身上
    //如果點(diǎn)不在控件上澄干,返回nil
    if (![self pointInside:point withEvent:event]) {
        return nil;
    }
//   3逛揩、從后往前遍歷子控件,重復(fù)1 2
    NSInteger count = self.subviews.count;
    for (NSInteger i = count - 1; i >= 0; i--) {
        UIView * view = self.subviews[i];
        CGPoint viewP = [self convertPoint:point toView:view];
        
        if ([view hitTest:viewP withEvent:event]) {
            NSLog(@"%@",view);
            return view;
        }
    }
    
//   4麸俘、如果沒(méi)有符合的子控件辩稽,那么自己就是合適的View
    NSLog(@"%@",self);
    return self;
    
}

demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市从媚,隨后出現(xiàn)的幾起案子逞泄,更是在濱河造成了極大的恐慌,老刑警劉巖拜效,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喷众,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡紧憾,警方通過(guò)查閱死者的電腦和手機(jī)到千,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赴穗,“玉大人憔四,你說(shuō)我怎么就攤上這事“忝迹” “怎么了了赵?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)煤篙。 經(jīng)常有香客問(wèn)我斟览,道長(zhǎng),這世上最難降的妖魔是什么辑奈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任苛茂,我火速辦了婚禮已烤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妓羊。我一直安慰自己胯究,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布躁绸。 她就那樣靜靜地躺著裕循,像睡著了一般。 火紅的嫁衣襯著肌膚如雪净刮。 梳的紋絲不亂的頭發(fā)上剥哑,一...
    開(kāi)封第一講書(shū)人閱讀 51,245評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音淹父,去河邊找鬼株婴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛暑认,可吹牛的內(nèi)容都是我干的困介。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蘸际,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼座哩!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起粮彤,我...
    開(kāi)封第一講書(shū)人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤根穷,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后驾诈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體缠诅,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年乍迄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了管引。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡闯两,死狀恐怖褥伴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情漾狼,我是刑警寧澤重慢,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站逊躁,受9級(jí)特大地震影響似踱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一核芽、第九天 我趴在偏房一處隱蔽的房頂上張望囚戚。 院中可真熱鬧,春花似錦轧简、人聲如沸驰坊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拳芙。三九已至,卻和暖如春皮璧,著一層夾襖步出監(jiān)牢的瞬間舟扎,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工恶导, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浆竭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓惨寿,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親删窒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子裂垦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

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