在iOS中不是所有對象都能處理事件,只有繼承了UIResponder的對象才能處理事件,我們稱之為"響應(yīng)者對象"
事件分類
- 觸摸事件
- 加速計(jì)事件
- 遠(yuǎn)程控制事件
觸摸事件
// 系統(tǒng)自動調(diào)用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 該方法會持續(xù)調(diào)用
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 手指離開屏幕時調(diào)用
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 事件中斷時時調(diào)用
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches NS_AVAILABLE_IOS(9_1);
加速計(jì)事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
遠(yuǎn)程控制事件
- (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);
UITouch
- 一個手指對應(yīng)一個UITouch對象
- UITouch的作用
- 保存跟手指相關(guān)的信心,比如觸摸的位置,事件,階段
- 當(dāng)手指移動時,系統(tǒng)會一直更新該UITouch對象,使之能夠一直保存該手指的觸摸位置
- 當(dāng)手指離開屏幕時,系統(tǒng)會自動銷毀該UITouch對象
- 獲取手指觸摸位置
UITouch *touch = [touches anyObject];
// 當(dāng)前觸摸的點(diǎn)
CGPoint currentP = [touch locationInView:self];
// 上一個觸摸的點(diǎn)
CGPoint previousP = [touch previousLocationInView:self];
事件的產(chǎn)生和傳遞
- 發(fā)生觸摸事件后,系統(tǒng)會將該事件加入到一個有UIApplication管理的事件隊(duì)列中
- UIApplication會從事件隊(duì)列中取出最前面的事件,并將事件分發(fā)下去以便處理.通常,先發(fā)送事件給應(yīng)用程序主窗口(keyWindow)
- 主窗口會在事件層次結(jié)構(gòu)中找到一個最合適的視圖來處理觸摸事件,這也是整個事件處理的第一步
- 找到合適的視圖控件之后,就會調(diào)用該視圖的touches方法來做具體的事件處理
事件傳遞的完整過程
- 先將事件對象由上往下傳遞,找到最合適的控件來處理這個事件
- 調(diào)用最合適控件的touches方法
- 如果調(diào)用了[super touche..]方法,就會將事件順著響應(yīng)者鏈條向上傳遞,傳遞給上一個響應(yīng)者
- 先去判斷當(dāng)前的view是不是控制器的view,如果是,那么它的上一個響應(yīng)者就是它的控制器
- 如果當(dāng)前的view不是控制器的view,那么它的上一個響應(yīng)者就是它的父控件
- 接著會調(diào)用上一個響應(yīng)者的touches方法
控件不能處理事件的原因
- 如果父控件不能處理事件,那么它的子控件也不能接收處理事件
- userInteractionEnabled = NO // 不接收用戶交互
- hidden = YES (當(dāng)一個控件隱藏的時候,它的子控件也會跟隨父控件一起隱藏)
- alpha = 0.0~0.01 (當(dāng)一個控件透明的時候,它的子控件也會跟隨父控件一起透明)
如何找到最適合的控件來處理事件
- 自己是否能接收觸摸事件
- 觸摸點(diǎn)是否在自己身上
- 從后往前遍歷子控件,重復(fù)以上兩個步驟
- 如果沒有符合條件的子控件,那么自己是最合適的
// 作用:去尋找最合適的View
// 什么時候調(diào)用:當(dāng)一個事件傳遞給當(dāng)前view就會調(diào)用該方法
// 返回的是誰,誰就是最適合的view
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
return nil;
}
hitTest方法的底層實(shí)現(xiàn)
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 判斷自己能否處理觸摸事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) {
return nil;
}
// 判斷點(diǎn)是否在自己身上
if (![self pointInside:point withEvent:event]) {
return nil;
}
// 從后往前遍歷子控件,重復(fù)前兩步操作
NSInteger count = self.subviews.count;
for (NSInteger i = count -1; i >= 0; i--) {
// 取出每一個view
UIView *childView = self.subviews[i];
// 轉(zhuǎn)換坐標(biāo)系
CGPoint childP = [self convertPoint:point toView:childView];
UIView *fitView = [childView hitTest:childP withEvent:event];
// 判斷是否找到合適的view
if (fitView) {
return fitView;
}
}
// 否則自己就是最合適的view
return self;
}
// 判斷當(dāng)前的點(diǎn)在不在調(diào)用它的view上
// 調(diào)用時間: 在hitTest方法中調(diào)用
// point點(diǎn)必須要跟它調(diào)用者在同一個坐標(biāo)系里面
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return YES;
}
UIGestureRecognizer手勢識別
- UIGestureRecognizer它是一個抽象類
- UITapGestureRecognizer(敲擊)
- UIPinchGestureRecognizer(捏合)
// 獲取縮放比例
CGFloat scale = tap.scale;
// 復(fù)位操作
[pinch setScale:1];
- UISwipeGestureRecognizer(輕掃)
- 分方向direction
UISwipeGestureRecognizerDirectionRight
UISwipeGestureRecognizerDirectionLeft
UISwipeGestureRecognizerDirectionUp
UISwipeGestureRecognizerDirectionDown
- UIPanGestureRecognizer(拖拽)
// 相對于最原始的偏移量
CGPoint point = [pan translationInView:view];
// 復(fù)位操作
[pan setTranslation:CGPointZero inView:view];
- UILongPressGestureRecognizer(長按)
- 長按移動時,會持續(xù)調(diào)用目標(biāo)方法
- 長按手勢分狀態(tài):state
UIGestureRecognizerStatePossible
UIGestureRecognizerStateBegan
UIGestureRecognizerStateChanged
UIGestureRecognizerStateEnded
UIGestureRecognizerStateCancelled
UIGestureRecognizerStateFailed
UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
- UIRotationGestureRecognizer(旋轉(zhuǎn))
// 獲取縮放角度
CGFloat rotation = tap.rotation;
// 復(fù)位操作
[tap setRotation:0];
- 多個手勢操作
// 該代理方法用來判斷是否同時支持多個手勢操作
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
添加手勢方法
UIView *view = [[UIView alloc] init];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];
// 設(shè)置代理
tap.delegate = self;
[view addGestureRecognizer:tap];
// 該代理方法可以用來設(shè)置可以點(diǎn)擊的區(qū)域
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}