天天看點

iOS開發--觸摸事件、手勢識别、搖晃事件、耳機線控

ios事件

ios事件分為三類:

1、觸摸事件(Multitouch events):通過觸摸、手勢進行觸摸(例如手指點選、移動、縮放、旋轉等)

2、運動事件(Accelerometer events):通過加速器進行觸發(例如手機搖晃)

3、遠端控制事件(Remote control events):通過遠端裝置進行觸發(例如耳機進行聲音控制)

在iOS中,隻有繼承自UIResponder類的對象才能才能處理事件,

iOS開發--觸摸事件、手勢識别、搖晃事件、耳機線控

一、觸摸事件

- (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(_1); 3D Touch相關方法,目前觸摸對象觸摸時力量改變所觸發的事件,傳回值是UITouchPropertyie

- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(_0); 運動開始時執行;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(_0); 運動結束後執行;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(_0); 運動被意外取消時執行;
遠端控制事件
- - (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(_0);
           

1、觸摸事件

實作以下效果:

iOS開發--觸摸事件、手勢識别、搖晃事件、耳機線控

具體代碼:

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(, , , )];
    [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提示;能捏合放大縮小圖檔,輕掃會切換圖檔。圖檔能拖動旋轉。

iOS開發--觸摸事件、手勢識别、搖晃事件、耳機線控

代碼:

#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 = ;
    CGFloat y =  +  + topPadding,height = screenSize.height - y - topPadding;
    CGRect imageFrame = CGRectMake(, 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,注意在ios中很少用輕按兩下操作
    tapGesture.numberOfTouchesRequired = ;//點按的手指數
    //添加手勢到對象(注意,這裡添加到了控制器視圖中,,而不是圖檔上,否則點選空白處無法隐藏導航欄
    [self.view addGestureRecognizer:tapGesture];

    /** 添加長按手勢 */
    UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressImage:)];
    longPressGesture.minimumPressDuration = ;//設定長按時間,預設為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 = ;
    _imageView.tag = ;
    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 + )%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 - )%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: 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: 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: 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                              = ,
    // 搖晃事件(從iOS3.0開始支援此事件)
    UIEventSubtypeMotionShake                       = ,
    //遠端控制子事件類型(從iOS4.0開始支援遠端控制事件)
    //播放事件【操作:停止狀态下,按耳機線控中間按鈕一下】
    UIEventSubtypeRemoteControlPlay                 = ,
    //暫停事件
    UIEventSubtypeRemoteControlPause                = ,
    //停止事件
    UIEventSubtypeRemoteControlStop                 = ,
    //播放或暫停切換【操作:播放或暫停狀态下,按耳機線控中間按鈕一下】
    UIEventSubtypeRemoteControlTogglePlayPause      = ,
    //下一曲【操作:按耳機線控中間按鈕兩下】
    UIEventSubtypeRemoteControlNextTrack            = ,
    //上一曲【操作:按耳機線控中間按鈕三下】
    UIEventSubtypeRemoteControlPreviousTrack        = ,
    //快退開始【操作:按耳機線控中間按鈕三下不要松開】
    UIEventSubtypeRemoteControlBeginSeekingBackward = ,
    //快退停止【操作:按耳機線控中間按鈕三下到了快退的位置松開】
    UIEventSubtypeRemoteControlEndSeekingBackward   = ,
    //快進開始【操作:按耳機線控中間按鈕兩下不要松開】
    UIEventSubtypeRemoteControlBeginSeekingForward  = ,
    //快進停止【操作:按耳機線控中間按鈕兩下到了快進的位置松開】
    UIEventSubtypeRemoteControlEndSeekingForward    = ,
};
           

根據以上做一個音樂播放器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:/ green:/ blue:/ alpha:];
    //設定全局導覽列風格和顔色
    [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:/ green:/ blue:/ alpha:]];
    [[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(, self.view.frame.size.height - , self.view.frame.size.width, )];
    view.backgroundColor = [UIColor lightGrayColor];
    view.alpha = ;
    [self.view addSubview:view];

    //添加播放按鈕
    _playButton = [UIButton buttonWithType:UIButtonTypeCustom];
    _playButton.bounds = CGRectMake(, , , );
    _playButton.center = CGPointMake(view.frame.size.width/, view.frame.size.height/);
    [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隻能在真機中測試。*