天天看點

IOS事件響應控制

    以前遇到一個項目,一個uiimageview對象上面有一個uibutton對象,然而項目的需求需要在點選 button的同時,uiimageview也接收到點選事件,在不使用代理和通知方法的前提下,通過事件響應鍊的原理,我們也可以很便捷的解決這個問題。

    在處理這個問題之前,我們應該先清楚ios的事件響應機制到底是個什麼樣的原理。

首先,這個事件響應的機制是分為兩個部分的。

1、先在視圖層級關系中找到應該響應事件的那個視圖。

這一步是什麼意思,其實很簡單,就是找到你所觸摸點對應的那個最上層的視圖,它的工作原理是這樣的:當使用者發出事件後,會産生一個觸摸事件,系統會将該事件加入到一個由uiapplication管理的事件隊列中,uiapplication會取出隊列中最前面的事件,發消息給uiwindow,然後uiwindow會對其所有子視圖調用hittest:withevent:這個方法,這個方法會傳回一個uiview的對象,這個方法在執行的時候,它會調用目前視圖的pointinside:withevent:這個方法,如果觸摸事件在目前視圖範圍内,pointinside:withevent:會傳回yes,否則會傳回no;如果傳回yes,則會周遊目前視圖的所有子視圖,統統發送hittest:withevent:這個消息,如果傳回no,則hittest:withevent:方法傳回nil;

上面說起來有些繞,其實就是:hittest:withevent:方法會一層一層的向上找,若最上層響應的子視圖pointinside:withevent:傳回yes,則傳回此子視圖,如果所有的都傳回nil,則傳回目前視圖本身self。

例如:我們建兩個檔案,一個繼承于uibutton,一個繼承于uiimageview,我們在uiimageview裡的代碼如下:

<a href="http://my.oschina.net/u/2340880/blog/396161#">?</a>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<code>#import "myimageview.h"</code>

<code>@implementation myimageview</code>

<code>- (instancetype)initwithframe:(cgrect)frame</code>

<code>{</code>

<code>    </code><code>self = [super initwithframe:frame];</code>

<code>    </code><code>if</code> <code>(self) {</code>

<code>        </code><code>self.backgroundcolor=[uicolor redcolor];</code>

<code>    </code><code>}</code>

<code>    </code><code>return</code> <code>self;</code>

<code>}</code>

<code>//在這裡,我們重寫了這個方法,讓它直接傳回自身,而不是繼續向下尋找應該響應事件的視圖</code>

<code>-(uiview *)hittest:(cgpoint)point withevent:(uievent *)event{</code>

<code>-(</code><code>void</code><code>)touchesbegan:(nsset *)touches withevent:(uievent *)event{</code>

<code>    </code><code>nslog(@</code><code>"點選了image"</code><code>);</code>

然後将他們建立在一個view上:

<code>- (</code><code>void</code><code>)viewdidload {</code>

<code>    </code><code>[super viewdidload];</code>

<code>    </code><code>myimageview * image = [[myimageview alloc]initwithframe:cgrectmake(60, 80, 200, 200)];</code>

<code>    </code><code>mybutton * btn =[uibutton buttonwithtype:uibuttontypesystem];</code>

<code>    </code><code>btn.frame=cgrectmake(20, 20, 40, 40);</code>

<code>    </code><code>[btn settitle:@</code><code>"button"</code> <code>forstate:uicontrolstatenormal];</code>

<code>    </code><code>[image addsubview:btn];</code>

<code>    </code><code>[self.view addsubview:image];</code>

<code>    </code><code>// do any additional setup after loading the view, typically from a nib.</code>

IOS事件響應控制

可以證明,在事件視圖尋找中,uiimageview我們重寫hittest:withevent:方法後,切斷了尋找鍊,如果我們這個做:

<code>    </code><code>return</code> <code>nil;</code>

你會發現,uiimageview也不再接收事件。

2、尋找到應該響應的視圖後,會進行消息處理,這個處理的方式是通過消息處理鍊來做的。如果它自身不能處理消息,會通過nextresponder将消息傳遞給下一個處理者,預設隻要有一個view将消息處理了,這個消息處理傳遞鍊将不再傳遞。

現在,我們把剛才uiimageview裡重寫的hittest:withevent:方法注釋掉,給btn添加一個點選方法,同時将使用者互動關閉:

<code>    </code><code>image.userinteractionenabled=yes;</code>

<code>    </code> 

<code>    </code><code>[btn addtarget:self action:@selector(click) forcontrolevents:uicontroleventtouchupinside];</code>

<code>     </code><code>btn.userinteractionenabled=no;</code>

<code>-(</code><code>void</code><code>)click{</code>

<code>    </code><code>nslog(@</code><code>"btn被點選了"</code><code>);</code>

這樣,我們的uiimageview又可以響應事件了,原因是事件處理傳遞鍊向下傳遞了。

現在,在回到我們剛開始的問題,如何讓btn響應的同時imageview也響應,我們這樣做:

20

21

22

<code>    </code><code>[btn addtarget:self action:@selector(click:) forcontrolevents:uicontroleventtouchupinside];</code>

<code>    </code><code>btn.userinteractionenabled=no;</code>

<code>-(</code><code>void</code><code>)click:(uibutton *)btn{</code>

<code>    </code><code>//響應鍊繼續傳遞</code>

<code>    </code><code>[btn.nextresponder touchesbegan:nil withevent:nil];</code>

結果如下:

IOS事件響應控制

雖然最終,我們完成了這個需求,可是我建議你最好不要這麼幹,因為這樣的邏輯是違背現實生活中人們的行為認知的,更重要的是,我們的項目最後也确實改掉了這樣的邏輯~~~

繼續閱讀