事件处理详解
一:事件处理
事件处理常见属性:
事件类型
- @property(nonatomic,readonly) UIEventType type;
- @property(nonatomic,readonly) UIEventSubtype subtype;
事件产生的时间
- @property(nonatomic,readonly) NSTimeInterval timestamp;
事件传递
- hitTest:withEvent:
SWIFT
func hitTest(_ point: CGPoint, withEvent event: UIEvent?) -> UIView?
OBJECTIVE-C
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- pointInside:withEvent:
SWIFT
1 func pointInside(_ point: CGPoint, 2 withEvent event: UIEvent?) -> a href="" Bool /a
OBJECTIVE-C
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
事件传递方法的简单实用:
事件传递的时候调用
- 什么时候调用:当事件传递给控件的时候,就会调用控件的这个方法,去寻找最合适的view
- 作用:寻找最合适的view
// point:当前的触摸点,point这个点的坐标系就是方法调用者
1 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 2 3 { 4 5 // 调用系统的做法去寻找最合适的view,返回最合适的view 6 7 UIView *fitView = [super hitTest:point withEvent:event]; 8 9 10 11 // NSLog(@"fitView--%@",fitView); 12 13 14 15 16 17 return fitView; 18 19 }
// 作用:判断当前这个点在不在方法调用者(控件)上
1 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 2 3 { 4 5 return YES; 6 7 }
事件传递底层的实现:
如果父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件(掌握)
- 如何找到最合适的控件来处理事件?
- 自己是否能接收触摸事件?
- 触摸点是否在自己身上?
- 从后往前遍历子控件数组,重复前面的两个步骤
- 如果没有符合条件的子控件,那么就自己最适合处理
// 点击黄色视图 -》 事件 -》 UIApplication -> UIWindow
// 因为所有的视图类都是继承BaseView
1 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 2 3 { 4 5 NSLog(@"%@--hitTest",[self class]); 6 7 // return [super hitTest:point withEvent:event]; 8 9 10 11 12 13 // 1.判断当前控件能否接收事件 14 15 if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil; 16 17 18 19 // 2. 判断点在不在当前控件 20 21 if ([self pointInside:point withEvent:event] == NO) return nil; 22 23 24 25 // 3.从后往前遍历自己的子控件 26 27 NSInteger count = self.subviews.count; 28 29 30 31 for (NSInteger i = count - 1; i >= 0; i--) { 32 33 UIView *childView = self.subviews[i]; 34 35 36 37 // 把当前控件上的坐标系转换成子控件上的坐标系 38 39 CGPoint childP = [self convertPoint:point toView:childView]; 40 41 42 43 UIView *fitView = [childView hitTest:childP withEvent:event]; 44 45 46 47 48 49 if (fitView) { // 寻找到最合适的view 50 51 return fitView; 52 53 } 54 55 56 57 58 59 } 60 61 62 63 // 循环结束,表示没有比自己更合适的view 64 65 return self; 66 67 68 69 }
关于事件传递的底层原理和方法的实现:
事件响应:(响应者链)
响应者链条:是由多个响应者对象连接起来的链条
作用:能很清楚的看见每个响应者之间的联系,并且可以让一个事件多个对象处理。
响应者对象:能处理事件的对象
事件传递的完整过程
- 1> 先将事件对象由上往下传递(由父控件传递给子控件),找到最合适的控件来处理这个事件。
- 2> 调用最合适控件的touches….方法
- 3> 如果调用了[super touches….];就会将事件顺着响应者链条往上传递,传递给上一个响应者
- 4> 接着就会调用上一个响应者的touches….方法
重点:如何判断上一个响应者
- 1> 如果当前这个view是控制器的view,那么控制器就是上一个响应者
- 2> 如果当前这个view不是控制器的view,那么父控件就是上一个响应者
二:触摸事件
各个方法的解释
UITouch相关属性:
触摸产生时所处的窗口
- @property(nonatomic,readonly,retain) UIWindow *window;
触摸产生时所处的视图
- @property(nonatomic,readonly,retain) UIView *view;
短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击
- @property(nonatomic,readonly) NSUInteger tapCount;
记录了触摸事件产生或变化时的时间,单位是秒
- @property(nonatomic,readonly) NSTimeInterval timestamp;
当前触摸事件所处的状态
- @property(nonatomic,readonly) UITouchPhase phase;
方法:
- - (CGPoint)locationInView:(UIView *)view;
- 返回值表示触摸在view上的位置
- 这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0))
- 调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置
- - (CGPoint)previousLocationInView:(UIView *)view;
该方法记录了前一个触摸点的位置
UIView不接收触摸事件的三种情况
不接收用户交互
- userInteractionEnabled = NO
隐藏
- hidden = YES
透明
- alpha = 0.0 ~ 0.01
提示:UIImageView的userInteractionEnabled默认就是NO,因此UIImageView以及它的子控件默认是不能接收触摸事件的
实现UIView的拖动:
OC&Swift版
1 -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 2 3 { 4 5 UITouch *touch = [touches anyObject]; 6 7 8 9 CGPoint curP = [touch locationInView:self]; 10 11 12 13 CGPoint preP = [touch previousLocationInView:self]; 14 15 16 17 CGFloat offsetX = preP.x - curP.x; 18 19 CGFloat offsetY = preP.y - curP.y; 20 21 self.transform = CGAffineTransformTranslate(self.transform, -offsetX, -offsetY); 22 23 } }
--------------------swift-----------------------
1 2 3 4 5 6 7 override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) { 8 9 10 11 var touch:UITouch = touches(AnyObject) 12 13 14 15 var preP:CGPoint = touch.locationInView(self) 16 17 var curP:CGPoint = touch.previousLocationInView(self) 18 19 20 21 var ofX = curP.x - preP.x 22 23 var ofY = curP.y - preP.y 24 25 26 27 self.transform = CGAffineTransformTranslate(self.transform, ofX, ofY) 28 29 } 30 31
触摸事件简单介绍:
// 当手指开始触摸view
// NSArray,字典,NSSet(无序)
1 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 2 3 { 4 5 NSLog(@"%ld", touches.count); 6 7 NSLog(@"%s",__func__); 8 9 }
// 当手指在view上移动的时候
1 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 2 3 { 4 5 NSLog(@"%s",__func__); 6 7 }
// 当手指离开这个view的时候
1 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ 2 3 NSLog(@"%s",__func__); 4 5 }
// 当触摸事件被打断的时候调用(电话打入)
1 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 2 3 { 4 5 6 7 }
三:gestureReginazation
各个方法的解释
UIView拖动
OC&Swift版
1 2 3 4 5 @interface ViewController () 6 7 8 9 @property (weak, nonatomic) IBOutlet iCocosView *dragView; 10 11 12 13 @end 14 15 16 17 @implementation ViewController 18 19 20 21 - (void)viewDidLoad { 22 23 [super viewDidLoad]; 24 25 26 27 // self.view.transform = CGAffineTransformTranslate(self.view.transform, 100, 100); 28 29 30 31 /** 32 33 为对应的View创建并且添加手势和手势监听方法 34 35 */ 36 37 UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(Drag:)]; 38 39 [self.dragView addGestureRecognizer:pan]; 40 41 42 43 }
/**
* 实现手势监听方法
*/
1 -(void)Drag:(UIPanGestureRecognizer *)pan 2 3 { 4 5 //获取盘的位置 6 7 CGPoint p = [pan translationInView:pan.view]; 8 9 10 11 /** 12 13 * 使用三步法实现赋值 14 15 */ 16 17 //根据pan的位置获取pan的中心点 18 19 CGPoint center = pan.view.center; 20 21 center.x += p.x; 22 23 center.y += p.y; 24 25 pan.view.center = center; 26 27 28 29 //根据pan的移动设置对应View的移动 30 31 [pan setTranslation:CGPointZero inView:pan.view]; 32 33 }
------------------swift-----------------------
1 @IBOutlet weak var dragViews: iCocos! 2 3 4 5 override func viewDidLoad() { 6 7 super.viewDidLoad() 8 9 10 11 var pan:UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: "Drag:") 12 13 self.dragViews.addGestureRecognizer(pan) 14 15 } 16 17 18 19 func Drag(pan:UIPanGestureRecognizer) 20 21 { 22 23 var P:CGPoint = pan.translationInView(pan.view!) 24 25 var center:CGPoint = pan.view!.center 26 27 center.x += P.x 28 29 center.y += P.y 30 31 pan.view?.center = center 32 33 34 35 pan.setTranslation(CGPointZero, inView: pan.view) 36 37
------------------------------------------
手势方法简单实用:UIGestureRecognizerDelegate
#pragma mark - 手势代理方法
// 是否允许开始触发手势
1 //- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 2 3 //{ 4 5 // return NO; 6 7 //}
// 是否允许同时支持多个手势,默认是不支持多个手势
// 返回yes表示支持多个手势
1 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer 2 3 { 4 5 return YES; 6 7 }
// 是否允许接收手指的触摸点
1 //- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{ 2 3 // // 获取当前的触摸点 4 5 // CGPoint curP = [touch locationInView:self.imageView]; 6 7 // 8 9 // if (curP.x < self.imageView.bounds.size.width * 0.5) { 10 11 // return NO; 12 13 // }else{ 14 15 // return YES; 16 17 // } 18 19 //} 20 21
#pragma mark - 点按手势
1 - (void)setUpTap 2 3 { 4 5 // 创建点按手势 6 7 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)]; 8 9 10 11 tap.delegate = self; 12 13 14 15 [_imageView addGestureRecognizer:tap]; 16 17 } 18 19 20 21 - (void)tap:(UITapGestureRecognizer *)tap 22 23 { 24 25 NSLog(@"%s",__func__); 26 27 } 28 29
#pragma mark - 长按手势
1 // 默认会触发两次 2 3 - (void)setUpLongPress 4 5 { 6 7 UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)]; 8 9 10 11 [self.imageView addGestureRecognizer:longPress]; 12 13 } 14 15 16 17 18 19 - (void)longPress:(UILongPressGestureRecognizer *)longPress 20 21 { 22 23 24 25 if (longPress.state == UIGestureRecognizerStateBegan) { 26 27 28 29 NSLog(@"%s",__func__); 30 31 } 32 33 }
#pragma mark - 清扫
1 - (void)setUpSwipe 2 3 { 4 5 // 默认轻扫的方向是往右 6 7 UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe)]; 8 9 10 11 swipe.direction = UISwipeGestureRecognizerDirectionUp; 12 13 14 15 [self.imageView addGestureRecognizer:swipe]; 16 17 18 19 // 如果以后想要一个控件支持多个方向的轻扫,必须创建多个轻扫手势,一个轻扫手势只支持一个方向 20 21 // 默认轻扫的方向是往右 22 23 UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe)]; 24 25 26 27 swipeDown.direction = UISwipeGestureRecognizerDirectionDown; 28 29 30 31 [self.imageView addGestureRecognizer:swipeDown]; 32 33 34 35 36 37 } 38 39 40 41 - (void)swipe 42 43 { 44 45 NSLog(@"%s",__func__); 46 47 }
#pragma mark - 旋转手势
1 - (void)setUpRotation 2 3 { 4 5 UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotation:)]; 6 7 rotation.delegate = self; 8 9 [self.imageView addGestureRecognizer:rotation]; 10 11 } 12 13 14 15 // 默认传递的旋转的角度都是相对于最开始的位置 16 17 - (void)rotation:(UIRotationGestureRecognizer *)rotation 18 19 { 20 21 22 23 self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, rotation.rotation); 24 25 26 27 // 复位 28 29 rotation.rotation = 0; 30 31 32 33 // 获取手势旋转的角度 34 35 NSLog(@"%f",rotation.rotation); 36 37 }
#pragma mark - 捏合
1 - (void)setUpPinch 2 3 { 4 5 UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)]; 6 7 pinch.delegate = self; 8 9 [self.imageView addGestureRecognizer:pinch]; 10 11 } 12 13 14 15 - (void)pinch:(UIPinchGestureRecognizer *)pinch 16 17 { 18 19 self.imageView.transform = CGAffineTransformScale(self.imageView.transform, pinch.scale, pinch.scale); 20 21 22 23 // 复位 24 25 26 27 pinch.scale = 1; 28 29 }
#pragma mark - 拖拽
1 - (void)setUpPan 2 3 { 4 5 UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; 6 7 8 9 10 11 [self.imageView addGestureRecognizer:pan]; 12 13 } 14 15 16 17 - (void)pan:(UIPanGestureRecognizer *)pan 18 19 { 20 21 // 获取手势的触摸点 22 23 CGPoint curP = [pan locationInView:self.imageView]; 24 25 26 27 // 移动视图 28 29 // 获取手势的移动,也是相对于最开始的位置 30 31 CGPoint transP = [pan translationInView:self.imageView]; 32 33 34 35 self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, transP.x, transP.y); 36 37 38 39 // 复位 40 41 [pan setTranslation:CGPointZero inView:self.imageView]; 42 43 44 45 NSLog(@"%@",NSStringFromCGPoint(curP)); 46 47 } 48 49
手势状态:
1 statetypedef NS_ENUM(NSInteger, UIGestureRecognizerState) { 2 3 // 没有触摸事件发生,所有手势识别的默认状态 4 5 UIGestureRecognizerStatePossible, 6 7 // 一个手势已经开始但尚未改变或者完成时 8 9 UIGestureRecognizerStateBegan, 10 11 // 手势状态改变 12 13 UIGestureRecognizerStateChanged, 14 15 // 手势完成 16 17 UIGestureRecognizerStateEnded, 18 19 // 手势取消,恢复至Possible状态 20 21 UIGestureRecognizerStateCancelled, 22 23 // 手势失败,恢复至Possible状态 24 25 UIGestureRecognizerStateFailed, 26 27 // 识别到手势识别 28 29 UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded 30 31 }; 32 33
四:加速计:(运动)
1 - (void)orientationChanged:(NSNotification *)notification { 2 3 4 5 // Respond to changes in device orientation 6 7 8 9 }
1 - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { 2 3 4 5 if (motion == UIEventSubtypeMotionShake) 6 7 8 9 { 10 11 12 13 // User was shaking the device. Post a notification named "shake." 14 15 16 17 [[NSNotificationCenter defaultCenter] postNotificationName:@"shake" object:self]; 18 19 20 21 } 22 23 24 25 }
五:远程控制
注册:
1 MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter]; 2 3 4 5 [commandCenter.playCommand addTargetUsingBlock:^(MPRemoteCommandEvent *event) { 6 7 8 9 // Begin playing the current track. 10 11 12 13 [[MyPlayer sharedPlayer] play]; 14 15 16 17 }
使用
1 - (void)configureNowPlayingInfo:(MPMediaItem*)item{ 2 3 4 5 MPNowPlayingInfoCenter* info = [MPNowPlayingInfoCenter defaultCenter]; 6 7 8 9 NSMutableDictionary* newInfo = [NSMutableDictionary dictionary]; 10 11 12 13 NSSet* itemProperties = [NSSet setWithObjects:MPMediaItemPropertyTitle, 14 15 16 17 MPMediaItemPropertyArtist, 18 19 20 21 MPMediaItemPropertyPlaybackDuration, 22 23 24 25 MPNowPlayingInfoPropertyElapsedPlaybackTime, 26 27 28 29 nil]; 30 31 32 33 34 35 36 37 [item enumerateValuesForProperties:itemProperties 38 39 40 41 usingBlock:^(NSString *property, id value, BOOL *stop) { 42 43 44 45 [newInfo setObject:value forKey:property]; 46 47 48 49 }]; 50 51 52 53 54 55 56 57 info.nowPlayingInfo = newInfo; 58 59 60 61 }