Контроли за възпроизвеждане на мултимедия с помощта на LockScreen и Център за управление в iOS с помощта на Swift

Ако някога сте използвали iPhone или iPad, за да слушате музика, вероятно сте виждали екрана Now Playing. Този екран ви показва заглавието и изпълнителя на текущо възпроизвежданата песен, както и контроли за пауза, пропускане и регулиране на силата на звука. Но Now Playing има нещо повече от това – това всъщност е мощен инструмент за контролиране на възпроизвеждането на вашата музика и също така е важна част от екосистемата на iOS.

В тази публикация ще разгледаме по-отблизо Now Playing в iOS, изследвайки неговите функции и възможности, както и някои от начините, по които може да се използва във вашите приложения.

Основи на Now Playing

Да започнем с основите. Когато възпроизвеждате музика на своя iPhone или iPad, можете да осъществите достъп до екрана „Сега се възпроизвежда“, като плъзнете надолу от горния десен ъгъл на екрана, за да получите достъп до Центъра за управление, който включва уиджет „Сега се възпроизвежда“, който ви показва текущата песен. Като алтернатива можете да видите NowPlaying на заключен екран.

Добре, сега разбираме Now Playing, как да го приложим? Е, можем да следваме тези стъпки, за да го внедрим във вашите собствени приложения.

  1. Настройка на AvAudioSession.
  2. Активиране на фонови режими за аудио.
  3. Настройка на дистанционните команди.
  4. Конфигуриране на контролите на NowPlaying и показване на информация.
  5. Обработка на грешки и обработка на прекъсвания.

Настройване на AvAudioSession

Няма да навлизам в много подробности при настройването на аудио сесията, можете да се обърнете към тази документация, тя много добре обяснява AVAudioSession.

// Get the singleton instance.
    let audioSession = AVAudioSession.sharedInstance()
    do {
        // Set the audio session category, mode, and options.
        try audioSession.setCategory(.playback, mode: .moviePlayback, options: [])
        try audioSession.setActive(true)
    } catch {
        print("Failed to set audio session category.")
    }

Активиране на фонови режими за аудио

Изберете целта на приложението си в Xcode и изберете раздела Възможности. В раздела Възможности задайте превключвателя за фонови режими на ВКЛ. и изберете опцията „Аудио, AirPlay и картина в картина“ в списъка с налични режими.

Настройка на дистанционните команди

Всяка отдалечена команда е MPRemoteCommand от рамката на Media Player, която отговаря на събития от отдалечена команда.

Примери за MPRemoteCommand — playCommand, pauseCommand, nextTrackCommand и др. (Справка)

Сега трябва да се регистрираме за различни дистанционни команди и да отговаряме съответно на всяка команда. За това можем да използваме споделения екземпляр на MPRemoteCommandCenter и да обработваме събития.

Предпочитам да използвам Enum, защото те са типове стойности и предлагат повече функционалност в Swift, което ги прави мощен инструмент за тази цел. Можем допълнително да използваме протокола CaseIterable, за да капсулираме различни видове отдалечени команди и също така да обработваме регистрирането на отдалечена команда в самия enum.

Нещо като това ..

enum NowPlayableCommand: CaseIterable {
    
    case play, pause, togglePlayPause,
         nextTrack, previousTrack,
         changePlaybackRate, changePlaybackPosition,
         skipForward, skipBackward,
         seekForward, seekBackward
}

// MARK:- MPRemoteCommand

extension NowPlayableCommand {
  var remoteCommand: MPRemoteCommand {
        
        let commandCenter = MPRemoteCommandCenter.shared()
        
        switch self {
        case .play:
            return commandCenter.playCommand
        case .pause:
            return commandCenter.pauseCommand
        case .togglePlayPause:
            return commandCenter.togglePlayPauseCommand
        case .nextTrack:
            return commandCenter.nextTrackCommand
        case .previousTrack:
            return commandCenter.previousTrackCommand
        case .changePlaybackRate:
            return commandCenter.changePlaybackRateCommand
        case .changePlaybackPosition:
            return commandCenter.changePlaybackPositionCommand
        case .skipForward:
            return commandCenter.skipForwardCommand
        case .skipBackward:
            return commandCenter.skipBackwardCommand
    
        case .seekForward:
            return commandCenter.seekForwardCommand
        case .seekBackward:
            return commandCenter.seekBackwardCommand
        }
    }
    // Adding Handler and accepting an escaping closure for event handling for a praticular remote command
    func addHandler(remoteCommandHandler: @escaping  (NowPlayableCommand, MPRemoteCommandEvent)->(MPRemoteCommandHandlerStatus)) {
        
        switch self {
        case .skipBackward:
            MPRemoteCommandCenter.shared().skipBackwardCommand.preferredIntervals = [10.0]
            
        case .skipForward:
            MPRemoteCommandCenter.shared().skipForwardCommand.preferredIntervals = [10.0]
            
        default:
            break
        }
        self.remoteCommand.addTarget { event in
            remoteCommandHandler(self,event)
        }
    }
}

Конфигуриране на контролите на NowPlaying и показване на информация

Сега ще създадем протокол, на който нашият клас видео плейър може да отговаря, нещо подобно..

protocol NowPlayable {
    var supportedNowPlayableCommands: [NowPlayableCommand] { get }
    
    func configureRemoteCommands(remoteCommandHandler: @escaping  (NowPlayableCommand, MPRemoteCommandEvent)->(MPRemoteCommandHandlerStatus))
    func handleRemoteCommand(for type: NowPlayableCommand, with event: MPRemoteCommandEvent)-> MPRemoteCommandHandlerStatus
    
    func handleInterruption(for type: NowPlayableInterruption)
    
    func handleNowPlayingItemChange()
    func handleNowPlayingItemPlaybackChange()
    
    func addNowPlayingObservers()
    
    func setNowPlayingInfo(with metadata: NowPlayableStaticMetadata)
    func setNowPlayingPlaybackInfo(with metadata: NowPlayableDynamicMetaData)
    
    func resetNowPlaying()
}

Поддържаните сега възпроизвеждани команди са дистанционните команди, които искате да показвате в Сега се възпроизвеждат и могат да отговарят на действия.

Функцията setNowPlayingInfo се използва за задаване на информация за текущо възпроизвеждане.
Тук използваме споделения екземпляр на MPNowPlayingInfoCenter и предаваме речник.

func setNowPlayingInfo(with metadata: NowPlayableStaticMetadata) {
        let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
        
        var nowPlayingInfo = [String:Any]()
        
        nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = metadata.isLive
        nowPlayingInfo[MPMediaItemPropertyTitle] = metadata.title
        nowPlayingInfo[MPMediaItemPropertyArtist] = metadata.artist
        nowPlayingInfo[MPMediaItemPropertyArtwork] = metadata.artworkImage
        nowPlayingInfo[MPMediaItemPropertyAlbumArtist] = metadata.albumArtist
        nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = metadata.albumTitle
        
        nowPlayingInfoCenter.nowPlayingInfo = nowPlayingInfo
    }

Споделеният екземпляр на MPNowPlayingInfoCenter има речник nowPlayingInfo. Можем да създадем и попълним метаданни във временен речник и да зададем речника nowPlayingInfo от този временен.

Тук настройвам статичните метаданни като произведение на изкуството, заглавие и т.н. За настройване на динамични метаданни като процент на играч, позиция за търсене и т.н. можем да използваме други ключове на речника. За повече ключове вижте тук.

Можем да извикаме setNowPlayingInfo при всяко възпроизвеждане на нов елемент за възпроизвеждане.
И също така ще видим, че трябва да извикаме setNowPlayingInfo многократно за синхронизиране на динамичните метаданни на плейъра като скорост на играча, позиция за търсене и т.н. с информация за nowPlaying.

Сега функцията configureRemoteCommands() ни позволява да се регистрираме за отдалечени команди и може да бъде реализирана нещо подобно.

func configureRemoteCommands(remoteCommandHandler: @escaping  (NowPlayableCommand, MPRemoteCommandEvent)->(MPRemoteCommandHandlerStatus)) {
        
        guard supportedNowPlayableCommands.count > 1 else {
            assertionFailure("Fatal error, atleast one remote command needs to be registered")
            return
        }
        
        supportedNowPlayableCommands.forEach { nowPlayableCommand in
            nowPlayableCommand.removeHandler()
            nowPlayableCommand.addHandler(remoteCommandHandler: remoteCommandHandler)
        }
        
    }

Използвам екраниращо затваряне, за да предам контрола или обработката на отдалечените командни събития до класа Video player, където вашият клас video player отговаря на NowPlaying. По този начин имаме ясен код и дефинирани отговорности.

В класа Video player, където отговаря на протокола NowPlayable, можем да внедрим

func handleRemoteCommand(for type: NowPlayableCommand,
                             with event: MPRemoteCommandEvent)

Тук можем да сравним какъв тип NowPlayableCommand има и въз основа на това можем да отговорим на промените на играчите. Например,

switch type {
        case .play :
            player.resume()
            
        case .pause:
            player.pause()

Имайте предвид, че не е необходимо ръчно да извикваме тази функция. Тъй като се регистрирахме за отдалечени команди (вижте NowPlayableCommand Enum), тази функция ще бъде извикана автоматично. Просто трябва да отговорим на промените от отдалечени команди.

И накрая, наблюдавайте видеоплейъра с помощта на наблюдатели и след това при всяка промяна на събитието на играча (като търсене, пауза и т.н.), трябва да извикаме setNowPlayingInfo(с динамичните метаданни), което допълнително ще създаде речник и ще премине го в MPNowPlayingInfoCenter споделен екземпляр nowPlaying речник. (Вижте горното изпълнение на функцията)

Заключителни бележки

Now Playing е мощен инструмент в iOS, предоставящ на потребителите удобен начин да контролират възпроизвеждането на музика. Чрез интегрирането на Now Playing в приложението ви можете да осигурите по-безпроблемно и интегрирано възпроизвеждане на музика за вашите потребители. Точно това направих, когато работих върху приложението Cricbuzz, което има повече от 100 милиона изтегляния. С добавянето на Now Playing позволихме на потребителите да имат повече контрол от заключения екран и обратната връзка, която получихме, беше изключително положителна. Потребителите харесаха възможността да контролират безпроблемно възпроизвеждането на музика, като същевременно са в крак с най-новите резултати и новини от крикет.

Ако работите върху приложение, което включва възпроизвеждане на музика, или просто искате да предоставите по-добро потребителско изживяване за вашите потребители, помислете за интегриране на Now Playing във вашето приложение. Това е мощен инструмент, който може да направи голяма разлика в начина, по който вашите потребители взаимодействат с вашето приложение.

Препратки за допълнителна помощ:

  1. https://medium.com/@varundudeja/showing-media-player-system-controls-on-notification-screen-in-ios-swift-4e27fbf73575
  2. https://developer.apple.com/documentation/mediaplayer/mpnowplayinginfocenter

Надяваме се, че ще намерите това за полезно. Ако това ви е харесало или имате отзиви или последващи въпроси, моля, коментирайте по-долу или се свържете с мен в Linkedin.

Благодарим, че прочетохте!