在SwiftUI/AVPlayer中艰难应对NotificationCenter/Combine [英] Struggling with NotificationCenter/Combine in SwiftUI/AVPlayer

查看:35
本文介绍了在SwiftUI/AVPlayer中艰难应对NotificationCenter/Combine的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在项目播放结束时暂停AVPlayer。使用SwiftUI实现这一点的最佳方式是什么?我不太了解通知,在哪里声明它们,等等。有什么方法可以使用联合来实现这一点吗?样例代码将会非常棒!提前谢谢您。

更新:

在下面答案的帮助下,我成功地创建了一个类,它接受AVPlayer并在项目结束时发布通知。您可以通过以下方式订阅通知:

类:

import Combine
import AVFoundation

class PlayerFinishedObserver {

    let publisher = PassthroughSubject<Void, Never>()

    init(player: AVPlayer) {
        let item = player.currentItem

        var cancellable: AnyCancellable?
        cancellable = NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime, object: item).sink { [weak self] change in
            self?.publisher.send()
            print("Gotcha")
            cancellable?.cancel()
        }
    }
}

添加到您的结构:

let finishedObserver: PlayerFinishedObserver

订阅某些视图:

.onReceive(finishedObserver.publisher) {
                print("Gotcha!")
            }

推荐答案

我为类似问题找到了一个解决方案:

  1. 我创建了AVPlayer
  2. 的新子类
  3. 将观察者添加到currentItem
  4. 覆盖函数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的简单示例(请注意,我编写它时没有使用playround/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"
        }
    }

}

关于CombineAVPlayer:您可以查看我的question,在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
    }
}

这篇关于在SwiftUI/AVPlayer中艰难应对NotificationCenter/Combine的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆