以前遇到一個項目,一個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裡的代碼如下:
#import "MyImageView.h"
@implementation MyImageView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor=[UIColor redColor];
}
return self;
}
//在這裡,我們重寫了這個方法,讓它直接傳回自身,而不是繼續向下尋找應該響應事件的視圖
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(@"點選了Image");
然後将他們建立在一個View上:
- (void)viewDidLoad {
[super viewDidLoad];
MyImageView * image = [[MyImageView alloc]initWithFrame:CGRectMake(60, 80, 200, 200)];
MyButton * btn =[UIButton buttonWithType:UIButtonTypeSystem];
btn.frame=CGRectMake(20, 20, 40, 40);
[btn setTitle:@"button" forState:UIControlStateNormal];
[image addSubview:btn];
[self.view addSubview:image];
// Do any additional setup after loading the view, typically from a nib.
我們運作,點選這個Btn,會列印如下的資訊:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SMlVTO1YDO1IGOkJGZ1gjY3UTY0cDMlNzN2kDOyIWO38CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
可以證明,在事件視圖尋找中,UIImageView我們重寫hitTest:withEvent:方法後,切斷了尋找鍊,如果我們這個做:
return nil;
你會發現,UIImageView也不再接收事件。