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