再生ロック画面通知バー表示#
背景#
音声を再生する際、通知画面に表示され、音声再生を制御できることを望んでいます。以前の要件では、バックグラウンドに入ると再生が一時停止していたため、通知画面を開くたびに再生が一時停止し、音楽プレーヤーのような効果を見ることができませんでした。その後、バックグラウンドに入ると一時停止するコードを削除すると、通知画面にプレーヤーが表示されるようになりましたが、制御はできず、進行状況も表示されませんでした。
実装#
バックグラウンド再生をサポート#
まず、APP がバックグラウンド再生をサポートする必要があります。つまり、一方でバックグラウンドに入ると再生が一時停止するコードロジックを削除し、もう一方で Target -> Signing & Capabilities で Background Modes を追加し、Audio, AirPlay, and Picture in Picture を有効にします。画像は以下の通りです:

AVAudioSessionを設定する際、再生前に実際のニーズに応じて設定し、再生後に閉じます。
AVAudioSessionCategoryタイプ
| カテゴリタイプ | "サイレント" またはロック画面時にサイレントになるか | 他のサポートされているアプリと混合再生できるか | バックグラウンドをサポートするか | シーンの例 |
|---|---|---|---|---|
| AVAudioSessionCategoryAmbient | はい | はい | いいえ | アプリのバックグラウンド音に一般的に使用されます。例えば、ゲームをプレイしながら音楽を聴くことができます。 |
| AVAudioSessionCategorySoloAmbient | はい | いいえ | いいえ | 同様にバックグラウンド音ですが、ゲームをプレイする際に音楽を聴きたくないシーンで使用されます。 |
| AVAudioSessionCategoryPlayback | いいえ | デフォルトではできませんが、サポート可能です | はい | 音楽再生、ロック画面時にも音楽を聴くことができます。 |
| AVAudioSessionCategoryRecord | いいえ | いいえ、録音のみ | はい | 録音機、録音中は他の音楽を再生できません。 |
| AVAudioSessionCategoryPlayAndRecord | いいえ | デフォルトでは可能で、録音も再生もできます | はい | 再生しながら録音する、例えば VOIP のようなシーン。 |
| AVAudioSessionCategoryAudioProcessing | いいえ | いいえ、ハードウェアで音声をデコードし、再生や録音はできません | はい | 音声フォーマット処理に使用されます。 |
| AVAudioSessionCategoryMultiRoute | いいえ | はい | いいえ | ヘッドフォン、USB デバイスで同時に再生。 |
AVAudioSessionCategoryOptionタイプ
| カテゴリオプションタイプ | 説明 | 適用カテゴリ |
|---|---|---|
| AVAudioSessionCategoryOptionMixWithOthers | 他のアプリと混合再生をサポート | AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryPlayback、AVAudioSessionCategoryMultiRoute |
| AVAudioSessionCategoryOptionDuckOthers | 他のアプリの音声音量を下げ、本アプリの音量を強調 | AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryPlayback、AVAudioSessionCategoryMultiRoute |
| AVAudioSessionCategoryOptionAllowBluetooth | Bluetooth 音声入力をサポート | AVAudioSessionCategoryRecord、AVAudioSessionCategoryPlayAndRecord |
| AVAudioSessionCategoryOptionDefaultToSpeaker | デフォルトの出力音声をスピーカーに設定 | AVAudioSessionCategoryPlayAndRecord |
| AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | アプリが音声再生を使用することがあり、再生中に他のアプリの音声を停止 | AVAudioSessionCategoryPlayback、AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryMultiRoute |
| AVAudioSessionCategoryOptionAllowBluetoothA2DP | ステレオ Bluetooth をサポート | AVAudioSessionCategoryPlayAndRecord |
| AVAudioSessionCategoryOptionAllowAirPlay | AirPlay デバイスをサポート | AVAudioSessionCategoryPlayAndRecord |
func setupAudioSession() {
do {
// .notifyOthersOnDeactivationを設定します。Activeがfalseのときに有効で、システムに本アプリの再生が終了したことを通知し、他のアプリの再生を続けることができます。
try AVAudioSession.sharedInstance().setActive(true, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation)
// 実際のニーズに応じて異なるカテゴリを切り替えます。
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: AVAudioSession.CategoryOptions.duckOthers)
} catch {
print("AudioSessionの設定エラー: %@", error)
}
}
ロック画面通知バー表示#
APP がバックグラウンド再生をサポートすると、通知バーに表示されるようになりますが、再生中は進行状況がなく、タイトルや画像もなく、アプリの名前と小さなアイコンだけが表示されます。これらの情報を変更するためのコードは以下の通りです:
#import <MediaPlayer/MPNowPlayingInfoCenter.h>
#import <MediaPlayer/MPRemoteCommandCenter.h>
#import <MediaPlayer/MPRemoteCommand.h>
#import <MediaPlayer/MPMediaItem.h>
// 通知バー表示を更新
- (void)updateNowPlaingInfo {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
// 曲のタイトルを設定
[dict setValue:@"タイトル" forKey:MPMediaItemPropertyTitle];
// アーティスト名を設定
[dict setValue:@"アーティスト" forKey:MPMediaItemPropertyArtist];
// アルバム名を設定
[dict setValue:@"アルバムタイトル" forKey:MPMediaItemPropertyAlbumTitle];
// 表示する画像を設定
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:ArtImage];
[dict setValue:artwork forKey:MPMediaItemPropertyArtwork];
// 曲の長さを設定
NSTimeInterval duration = self.player.duration;
[dict setValue:[NSNumber numberWithDouble:duration] forKey:MPMediaItemPropertyPlaybackDuration];
// すでに再生された時間を設定
NSTimeInterval currentTime = self.player.currentTime;
[dict setValue:[NSNumber numberWithDouble:currentTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
// 再生速度を設定
[dict setValue:@(1.0) forKey:MPNowPlayingInfoPropertyPlaybackRate];
// 更新
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
}
再生が完了した後、通知バーに表示しないようにするには、以下のように設定します。
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:@{}];
通知バーで再生を制御するために、一時停止、前の曲、次の曲を設定するには、MPRemoteCommandCenterのプロパティを設定して、対応する機能を有効にします。イベントの処理には 2 つの方法があります:
- 方法 1:
remoteControlReceivedWithEvent:メソッドを使用して、対応するイベントに応答します。 - 方法 2:
MPRemoteCommandCenterのCommandにaddTargetを使用して、対応するイベントを処理します。
通知バーの対応機能を有効にするためのコードは以下の通りです:
// AppDelegate内、または対応する再生コントローラー内で、システム制御イベントの受信を開始します。
// システム制御イベントを受信
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
- (void)setupCommandCenter {
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.playCommand removeTarget:self];
[commandCenter.pauseCommand removeTarget:self];
// 前の曲、次の曲を無効にします
commandCenter.previousTrackCommand.enabled = NO;
commandCenter.nextTrackCommand.enabled = NO;
// 再生
commandCenter.playCommand.enabled = YES;
// 一時停止
commandCenter.pauseCommand.enabled = YES;
// 再生と一時停止(ヘッドフォン制御)
commandCenter.togglePlayPauseCommand.enabled = NO;
// 進行状況をドラッグ
commandCenter.changePlaybackPositionCommand.enable = YES;
}
イベント処理方法 1 のコードは以下の通りです:
// リモートイベントに応答
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl) {
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
{
NSLog(@"リモートコントロールイベント: 再生");
}
break;
case UIEventSubtypeRemoteControlPause:
{
NSLog(@"リモートコントロールイベント: 一時停止");
}
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
NSLog(@"ヘッドフォン制御:一時停止||再生");
break;
case UIEventSubtypeRemoteControlNextTrack:
{
NSLog(@"リモートコントロールイベント: 次の曲");
}
break;
case UIEventSubtypeRemoteControlPreviousTrack:
{
NSLog(@"リモートコントロールイベント: 前の曲");
}
break;
default:
break;
}
}
}
イベント処理方法 2 のコードは以下の通りです:
- (void)setupCommandCenter {
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
// 再生
[commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
NSLog(@"再生");
return MPRemoteCommandHandlerStatusSuccess;
}];
// 一時停止
[commandCenter.pauseCommand addTarget:self action:@selector(handlePauseCommand:)];
// 進行状況をドラッグ
[commandCenter.changePlaybackPositionCommand addTarget:self action:@selector(handlePlaybackPositionCommand:)];
}
- (MPRemoteCommandHandlerStatus):(id)sender {
NSLog(@"一時停止");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)handlePlaybackPositionCommand:
(MPChangePlaybackPositionCommandEvent *) event
{
[self.palyer seekToTime:CMTimeMakeWithSeconds(event.positionTime, 1)];
NSLog(@"changePlaybackPosition to %f", event.positionTime);
return MPRemoteCommandHandlerStatusSuccess;
}
問題#
#
beginReceivingRemoteControlEventsを追加しない場合、通知バーは表示されるか、2 つの方法の処理に影響するか
イベント処理方法 2 の応答が 2 回走る
カスタム再生の進行状況と通知バーの進行状況が一致しない