Я нашел одно решение для аналогичной проблемы:
- Я создал новый подкласс
AVPlayer
; - Добавлен наблюдатель в
currentItem
; - Переопределить fun c
observeValue
, где добавляется наблюдатель для текущего предмета, когда игрок достигает времени окончания;
Вот упрощенный пример:
import AVKit // for player
import Combine // for observing and adding as environmentObject
final class AudioPlayer: AVPlayer, ObservableObject {
var songDidEnd = PassthroughSubject<Void, Never>() // you can use it in some View with .onReceive function
override init() {
super.init()
registerObserves()
}
private func registerObserves() {
self.addObserver(self, forKeyPath: "currentItem", options: [.new], context: nil)
// example of using
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
// currentItem could be nil in the player. I add observer to exist item
if keyPath == "currentItem", let item = currentItem {
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item)
// another way, using Combine
var cancellable: AnyCancellable?
cancellable = NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime, object: item).sink { [weak self] _ in
self?.songDidEnd.send()
cancellable?.cancel()
}
}
// other observers
}
@objc private func playerDidFinishPlaying(_ notification: Notification) {
playNextSong() // my implementation, here you can just write: "self.pause()"
}
}
UPDATE : простой пример использования .onReceive
(будьте осторожны, я написал это без игровой площадки / Xcode, чтобы он мог содержать ошибки):
struct ContentView: View {
@EnvironmentObject var audioPlayer: AudioPlayer
@State private var someText: String = "song is playing"
var body: some View {
Text(someText)
.onReceive(self.audioPlayer.songDidEnd) { // maybe you need "_ in" here
self.handleSongDidEnd()
}
}
private func handleSongDidEnd() {
print("song did end")
withAnimation {
someText = "song paused"
}
}
}
О Combine
с AVPlayer
: вы можете посмотреть на мой вопрос , там вы увидите несколько способов наблюдать время воспроизведения и функциональность, чтобы перематывать время с помощью ползунка в SwiftUI
.
Я использую один экземпляр AudioPlayer
, управляющий игрой / приостановить функции или изменить currentItem
(что означает установку другой песни) следующим образом:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// other staff
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let homeView = ContentView()
.environmentObject(AudioPlayer())
// other staff of SceneDelegate
}
}