• iOS开发系列之远程控制事件


    在今天的文章中还剩下最后一类事件:远程控制,远程控制事件这里主要说的就是耳机线控操作。在前面的事件列表中,大家可以看到在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。
    
    即使利用线控进行音频控制我们也无法监控到耳机增加音量、减小音量的按键操作(另外注意模拟器无法模拟远程事件,请使用真机调试)。
    
    子事件的类型跟当前音频状态有直接关系,点击一次播放/暂停按钮究竟是【播放】还是【播放/暂停】状态切换要看当前音频处于什么状态,如果处于停止状态则点击一下是播放,如果处于暂停或播放状态点击一下是暂停和播放切换。
    
    上面的程序已在真机调试通过,无论是线控还是点击应用按钮都可以控制播放或暂停。
  • 相关阅读:
    Wireshark抓取网站用户名密码
    为ctf而生的web扫描器ctf-wscan
    windows下pip安装python模块时报错总结
    火绒剑获取IP位置
    shodan 搜寻公共摄像头
    肥猪流码农的逆袭之路(1)
    如何备份centos系统
    centos下nginx搭建drupal 8
    openstack学习笔记9-selfservice网络配置
    术语解释-什么是Overlay Network和Underlay Network
  • 原文地址:https://www.cnblogs.com/fengmin/p/5548793.html
Copyright © 2020-2023  润新知