在今天的文章中还剩下最后一类事件:远程控制,远程控制事件这里主要说的就是耳机线控操作。在前面的事件列表中,大家可以看到在iOS中和远程控制事件有关的只有一个- (void)remoteControlReceivedWithEvent:(UIEvent *)event NS_AVAILABLE_IOS(4_0);事件。要监听到这个事件有三个前提(视图控制器UIViewController或应用程序UIApplication只有两个)

启用远程事件接收(使用[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];方法)。

对于UI控件同样要求必须是第一响应者(对于视图控制器UIViewController或者应用程序UIApplication对象监听无此要求)。

应用程序必须是当前音频的控制者,也就是在iOS 7中通知栏中当前音频播放程序必须是我们自己开发程序。

基于第三点我们必须明确,如果我们的程序不想要控制音频,只是想利用远程控制事件做其他的事情,例如模仿iOS7中的按音量+键拍照是做不到的,目前iOS7给我们的远程控制权限还仅限于音频控制(当然假设我们确实想要做一个和播放音频无关的应用但是又想进行远程控制,也可以隐藏一个音频播放操作,拿到远程控制操作权后进行远程控制)。

运动事件中我们也提到一个枚举类型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,
};

这里我们将远程控制事件放到视图控制器(事实上很少直接添加到UI控件,一般就是添加到UIApplication或者UIViewController),模拟一个音乐播放器。

1.首先在应用程序启动后设置接收远程控制事件,并且设置音频会话保证后台运行可以播放(注意要在应用配置中设置允许多任务)

//
//  AppDelegate.m
//  TouchEventAndGesture
//
//  Created by Kenshin Cui on 14-3-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
// #import "AppDelegate.h"
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "KCApplication.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];
//    [self becomeFirstResponder];    [_window makeKeyAndVisible];    return YES;
} //-(void)remoteControlReceivedWithEvent:(UIEvent *)event{
//    NSLog(@"remote");
//} - (void)applicationWillResignActive:(UIApplication *)application {
   // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
   // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
} - (void)applicationDidEnterBackground:(UIApplication *)application {
   // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
   // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.    [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
} - (void)applicationWillEnterForeground:(UIApplication *)application {
   // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
} - (void)applicationDidBecomeActive:(UIApplication *)application {
   // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
} - (void)applicationWillTerminate:(UIApplication *)application {
   // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
} @end

2.在视图控制器中添加远程控制事件并音频播放进行控制

//
//  ViewController.m
//  RemoteEvent
//
//  Created by Kenshin Cui on 14-3-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
// #import "ViewController.h" @interface ViewController (){
   UIButton *_playButton;
   BOOL _isPlaying;
} @end @implementation ViewController - (void)viewDidLoad {
   [super viewDidLoad];    [self initLayout];
} -(BOOL)canBecomeFirstResponder{
   return NO;
} -(void)viewDidAppear:(BOOL)animated{
   [super viewDidAppear:animated];
   _player = [[AVPlayer alloc] initWithURL:[NSURL URLWithString:@"http://stream.jewishmusicstream.com:8000"]];    //[_player play];
   //_isPlaying=true;
} #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:@"wxl.jpg"];
   UIImageView *imageView=[[UIImageView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame];
   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);
   _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:@"playing_btn_pause_n.png"] forState:UIControlStateNormal];
       [_playButton setImage:[UIImage imageNamed:@"playing_btn_pause_h.png"] forState:UIControlStateHighlighted];
   }else{
       [_playButton setImage:[UIImage imageNamed:@"playing_btn_play_n.png"] forState:UIControlStateNormal];
       [_playButton setImage:[UIImage imageNamed:@"playing_btn_play_h.png"] forState:UIControlStateHighlighted];
   }
} -(void)btnClick:(UIButton *)btn{
   if (_isPlaying) {
       [_player pause];
   }else{
       [_player play];
   }
   _isPlaying=!_isPlaying;
   [self changeUIState];
}
@end

运行效果(真机截图):

注意:

为了模拟一个真实的播放器,程序中我们启用了后台运行模式,配置方法:在info.plist中添加UIBackgroundModes并且添加一个元素值为audio。

即使利用线控进行音频控制我们也无法监控到耳机增加音量、减小音量的按键操作(另外注意模拟器无法模拟远程事件,请使用真机调试)。

子事件的类型跟当前音频状态有直接关系,点击一次播放/暂停按钮究竟是【播放】还是【播放/暂停】状态切换要看当前音频处于什么状态,如果处于停止状态则点击一下是播放,如果处于暂停或播放状态点击一下是暂停和播放切换。

上面的程序已在真机调试通过,无论是线控还是点击应用按钮都可以控制播放或暂停。

iOS开发系列之远程控制事件的更多相关文章

  1. iOS开发系列之触摸事件

    基础知识 三类事件中触摸事件在iOS中是最常用的事件,这里我们首先介绍触摸事件. 在下面的例子中定义一个KCImage,它继承于UIView,在KCImage中指定一个图片作为背景.定义一个视图控制器 ...

  2. iOS开发系列之运动事件

    前面我们主要介绍了触摸事件以及由触摸事件引出的手势识别,下面我们简单介绍一下运动事件.在iOS中和运动相关的有三个事件:开始运动.结束运动.取消运动. 监听运动事件对于UI控件有个前提就是监听对象必须 ...

  3. iOS开发系列文章(持续更新……)

    iOS开发系列的文章,内容循序渐进,包含C语言.ObjC.iOS开发以及日后要写的游戏开发和Swift编程几部分内容.文章会持续更新,希望大家多多关注,如果文章对你有帮助请点赞支持,多谢! 为了方便大 ...

  4. iOS开发系列--App扩展开发

    概述 从iOS 8 开始Apple引入了扩展(Extension)用于增强系统应用服务和应用之间的交互.它的出现让自定义键盘.系统分享集成等这些依靠系统服务的开发变成了可能.WWDC 2016上众多更 ...

  5. iOS开发系列--通知与消息机制

    概述 在多数移动应用中任何时候都只能有一个应用程序处于活跃状态,如果其他应用此刻发生了一些用户感兴趣的那么通过通知机制就可以告诉用户此时发生的事情.iOS中通知机制又叫消息机制,其包括两类:一类是本地 ...

  6. iOS开发系列--网络开发

    概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博.微信等,这些应用本身可能采用iOS开发,但是所有的数据支撑都是基于后台网络服务器的.如今,网络编程越来越普遍,孤立的应用通常是没有生命力 ...

  7. iOS开发系列--让你的应用“动”起来

    --iOS核心动画 概览 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌.在这里你可以看到iOS中如何使用图层精简非交互式绘图,如何通过核心动画创建 ...

  8. iOS开发系列--并行开发其实很容易

    --多线程开发 概览 大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的, ...

  9. iOS开发系列--让你的应用“动”起来

    --iOS核心动画 概览 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌.在这里你可以看到iOS中如何使用图层精简非交互式绘图,如何通过核心动画创建 ...

随机推荐

  1. php基础知识【函数】(6)mysql数据库

    一.连接和关闭 1.mysql_connect('example.com:3307', 'root', '123456') --打开一个到 MySQL 服务器的非永久连接 2.mysql_pconne ...

  2. 技术大牛面试 http://www.itmian4.com/forum.php?mod=viewthread&tid=3824

    不久前,byvoid面阿里星计划的面试结果截图泄漏,引起无数IT屌丝的羡慕敬仰.看看这些牛人,NOI金牌,开源社区名人,三年级开始写Basic...在跪拜之余我们不禁要想,和这些牛人比,作为绝大部分技 ...

  3. Ubuntu14.02 Sublimte2安装

    $sudo add-apt-repository ppa:webupd8team/sublime-text-2 $sudo apt-get update $sudo apt-get install s ...

  4. how to use a xml_id in field domain

    "[('parent_id','child_of', %(other_module.xml_id)d)]"

  5. 【MySQL】SQL语法,between and 使用注意事项

    业务代码中有条查询学生姓名的sql: select stu_name from stu_info where stu_id between id_1 and id_2; 估计当时一时恍惚,拼接sql时 ...

  6. 工作总结:MFC自写排序算法(升序)

    最近一个需求里面需要实IP升序排序,用了qsort,结果是内部排序,甚至感觉排序结果不可预测性,于是自己写了一个外部排序. 需求如下:一个指针里面有N条记录,每条记录包含:IP,偏移地址,保留位,均占 ...

  7. BZOJ 1143 祭祀

    Description 在遥远的东方,有一个神秘的民族,自称Y族.他们世代居住在水面上,奉龙王为神.每逢重大庆典, Y族都会在水面上举办盛大的祭祀活动.我们可以把Y族居住地水系看成一个由岔口和河道组成 ...

  8. C#程序设计基础——运算符与表达式

    运算符就是完成操作的一系列符号,它主要包括算术运算符.赋值运算符.关系运算符.逻辑运算符.条件运算.位操作运算符和字符串运算符. 表达式就是运算符和操作数的组合,如a*b+1-c.表达式主要包括算术表 ...

  9. SIM卡基础知识

    一:了解Sim卡和GSM网络登录步骤的基本知识 (一)名词解释: SIM卡(Subscriber Identity Module),即用户识别卡,它是一张符合GSM规范的“智慧卡”,SIM卡有大小之分 ...

  10. 1.AJAX简介

    没有AJAX会怎么样?普通的ASP.Net每次执行服务端方法的时候都要刷新当前页面,比如实现显示服务器时间.每次都要刷新页面的坏处:页面刷新打断用户操作.速度慢.增加服务器的流量压力.如果没有AJAX ...