转载自iOS事件:触摸事件.运动事件.远程控制事件
iOS开发之运动事件和远程控制
iOS中,提供了事件处理:触摸事件,运动事件,远程控制事件.这很大得方便程序猿的工作.
这里先简单做个介绍:
//
// ViewController.m
// demo
//
// Created by shaoting on 15/11/23.
// Copyright © 2015年 9elephas. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
/**
* IOS事件分类:1,触摸事件2,运动事件3,远程控制事件
1.触摸事件:触碰屏幕
2.运动事件:通过加速器触发(如晃动手机)
3.远程控制事件:通过其他设备远程控制(如蓝牙)
在iOS中,只有继承自UIResponder类的对象才能触发事件
*/
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"iOS事件";
self.view.backgroundColor = [UIColor whiteColor];
//开个图
UIImage * image = [UIImage imageNamed:@"屏幕快照 2015-11-23 17.37.19"];
self.imageView = [[UIImageView alloc]initWithFrame:CGRectMake(20, 20, 150, 300)];
self.imageView.image = image;
[self.view setBackgroundColor:[UIColor colorWithPatternImage:image]]; //view的背景色使用image
[self.view addSubview:_imageView];
}
//触摸事件
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"一根手指或多根手指触摸时触发");
UITouch * touch = [touches anyObject]; //获得任意触摸对象
NSLog(@"触摸对象%@",touch);
CGPoint current = [touch locationInView:self.view];//获得当前的触摸对象
NSLog(@"当前的触摸对象%@",NSStringFromCGPoint(current)); //把CGPoint对象转换为字符串对象
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"一根或者多根手指离开屏幕时触发");
}
//以图片随鼠标移动为例
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"一根或者多根手指移动时触发");
UITouch * touch = [touches anyObject];
CGPoint current = [touch locationInView:self.view];//当前位置
CGPoint previous = [touch previousLocationInView:self.view];//当前位置的前一个位置
CGPoint center = _imageView.center;//移动前的中心位置
CGPoint offset = CGPointMake(current.x - previous.x, current.y - previous.y);
_imageView.center = CGPointMake(center.x+offset.x, center.y+offset.y);
NSUInteger count = touch.tapCount;//短时间内点击的次数
NSLog(@"短时间内点击的次数%ld",count);
UITouchPhase phase = touch.phase;//触摸周期内的各个状态
NSLog(@"触摸周期内的各个状态%ld",phase);
NSTimeInterval tamp = touch.timestamp;//触摸产生或者变化的时间戳
NSLog(@"触摸产生或者变化的时间戳%f",tamp);
UIView * view = touch.view; //触摸时所在的视图
NSLog(@"触摸时所在的视图%@",view);
UIWindow * window = touch.window;//触摸时所在的window
NSLog(@"触摸时所在的window%@",window);
}
//运动事件
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
NSLog(@"运动开始时触发");
}
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
NSLog(@"运动结束时触发");
}
-(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{
NSLog(@"运动被意外取消时触发");
}
//远程控制事件
-(void)remoteControlReceivedWithEvent:(UIEvent *)event{
NSLog(@"接收到远程控制消息时执行");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
其中,模拟器开启晃动手机的功能键是:
触摸事件:
//
// ViewController.m
// demo1
//
// Created by shaoting on 15/11/23.
// Copyright © 2015年 9elephas. All rights reserved.
//
#import "ViewController.h"
#define Hight [UIScreen mainScreen].bounds.size.height-49
#define Width [UIScreen mainScreen].bounds.size.width
@interface ViewController ()<UIGestureRecognizerDelegate>{
int _count;
}
@end
@implementation ViewController
/**
* 复杂手势:6种
点按手势:UITapGestureRecognizer
捏合手势:UIPinchGestureRecognizer
拖动手势:UIPanGetureRecognizer
清扫手势:UISwipeGetureRecognizer
旋转手势:UIRotationGetureReconizer
长按手势:UILongPressGetureReconizer
6种手势中点按手势属于离散型手势(不用判断状态),其余手势属于连续手势(除清扫手势外均需要判断状态).
各手势的触发时机:点按事件:点击屏幕然后松开时触发
长按事件:长时间点击屏幕不松开即会触发(不加长按&拖动冲突解决),长时间点击屏幕松开即会触发(加长按&拖动手势冲突解决ß)
拖动事件:手指在屏幕上拖动时触发
旋转手势:手指在屏幕上旋转时触发
清扫手势:只在清扫完毕(手指清扫屏幕离开屏幕时)才会触发
捏合手势:手指捏合时触发
事件冲突:点击手势会和旋转手势会发生冲突.
清扫手势会和拖动手势会发生冲突.
*/
- (void)viewDidLoad {
[super viewDidLoad];
[self initLayout];
[self addGesture];
// Do any additional setup after loading the view, typically from a nib.
}
//布局
-(void)initLayout{
_imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 49, Width, Hight)];
_imageView.userInteractionEnabled = YES; //必须设置为YES,否则无法就收手势操作
UIImage * image = [UIImage imageNamed:@"0"];
_imageView.image = image;
self.title = @"0";
[self.view addSubview:_imageView];
}
//添加手势
-(void)addGesture{
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
tap.numberOfTapsRequired = 1; //设置点击次数,默认为1
tap.numberOfTouchesRequired = 1;//设置为手指数
[self.view addGestureRecognizer:tap];//添加手势进视图控制器上的视图
UIPinchGestureRecognizer * pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinch:)];
[self.view addGestureRecognizer:pinch];
UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];
UISwipeGestureRecognizer * left = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
left.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:left];
UISwipeGestureRecognizer * right = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
right.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:right];
UIRotationGestureRecognizer * rotation = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotation:)];
[self.view addGestureRecognizer:rotation];
UILongPressGestureRecognizer * longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];
[self.view addGestureRecognizer:longPress];
//手势冲突解决
[pan requireGestureRecognizerToFail:left];
[pan requireGestureRecognizerToFail:right];//解决拖动手势和清扫手势的冲突
[longPress requireGestureRecognizerToFail:pan];//解决长按手势和拖动手势的冲突
//多种手势在不同视图上的同时执行
//iOS默认只会触发一次,因为触发是沿着响应者链依次匹配,只要匹配到响应就会停止.可以修改- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer的返回值,默认返回NO
//上面演示了添加长按手势进视图控制器视图上,下面再添加长按手势到_imageView上,让这两个手势可以同时在不同视图上执行.
UILongPressGestureRecognizer * longpress1 = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress1:)];
longpress1.delegate = self;
[_imageView addGestureRecognizer:longpress1];
}
//点按事件
//点按是否显示状态栏
-(void)tap:(UITapGestureRecognizer *)tap{
BOOL recognizer = !self.navigationController.navigationBarHidden;
[self.navigationController setNavigationBarHidden:recognizer];
}
//捏合事件
//缩放图片
-(void)pinch:(UIPinchGestureRecognizer *)pinch{
// NSLog(@"%ld",(long)pinch.state); // 捏合状态
//状态:0--未开始 1--手势开始 2--手势进行 3--手势完成
// typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
// UIGestureRecognizerStatePossible, // 尚未识别是何种手势操作(但可能已经触发了触摸事件),默认状态
//
// UIGestureRecognizerStateBegan, // 手势已经开始,此时已经被识别,但是这个过程中可能发生变化,手势操作尚未完成
// UIGestureRecognizerStateChanged, // 手势状态发生转变
// UIGestureRecognizerStateEnded, // 手势识别操作完成(此时已经松开手指)
// UIGestureRecognizerStateCancelled, // 手势被取消,恢复到默认状态
//
// UIGestureRecognizerStateFailed, // 手势识别失败,恢复到默认状态
//
// UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded // 手势识别完成,同UIGestureRecognizerStateEnded
// };
if (pinch.state == UIGestureRecognizerStateChanged) {
_imageView.transform = CGAffineTransformMakeScale(pinch.scale, pinch.scale); //.scele记录缩放属性
}else if(pinch.state == UIGestureRecognizerStateEnded){ //缩放完毕后恢复
[UIView animateWithDuration:5 animations:^{
_imageView.transform = CGAffineTransformIdentity;//取消形变
}];
}
}
//拖动手势
-(void)pan:(UIPanGestureRecognizer *)pan{
if (pan.state == UIGestureRecognizerStateChanged) {
CGPoint transform = [pan translationInView:self.view];//利用translationInView:取得在相对视图中的移动
_imageView.transform = CGAffineTransformMakeTranslation(transform.x, transform.y);
}else if(pan.state == UIGestureRecognizerStateEnded){
[UIView animateWithDuration:2 animations:^{
_imageView.transform = CGAffineTransformIdentity;
}];
}
}
//清扫手势
-(void)swipe:(UISwipeGestureRecognizer *)swipe{
//因为清扫手势虽然是连续手势,但是只有在清扫完毕时才会触发,所以不用判断状态
if (swipe.direction == UISwipeGestureRecognizerDirectionRight) {
[self nextImage];
}
if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
[self lastImage];
}
}
-(void)nextImage{
int index=(_count+2+1)%2;
NSString *imageName=[NSString stringWithFormat:@"%i",index];
_imageView.image=[UIImage imageNamed:imageName];
_count=index;
[self showName];
}
-(void)lastImage{
int index=(_count+2-1)%2;
NSString *imageName=[NSString stringWithFormat:@"%i",index];
_imageView.image=[UIImage imageNamed:imageName];
_count=index;
[self showName];
}
-(void)showName{
NSString * name = [NSString stringWithFormat:@"%i",_count];
self.title = name;
}
//旋转事件
//旋转使图片旋转
-(void)rotation:(UIRotationGestureRecognizer *)rotation{
if (rotation.state == UIGestureRecognizerStateChanged) {
_imageView.transform = CGAffineTransformMakeRotation(rotation.rotation);// .rotation记录了改变的弧度
}else if(rotation.state == UIGestureRecognizerStateEnded){
[UIView animateWithDuration:2 animations:^{
_imageView.transform = CGAffineTransformIdentity;
}];
}
}
//长按事件
-(void)longPress:(UILongPressGestureRecognizer *)longPress{
//长按选择是否下载图片
if (longPress.state == UIGestureRecognizerStateBegan) {
UIAlertController * alterC = [UIAlertController alertControllerWithTitle:@"提示" message:@"是否下载该图片" preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction * cance = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
UIAlertAction * down = [UIAlertAction actionWithTitle:@"下载" style:UIAlertActionStyleDefault handler:nil];
[alterC addAction:cance];
[alterC addAction:down];
[self presentViewController:alterC animated:YES completion:nil];
}
}
-(void)longPress1:(UILongPressGestureRecognizer *)longpress{
NSLog(@"添加手势进_imageView");
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
if ([otherGestureRecognizer.view isKindOfClass:[self.view class]]) {//如果是控制器视图则继续沿着响应者链传播
return YES;
}
return NO;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
运动事件:
监听运动事件对于UI控件有个前提就是监听对象必须是第一响应者(对于UIViewController视图控制器和UIAPPlication没有此要求)。这也就意味着如果监听的是一个UI控件那么-(BOOL)canBecomeFirstResponder;方法必须返回YES。同时控件显示时(在-(void)viewWillAppear:(BOOL)animated;事件中)调用视图控制器的becomeFirstResponder方法。当视图不再显示时(在-(void)viewDidDisappear:(BOOL)animated;事件中)注销第一响应者身份。(是真的吗?)
//
// ViewController.m
// demo2
//
// Created by shaoting on 15/11/25.
// Copyright © 2015年 9elephas. All rights reserved.
//
#import "ViewController.h"
@interface ViewController (){
UIImageView * _imageView;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_imageView = [[UIImageView alloc]initWithFrame:[UIScreen mainScreen].bounds];
_imageView.image = [UIImage imageNamed:@"1"];
[self.view addSubview:_imageView];
}
#pragma mark 运动开始
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
NSLog(@"运动开始了");
//这里只处理摇晃事件
if (motion==UIEventSubtypeMotionShake) {
_imageView.image=[self getImage];
}
}
#pragma mark 运动结束
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
NSLog(@"运动结束了");
}
-(UIImage *)getImage{
int index= arc4random()%2;
NSString *imageName=[NSString stringWithFormat:@"%i",index];
UIImage *image=[UIImage imageNamed:imageName];
return image;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
远程控制事件:
iOS远程控制事件,是通过其他远程设备触发的(比如耳机控制按钮),iOS远程控制事件相关的只有-(void)remoteControlReceivedWithEvent:(UIEvent *)event
监听远程控制事件的前提:
启动远程事件接收,调用
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
UI控件同样要求必须成为第一响应者【使用参考运动事件】
但如果是视图控制器或UIApplication,就没有要求成为第一响应者
应用程序必须是 当前音频额控制者
目前iOS7给我们的远程控制权限仅限于音频控制
typedef NS_ENUM(NSInteger, UIEventSubtype) {
// 不包含任何子事件类型
UIEventSubtypeNone = 0,
// 摇晃事件(从iOS3.0开始支持此事件)
UIEventSubtypeMotionShake = 1,
//远程控制子事件类型(从iOS4.0开始支持远程控制事件)
//播放事件【操作:停止状态下,按耳机线控中间按钮一下】
UIEventSubtypeRemoteControlPlay = 100,
//暂停事件
UIEventSubtypeRemoteControlPause = 101,
//停止事件
UIEventSubtypeRemoteControlStop = 102,
//播放或暂停切换【操作:播放或暂停状态下,按耳机线控中间按钮一下】
UIEventSubtypeRemoteControlTogglePlayPause = 103,
//下一曲【操作:按耳机线控中间按钮两下】
UIEventSubtypeRemoteControlNextTrack = 104,
//上一曲【操作:按耳机线控中间按钮三下】
UIEventSubtypeRemoteControlPreviousTrack = 105,
//快退开始【操作:按耳机线控中间按钮三下不要松开】
UIEventSubtypeRemoteControlBeginSeekingBackward = 106,
//快退停止【操作:按耳机线控中间按钮三下到了快退的位置松开】
UIEventSubtypeRemoteControlEndSeekingBackward = 107,
//快进开始【操作:按耳机线控中间按钮两下不要松开】
UIEventSubtypeRemoteControlBeginSeekingForward = 108,
//快进停止【操作:按耳机线控中间按钮两下到了快进的位置松开】
UIEventSubtypeRemoteControlEndSeekingForward = 109,
};
实例:
#import "ViewController.h"
@interface ViewController (){
UIButton *_playButton;
BOOL _isPlaying;}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self initLayout];
}
- (BOOL)canBecomeFirstResponder{
return NO;
}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSURL *url = [NSURL URLWithString:@"http://stream.jewishmusicstream.com:8000"]; _player = [[AVPlayer alloc] initWithURL:url];
}
#pragma mark 远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event{
if(event.type == UIEventTypeRemoteControl){
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay: [_player play];
_isPlaying = true;
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
[self btnClick:_playButton];
break;
case UIEventSubtypeRemoteControlNextTrack:
NSLog(@"Next...");
break;
case UIEventSubtypeRemoteControlPreviousTrack:
NSLog(@"Previous...");
break;
case UIEventSubtypeRemoteControlBeginSeekingForward:
NSLog(@"Begin seek forward...");
break;
case UIEventSubtypeRemoteControlEndSeekingForward:
NSLog(@"End seek forward...");
break;
case UIEventSubtypeRemoteControlBeginSeekingBackward:
NSLog(@"Begin seek backward...");
break;
case UIEventSubtypeRemoteControlEndSeekingBackward:
NSLog(@"End seek backward...");
break;
default:
break;
}
[self changeUIState];
}}
#pragma mark 界面布局
- (void)initLayout{
//专辑封面
UIImage *image = [UIImage imageNamed:@"wxl.jpg"];
CGRect *frame = [UIScreen mainScreen].applicationFrame;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = image;
imageView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:imageView];
//播放控制面板
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 480, 320, 88)];
view.backgroundColor = [UIColor lightGrayColor]; view.alpha = 0.9;
[self.view addSubview:view];
//添加播放按钮 _playButton = [UIButton buttonWithType:UIButtonTypeCustom];
_playButton.bounds = CGRectMake(0, 0, 50, 50);
CGFloat playBtnX = view.frame.size.width/2;
CGFloat playBtnY = view.frame.size.height/2; _playButton.center = CGPointMake(playBtnX, playBtnY);
[self changeUIState];
[_playButton addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview:_playButton];
}
#pragma mark 界面状态
- (void)changeUIState{
if(_isPlaying){
UIImage *pauseImage = [UIImage imageNamed:@"playing_btn_pause_n.png"];
UIImage *pauseImageH = [UIImage imageNamed:@"playing_btn_pause_h.png"];
[_playButton setImage:pauseImage forState:UIControlStateNormal];
[_playButton setImage:pauseImageH forState:UIControlStateHighlighted];
}else{
UIImage *playImage = [UIImage imageNamed:@"playing_btn_play_n.png"];
UIImage *playImageH = [UIImage imageNamed:@"playing_btn_play_h.png"];
[_playButton setImage:playImage forState:UIControlStateNormal];
[_playButton setImage:playImageH forState:UIControlStateHighlighted];
}}
- (void)btnClick:(UIButton *)btn{
if (_isPlaying) {
[_player pause];
}else{
[_player play];
}
_isPlaying =! _isPlaying;
[self changeUIState];
}
@end
远程控制实例效果