iOS开发--触摸事件、手势识别、摇晃事件、耳机线控

本文详细介绍了iOS中的各类事件处理机制,包括触摸事件、手势识别、运动事件及远程控制事件。通过具体的代码实例展示了如何实现这些事件的响应与处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ios事件
ios事件分为三类:
1、触摸事件(Multitouch events):通过触摸、手势进行触摸(例如手指点击、移动、缩放、旋转等)
2、运动事件(Accelerometer events):通过加速器进行触发(例如手机摇晃)
3、远程控制事件(Remote control events):通过远程设备进行触发(例如耳机进行声音控制)

在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:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;  触摸意外取消时执行(例如正在触摸时打入电话);

- (void)touchesEstimatedPropertiesUpdated:(NSSet * _Nonnull)touches NS_AVAILABLE_IOS(9_1); 3D Touch相关方法,当前触摸对象触摸时力量改变所触发的事件,返回值是UITouchPropertyie

- (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);

1、触摸事件
实现以下效果:
这里写图片描述
具体代码:

a、新建一个继承与UIView的类
#import "XFImage.h"

@implementation XFImage

-(instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"u=2119684598,3653404795&fm=21&gp=0.jpg"]]];
    }
    return self;
}
b、在ViewController中
#import "ViewController.h"
#import "XFImage.h"
@interface ViewController ()

@property (strong, nonatomic) XFImage *image;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _image = [[XFImage alloc]initWithFrame:CGRectMake(50, 50, 150, 169)];
    [self.view addSubview:_image];
}
#pragma mark - **************** 视图控制器的触摸事件
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"UIViewController start touch ...");
}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //取得第一个触摸对象(对于多点触摸可能有多个对象)
    UITouch *touch = [touches anyObject];
    NSLog(@"%@",touch);

    //取得当前的位置
    CGPoint current = [touch locationInView:self.view];
    //取得前一个位置
    CGPoint previous = [touch previousLocationInView:self.view];

    //移动前的中点位置
    CGPoint center = _image.center;
    //移动偏移量
    CGPoint offset = CGPointMake(current.x - previous.x, current.y - previous.y);
    //重新设置新位置
    _image.center = CGPointMake(center.x + offset.x, center.y + offset.y);

    NSLog(@"UIViewController moving...");
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"UIViewController touch end...");
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

上面示例中我们用到了UITouch类,当执行触摸事件时会将这个对象传入。在这个对象中包含了触摸的所有信息:

  • window:触摸时所在的窗口
  • view:触摸时所在视图
  • tapCount:短时间内点击的次数
  • timestamp:触摸产生或变化的时间戳
  • phase:触摸周期内的各个状态
  • locationInView:方法:取得在指定视图的位置
  • previousLocationInView:方法:取得移动的前一个位置

注意从上面运行效果可以看到无论是选择XFImage拖动还是在界面其他任意位置拖动都能达到移动图片的效果。既然XFImage是UIView当然在XFImage中也能触发相应的触摸事件,如果在XFImage中实现了触摸事件方法,则此时如果运行程序会发现如果拖动KCImage无法达到预期的效果,但是可以发现此时会调用KCImage的触摸事件而不会调用 KCTouchEventViewController中的触摸事件。如果直接拖拽其他空白位置则可以正常拖拽,而且从输出信息可以发现此时调用的是视图 控制器的触摸事件。
二、手势识别
iOS 中有6种手势:

UITapGestureRecognizer     点按手势

UIPinchGestureRecognizer   捏合手势

UIPanGestureRecognizer      拖动手势

UISwipeGestureRecognizer    轻扫手势,支持四个方向的轻扫,但是不同的方向要分别定义轻扫手势

UIRotationGestureRecognizer    旋转手势

UILongPressGestureRecognizer    长按手势

所有的手势操作都继承于UIGestureRecognizer,这个类本身不能直接使用。这个类中定义了这几种手势共有的一些属性和方法

@property(nonatomic,readonly) UIGestureRecognizerState state; 手势状态
@property(nonatomic, getter=isEnabled) BOOL enabled;  手势是否可用
@propert (nullable, nonatomic,readonly) UIView *view;  触发手势的视图
@property(nonatomic) BOOL cancelsTouchesInView;
@property(nonatomic) BOOL delaysTouchesBegan;  手势识别失败前不执行触摸开始事件,默认为NO;如果为YES,那么成功识别则不执行触摸开始事件,失败则执行触摸开始事件;如果为NO,则不管成功与否都执行触摸开始事件
@property(nonatomic) BOOL delaysTouchesEnded;
- (void)addTarget:(id)target action:(SEL)action;  添加触摸执行事件
- (void)removeTarget:(nullable id)target action:(nullable SEL)action; 移除触摸执行事件
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;指定一个手势需要另一个手势执行失败才会执行
- (CGPoint)locationInView:(nullable UIView*)view;在指定视图中的相对位置
- (NSUInteger)numberOfTouches; 触摸点的个数
- (CGPoint)locationOfTouch:(NSUInteger)touchIndex inView:(nullable UIView*)view; 触摸点相对于指定视图的位置
iOS 中将手势状态分为:
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
    UIGestureRecognizerStatePossible,   // 尚未识别是何种手势操作(但可能已经触发了触摸事件),默认状态
    UIGestureRecognizerStateBegan,      // 手势已经开始,此时已经被识别,但是这个过程中可能发生变化,手势操作尚未完成
    UIGestureRecognizerStateChanged,    // 手势状态发生转变
    UIGestureRecognizerStateEnded,      // 手势识别操作完成(此时已经松开手指)
    UIGestureRecognizerStateCancelled,  // 手势被取消,恢复到默认状态
    UIGestureRecognizerStateFailed,     // 手势识别失败,恢复到默认状态
    UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded // 手势识别完成,同UIGestureRecognizerStateEnded
};

创建手势可归为四部:
1、创建对应的手势对象
2、设置手势识别属性
3、将手势加到对象上
4、编写手势操作的方法

使用手势做一个 如下demo:
这个demo的功能主要有:
点图片会在导航栏显示图片名称;长按图片会显示actionsheet提示;能捏合放大缩小图片,轻扫会切换图片。图片能拖动旋转。
这里写图片描述
代码:

#import "PhotoViewController.h"

@interface PhotoViewController ()<UIGestureRecognizerDelegate>

@property (strong, nonatomic) UIImageView *imageView;//图片展示控件

@property (assign, nonatomic) int currentIndex;//当前图片索引

@end
#define kImageCount 3
@implementation PhotoViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    [self initLayout];
    [self addGesture];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - **************** 布局
-(void)initLayout{
    /** 添加图片展示控件 */
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
    CGFloat topPadding = 20;
    CGFloat y = 22 + 44 + topPadding,height = screenSize.height - y - topPadding;
    CGRect imageFrame = CGRectMake(0, y, screenSize.width, height);
    _imageView = [[UIImageView alloc]initWithFrame:imageFrame];
    _imageView.contentMode = UIViewContentModeScaleToFill;//设置内容模式为缩放填充
    _imageView.userInteractionEnabled = YES;//这里必须为yes,否则无法接收手势消息。
    [self.view addSubview:_imageView];

    //添加图片库
    UIImage *image = [UIImage imageNamed:@"0.jpg"];
    _imageView.image = image;
    [self showPhotoName];
}
#pragma mark - **************** 添加手势
-(void)addGesture{
    /** 添加点按手势 */
    //创建手势对象
    UITapGestureRecognizer *tapGesture  = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapImage:)];
    tapGesture.numberOfTapsRequired = 1;//设置点按次数,默认为1,注意在ios中很少用双击操作
    tapGesture.numberOfTouchesRequired = 1;//点按的手指数
    //添加手势到对象(注意,这里添加到了控制器视图中,,而不是图片上,否则点击空白处无法隐藏导航栏
    [self.view addGestureRecognizer:tapGesture];

    /** 添加长按手势 */
    UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressImage:)];
    longPressGesture.minimumPressDuration = 0.5;//设置长按时间,默认为0.5秒,一般这个值不要修改
    //注意由于我们要做长按提示删除操作,因此这个手势不再添加到控制器视图上而是添加到图片上
    [_imageView addGestureRecognizer:longPressGesture];

    /** 添加捏合手势 */
    UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinchImage:)];
    [self.view addGestureRecognizer:pinchGesture];

    /** 添加旋转拖动手势 */
    UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotateImage:)];
    [_imageView addGestureRecognizer:rotationGesture];

    /** 添加拖动手势 */
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panImage:)];
    [_imageView addGestureRecognizer:panGesture];

    /** 添加轻扫手势 */
    //注意一个轻扫手势只能控制一个方向,默认向右,通过direction进行方向控制
    UISwipeGestureRecognizer *swipeGestureToRight = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeImage:)];
    swipeGestureToRight.direction = UISwipeGestureRecognizerDirectionRight;
    [self.view addGestureRecognizer:swipeGestureToRight];

    UISwipeGestureRecognizer *swipeGestureToLeft = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeImage:)];
    swipeGestureToRight.direction = UISwipeGestureRecognizerDirectionLeft;
    [self.view addGestureRecognizer:swipeGestureToLeft];

    //解决在图片上滑动时拖动手势和轻扫手势的冲突
    [panGesture requireGestureRecognizerToFail:swipeGestureToRight];
    [panGesture requireGestureRecognizerToFail:swipeGestureToLeft];
    //解决拖动和长按手势之间的冲突
    [longPressGesture requireGestureRecognizerToFail:panGesture];

    /** 演示不同视图的手势同时执行
     * 在上面imageView 已经添加了长按手势,这里给视图控制器也加上长按手势,两者都执行
     *
     *
     */
    self.view.tag = 100;
    _imageView.tag = 200;
    UILongPressGestureRecognizer *viewLongPressgesture = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressView:)];
     viewLongPressgesture.delegate=self;
    [self.view addGestureRecognizer:viewLongPressgesture];
}
#pragma mark 显示图片名称
-(void)showPhotoName{
    NSString *title = [NSString stringWithFormat:@"%i.jpg",_currentIndex];
    [self setTitle:title];
}
#pragma mark 下一张图片
-(void)nextImage{
    int index = (_currentIndex + kImageCount + 1)%kImageCount;
    NSString *imageName = [NSString stringWithFormat:@"%i.jpg",index];
    _imageView.image = [UIImage imageNamed:imageName];
    _currentIndex = index;
    [self showPhotoName];
}
#pragma mark 上一张图片
-(void)lastImage{
    int index = (_currentIndex + kImageCount - 1)%kImageCount;
    NSString *imageName = [NSString stringWithFormat:@"%i.jpg",index];
    _imageView.image = [UIImage imageNamed:imageName];
    _currentIndex = index;
    [self showPhotoName];
}
#pragma mark - **************** 手势操作
#pragma mark 点按隐藏或显示导航栏
-(void)tapImage:(UITapGestureRecognizer *)gesture{
    NSLog(@"tap:%li",(long)gesture.state);
    BOOL hidden = !self.navigationController.navigationBarHidden;
    [self.navigationController setNavigationBarHidden:hidden animated:YES];
}
#pragma mark 长按提示是否删除
-(void)longPressImage:(UILongPressGestureRecognizer *)gesture{
    NSLog(@"longpress:%i",gesture.state);
    //注意其实在手势里面有一个view属性可以获取点按的视图
//    UIImageView *imageView = (UIImageView *)gesture.view;
    //由于连续手势此方法会调用多次,所以需要判断起手势状态
    if (gesture.state == UIGestureRecognizerStateBegan) {
        UIAlertController * alertController = [UIAlertController alertControllerWithTitle: nil                                                                            message: nil                                                                      preferredStyle:UIAlertControllerStyleActionSheet];
        //添加Button
        [alertController addAction: [UIAlertAction actionWithTitle: @"Delete the photo" style: UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
            //处理点击拍照
        }]];
        [alertController addAction: [UIAlertAction actionWithTitle: @"取消" style: UIAlertActionStyleCancel handler:nil]];

        [self presentViewController: alertController animated: YES completion: nil];

}
}
#pragma mark 捏合时缩放图片
-(void)pinchImage:(UIPinchGestureRecognizer *)gesture{
    if (gesture.state == UIGestureRecognizerStateChanged) {
        //捏合手势scale属性记录的缩放比例
        _imageView.transform = CGAffineTransformMakeScale(gesture.scale, gesture.scale);
    }else if (gesture.state == UIGestureRecognizerStateEnded){
        //结束恢复
        [UIView animateWithDuration:0.5 animations:^{
            _imageView.transform = CGAffineTransformIdentity;//取消一切的变形
        }];
    }
}
#pragma mark 旋转图片
-(void)rotateImage:(UIRotationGestureRecognizer *)gesture{
    if (gesture.state == UIGestureRecognizerStateChanged) {
        //旋转手势中rotation属性记录了旋转弧度
        _imageView.transform = CGAffineTransformMakeRotation(gesture.rotation);
    }else if (gesture.state == UIGestureRecognizerStateEnded){
        [UIView animateWithDuration:0.8 animations:^{
            _imageView.transform = CGAffineTransformIdentity;//取消形变
        }];
    }
}
#pragma mark 拖动图片
-(void)panImage:(UIPanGestureRecognizer *)gesture{
    if (gesture.state==UIGestureRecognizerStateChanged) {
        CGPoint translation=[gesture translationInView:self.view];//利用拖动手势的translationInView:方法取得在相对指定视图(这里是控制器根视图)的移动
        _imageView.transform=CGAffineTransformMakeTranslation(translation.x, translation.y);
    }else if(gesture.state==UIGestureRecognizerStateEnded){
        [UIView animateWithDuration:0.5 animations:^{
            _imageView.transform=CGAffineTransformIdentity;
        }];
    }
}
#pragma mark 轻扫则查看下一张或者上一张
//注意虽然轻扫手势是连续手势,但是只有在识别结束才会触发,不用判断状态
-(void)swipeImage:(UISwipeGestureRecognizer *)gesture{
    //direction记录的轻扫的方向
    if (gesture.direction==UISwipeGestureRecognizerDirectionRight) {//向右
        [self nextImage];
        //            NSLog(@"right");
    }else if(gesture.direction==UISwipeGestureRecognizerDirectionLeft){//向左
        //            NSLog(@"left");
        [self lastImage];
    }
}
#pragma mark 视图控制器的长按手势
-(void)longPressView:(UILongPressGestureRecognizer *)gesture{
    NSLog(@"view long press!");
}
#pragma mark - **************** 手势代理
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    //注意,这里控制只有在UIImageView中才能向下传播,其他情况不允许
    if ([otherGestureRecognizer.view isKindOfClass:[UIImageView class]]) {
        return YES;
    }
    return NO;
}
#pragma mark - **************** 触摸事件
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"touch begin...");
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"touch end...");
}
@end

注意:
- UIImageView默认是不支持交互的,也就是userInteractionEnabled=NO ,因此要接收触摸事件(手势识别),必须设置userInteractionEnabled=YES(在iOS中UILabel、UIImageView 的userInteractionEnabled默认都是NO,UIButton、UITextField、UIScrollView、 UITableView等默认都是YES)。
- 轻扫手势虽然是连续手势但是它的操作事件只会在识别结束时调用一次,其他连续手势都会调用多次,一般需要进行状态判断;此外轻扫手势支持四个方向,但是如果要支持多个方向需要添加多个轻扫手势。
- 如果不设置

 //解决在图片上滑动时拖动手势和轻扫手势的冲突
-     [panGesture requireGestureRecognizerToFail:swipeGestureToRight];
-     [panGesture requireGestureRecognizerToFail:swipeGestureToLeft];
    //解决拖动和长按手势之间的冲突
-     [longPressGesture requireGestureRecognizerToFail:panGesture];

则扫描图片不会切换图片,因为轻扫和拖动手势冲突。
- 代码中使用了 手势代理-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;这个方法,这个代理方法默认返回NO,会阻断继续向下识别手势,如果返回YES则可以继续向下传播识别。这样在上面demo中长按手势在image上也会传递到控制器上。

三、运动事件:
在iOS中和运动相关的有三个事件:开始运动、结束运动、取消运动。
监听运动事件对于UI控件有个前提就是监听对象必须是第一响应者(对于UIViewController视图控制器和UIAPPlication没有此要求)。这也就意味着如果监听的是一个UI控件那么-(BOOL)canBecomeFirstResponder;方法必须返回YES。同时控件显示时(在-(void)viewWillAppear:(BOOL)animated;事件中)调用视图控制器的becomeFirstResponder方法。当视图不再显示时(在-(void)viewDidDisappear:(BOOL)animated;事件中)注销第一响应者身份。
实现一个demo,能晃动手机进行切图:
代码:

ImageView.h#import <UIKit/UIKit.h>

@interface ImageView : UIImageView

@end
ImageView.m#import "ImageView.h"

#define kImageCount 3

@implementation ImageView

-(instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        self.image = [self getImage];
    }
    return self;
}
#pragma mark - **************** 设置控件可以成为第一响应者
-(BOOL)canBecomeFirstResponder{
    return YES;
}
#pragma mark - **************** 运动开始
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
    //这里只处理摇晃事件
    if (motion == UIEventSubtypeMotionShake) {
        self.image = [self getImage];
    }
}
#pragma mark - **************** 运动结束
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{

}
#pragma mark - **************** 随机取得图片
-(UIImage *)getImage{
    int index = arc4random()%kImageCount;
    NSString *imageName = [NSString stringWithFormat:@"%i.jpg",index];
    UIImage *image = [UIImage imageNamed:imageName];
    return image;
}
@end
ViewController中:
#import "ShakeViewController.h"
#import "ImageView.h"
@interface ShakeViewController ()

@property (strong, nonatomic) ImageView *imageView;

@end

@implementation ShakeViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];

}
//视图显示时让控件变成第一响应者
-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    _imageView = [[ImageView alloc]initWithFrame:[UIScreen mainScreen].bounds];
    _imageView.userInteractionEnabled = true;
    [self.view addSubview:_imageView];
    [_imageView becomeFirstResponder];
}
//视图不显示时让控件注销第一响应者的身份
-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    [_imageView resignFirstResponder];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

四、远程控制事件

要监听到这个事件有三个前提(视图控制器UIViewController或应用程序UIApplication只有两个)

*- 启用远程事件接收(使用[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];方法)。
- 对于UI控件同样要求必须是第一响应者(对于视图控制器UIViewController或者应用程序UIApplication对象监听无此要求)。
- 应用程序必须是当前音频的控制者,也就是在iOS 7中通知栏中当前音频播放程序必须是我们自己开发程序。*

运动事件中我们也提到一个枚举类型UIEventSubtype,而且我们利用它来判断是否运动事件,在枚举中还包含了我们运程控制的子事件类型:

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,
};

根据以上做一个音乐播放器demo:
代码:

Appdelegate中:
#import "AppDelegate.h"
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    _window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    _window.backgroundColor = [UIColor colorWithRed:249/255.0 green:249/255.0 blue:249/255.0 alpha:1];
    //设置全局导航条风格和颜色
    [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:23/255.0 green:180/255.0 blue:237/255.0 alpha:1]];
    [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];

    ViewController *mainController = [[ViewController alloc]init];
    _window.rootViewController = mainController;

    //设置播放会话,在后台可以继续播放(还需要设置程序允许后台运行模式)
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    if (![[AVAudioSession sharedInstance] setActive:YES error:nil]) {
        NSLog(@"Failed to set up a session");
    }

    //启动远程控制事件接收
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

    [_window makeKeyAndVisible];
    return YES;
}
ViewController中:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
@interface ViewController ()

@property (strong, nonatomic) UIButton *playButton;

@property (assign, nonatomic) BOOL  isPlaying;

@property (strong, nonatomic) AVPlayer *player;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self initLayout];
}
-(BOOL)canBecomeFirstResponder{
    return NO;
}
-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    _player = [[AVPlayer alloc]initWithURL:[NSURL URLWithString:@"http://sc1.111ttt.com/2016/5/06/11/199111211522.mp3"]];
}

#pragma mark - **************** 远程控制事件
-(void)remoteControlReceivedWithEvent:(UIEvent *)event{
    NSLog(@"%i,%i",event.type,event.subtype);
    if (event.type == UIEventTypeRemoteControl) {
        switch (event.subtype) {
            case UIEventSubtypeRemoteControlPlay:
                [_player play];
                _isPlaying = true;
                break;
            case UIEventSubtypeRemoteControlTogglePlayPause:
                if (_isPlaying) {
                    [_player pause];
                }else{
                    [_player play];
                }
                _isPlaying = !_isPlaying;
                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:@"1.jpg"];
    UIImageView *imageView = [[UIImageView alloc]initWithFrame:[UIScreen mainScreen].bounds];
    imageView.image = image;
    imageView.contentMode = UIViewContentModeScaleAspectFill;
    [self.view addSubview:imageView];
    //播放控制面板
    UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height - 88, self.view.frame.size.width, 88)];
    view.backgroundColor = [UIColor lightGrayColor];
    view.alpha = 0.9;
    [self.view addSubview:view];

    //添加播放按钮
    _playButton = [UIButton buttonWithType:UIButtonTypeCustom];
    _playButton.bounds = CGRectMake(0, 0, 50, 50);
    _playButton.center = CGPointMake(view.frame.size.width/2, view.frame.size.height/2);
    [self changeUIState];
    [_playButton addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
    [view addSubview:_playButton];

}
#pragma mark - **************** 界面状态
-(void)changeUIState{

    if(_isPlaying){
        [_playButton setImage:[UIImage imageNamed:@"shanchu1.png"] forState:UIControlStateNormal];
        [_playButton setImage:[UIImage imageNamed:@"shanchu1.png"] forState:UIControlStateHighlighted];
    }else{
        [_playButton setImage:[UIImage imageNamed:@"bofang2.png"] forState:UIControlStateNormal];
        [_playButton setImage:[UIImage imageNamed:@"bofang2.png"] forState:UIControlStateHighlighted];
    }

}
-(void)btnClick:(UIButton *)btn{
    if (_isPlaying) {
        [_player pause];
    }else{
        [_player play];
    }
    _isPlaying=!_isPlaying;
    [self changeUIState];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

:*在info.plist中添加UIBackgroundModes并且添加一个元素值为audio。
这个demo只能在真机中测试。*

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值