Похоже, есть много решений для решения этой проблемы, но ни одно из этих решений не помогло мне. В настоящее время я использую Swift 5. У меня есть AVPlayer, воспроизводящий анимацию (которая зацикливается) в моем ViewController. Когда вызов поступает через CallKit, независимо от того, отвечаю я на вызов или отклоняю его, анимация, воспроизводимая AVPlayer, не возобновляется после обработки вызова. Кажется, что обработчик прерываний называется до прерывания, но обычно не вызывается после прерывания.
override func viewDidLoad() {
super.viewDidLoad()
prepareBGVideo()
...
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationWillEnterForeground(notification:)),
name: UIApplication.willEnterForegroundNotification,
object: nil)
...
}
func prepareBGVideo() {
guard let path = Bundle.main.path(forResource: "animation", ofType:"mp4") else {
print("video not found")
return
}
let item = AVPlayerItem(url: URL(fileURLWithPath: path))
avPlayer = AVPlayer(playerItem: item)
NotificationCenter.default.addObserver(self,
selector: #selector(loopVideoBG),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: item)
NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption(notification:)), name: AVAudioSession.interruptionNotification, object: nil)
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.backgroundColor = UIColor.black.cgColor
avPlayer.volume = 0
avPlayer.actionAtItemEnd = .none
avPlayer.play()
view.backgroundColor = .clear
avPlayerLayer.frame = view.layer.bounds
view.layer.insertSublayer(avPlayerLayer, at: 0)
avPlayerLayer.videoGravity = isIPAD ? AVLayerVideoGravity.resize : AVLayerVideoGravity.resizeAspectFill // Changed from AVLayerVideoGravity.resizeAspect to AVLayerVideoGravity.resize so that video fits iPad screen
NotificationCenter.default.addObserver(self,
selector: #selector(willEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil)
}
@objc func handleInterruption(notification: Notification) {
guard let info = notification.userInfo,
let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
if type == .began {
// Interruption began, take appropriate actions (save state, update user interface)
self.avPlayer.pause()
} else if type == .ended {
guard let optionsValue =
info[AVAudioSessionInterruptionOptionKey] as? UInt else {
return
}
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
// Interruption Ended - playback should resume
self.avPlayer.play()
}
}
}
/// Resume video while app wake up from background
@objc func willEnterForeground() {
avPlayer.seek(to: CMTime.zero)
JPUtility.shared.performOperation(0.1) {
self.avPlayer.play()
}
}
@objc func loopVideoBG() {
avPlayer.seek(to: CMTime.zero)
avPlayer.play()
}
Вот все решения, которые у меня есть пробовал:
- две секунды ожидания перед вызовом
self.avPlayer.play()
in if options.contains(.shouldResume){}
- Установка
AVAudioSession.sharedInstance().setActive
в ложь, когда начинается прерывание, а затем установка это не правда, когда прерывание заканчивается . Проблема с этим подходом заключается в том, что блок if interruption == .ended {}
не всегда вызывается, поэтому настройка setActive
не имела никакого эффекта. - Установка
AVAudioSession
категории воспроизведения на AVAudioSessionCategoryOptions.MixWithOthers
. В моей анимации все равно нет звука.
Я видел упоминания о возобновлении воспроизведения в applicationDidBecomeActive(_:)
, но некоторые советуют против этого. Будет ли это хорошей практикой?
Есть ли способ обеспечить выполнение блока else if type == .ended {}
? Или, возможно, обходной путь, который работает более надежно, чем наблюдение AVAudioSession.interruptionNotification
?