Как заставить внешние кнопки iPhone работать на себя

от автора

Здравствуйте, дорогие читатели Хабра!

Уже довольно давно работаю фрилансером и иногда беру пару-тройку простеньких проектов за $100-200 для разгрузки мозга. В этот раз клиент попросил использовать внешние кнопки регулировки громкости в iPhone. Проблема состояла в том, что встроенного API для внешних кнопок в iOS не существует: до недавних пор использование хардверных элементов устройства, отличное от системного поведения, было запрещено. Поэтому различные приложения типа «Camera+» и «Camera Pro» никак не могли донести подобный функционал до пользователя. Однако, по счастливой случайности, в iOS 5 разработчики Apple сами начали использовать подобный подход к интерфейсу: сделать фотографию в системном приложении камеры теперь можно, нажав на клавишу увеличения громкости.

Как реализовать подобное поведение внешних клавиш в своем приложении, смотрите под катом. Исходники прилагаются в конце статьи.

Немного погуглив о задаче, можно наткнуться на открытое решение RBVolumeButtons, которое открывает аудио сессию и начинает слушать изменение громкости. В моем случае, этот класс неприятно влиял на работу камеры: активировав новую аудио сессию, мы прерывали аудиосессию камеры. Я решил собрать свой велосипед, подойдя к процессу со слегка иной стороны; написать отдельный класс NKVolumeButtons, скрывающий в себе весь необходимый функционал.

После короткого разбора полетов я решил использовать класс MPMusicPlayer из встроенного фреймворка MediaPlayer. В этом класе есть два сиглтона музыкального плеера: один для приложения и второй системный, общий для всего телефона. Мы будем слушать изменения громкости музыкального плеера для нашего приложения. Для этого добавим в метод инициализации объекта NKVolumeButtons немного кода:

Жми меня!

[[MPMusicPlayerController applicationMusicPlayer] addObserver:self forKeyPath:@"volume" options:NSKeyValueObservingOptionNew context:nil]; 

Все по канонам KVO: вместо собственного велосипеда мы используем старую добрую категорию, унаследованную от NSObject. Соответственно, нам нужен и обработчик события — когда параметр, который мы слушаем, изменится, нам нужно как-то принять эту информацию. Смело добавляем метод в NKVolumeButtons!

Жми меня!

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {     // Здесь мы можем проверить, тот ли keyPath мы получаем; но зачем? Ведь единственный параметр, который мы слушаем - это volume     [self checkVolumeButtons]; }  - (void)checkVolumeButtons {          // 1     float currentVolume = [[MPMusicPlayerController applicationMusicPlayer] volume];          // 2     if (currentVolume > 0.5) {         [self volumeUp];     } else if (currentVolume < 0.5) {         [self volumeDown];     }          // 3     [[MPMusicPlayerController applicationMusicPlayer] setVolume:0.5]; } 

Разберем код по-порядку:

  1. Получаем текущую громкость приложения
  2. Проверяем, увеличилась ли громкость, или уменьшилась
  3. Возвращаем громкость к исходному значению

Что за исходное значение? Объясняю: если пользователь зашел в наше приложение, а громкость на нуле? Тогда нажатия клавиши уменьшения громкости не будут иметь смысла — ничего работать не будет. Поэтому с самого начала нам нужно установить громкость на определенном уровне, от которого и будем плясать. Параметр volume может принимать значения от 0 до 1, так что мы выберем середину — 0.5. Добавим следующий код в инициализацию объекта NKVolumeButtons:

Жми меня!

[[MPMusicPlayerController applicationMusicPlayer] setVolume:0.5]; 

А теперь приступим к реализации методов volumeUp и volumeDown. Для удобства добавим два параметра классу NKVolumeButtons, доступных извне — upBlock и downBlock. Приведем файл NKVolumeButtons.h к следующему виду:

Жми меня!

typedef void (^ButtonBlock)();  #import <Foundation/Foundation.h>  @interface NKVolumeButtons : NSObject  @property (nonatomic, copy) ButtonBlock upBlock; @property (nonatomic, copy) ButtonBlock downBlock;  @end 

Здесь мы просто в удобном ключе добавили возможность устанавливать извне блоки кода, которые будут вызываться при нажатии клавиш регулировки громкости. Не стоит забывать и о синтезации наших параметров. Добавьте следующее в имплементацию NKVolumeButtons:

Жми меня!

@synthesize upBlock = _upBlock; @synthesize downBlock = _downBlock; 

И, конечно же, вызов наших блоков в нужное время:

Жми меня!

- (void)volumeUp {     // Всегда нужно проверять, получилось ли задать нужные параметры     if(self.upBlock) {         self.upBlock();     } }  - (void)volumeDown {     // Всегда нужно проверять, получилось ли задать нужные параметры     if(self.upBlock) {         self.upBlock();     } } 

А вот и еще одна проблемка! Каждый раз, когда мы изменяем громкость, на экране появляется индикатор громкости. Что же, воспользуемся готовым решением, которое уже было в RBVolumeButtons. На то он и opensource, чтобы помогать друг другу, не так ли? Добавьте следующий код в NKVolumeButtons.m:

Жми меня!

// Прячем индикатор громкости CGRect frame = CGRectMake(0, -100, 10, 0); UIView *volumeView = [[MPVolumeView alloc] initWithFrame:frame]; [volumeView sizeToFit]; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] addSubview:volumeView]; 

Вот и все! Нам остается только добавить этот класс в проект, инициализировать объект NKVolumeButtons и задать нужные блоки кода. Вот таким простым костылем решается проблема недостатка API внешних клавиш.

Спасибо за то, что дочитали до конца! Исходники доступны на гитхабе.

ссылка на оригинал статьи http://habrahabr.ru/post/182774/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *