首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >延迟init() AVPlayer SwitUI

延迟init() AVPlayer SwitUI
EN

Stack Overflow用户
提问于 2021-06-14 19:29:15
回答 1查看 66关注 0票数 0

我发现OObject的代码可以作为基本音频播放器(带有滑块)使用,但是到目前为止,我可以在ContentView中使用它,如下所示:

代码语言:javascript
复制
@ObservedObject var player: AudioPlayerAV

let audioFileURL = URL(string: "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_5MG.mp3")


init(){
    
    var playerItem = AVPlayerItem(url: audioFileURL!)
    player = AudioPlayerAV(avPlayer: AVPlayer(playerItem: playerItem))
}

如果我有URL或本地路径,我可以使用它。我不知道如何推迟初始化,例如,我想在同一个ContentView中记录的元素上使用它。

代码语言:javascript
复制
//OObject

import AVFoundation
import Combine

let timeScale = CMTimeScale(1000)
let time = CMTime(seconds: 0.5, preferredTimescale: timeScale)

enum PlayerScrubState {
    case reset
    case scrubStarted
    case scrubEnded(TimeInterval)
}


final class AudioPlayerAV: NSObject, ObservableObject {

 
    @Published var displayTime: TimeInterval = 0
    @Published var observedTime: TimeInterval = 0
    @Published var itemDuration: TimeInterval = 0
    @Published var audioFinishedPlaying: Bool = false
    @Published var timeControlStatus: AVPlayer.TimeControlStatus = .paused
   
    
    fileprivate var itemDurationKVOPublisher: AnyCancellable!
    fileprivate var timeControlStatusKVOPublisher: AnyCancellable!
    fileprivate var avPlayer: AVPlayer
    fileprivate var periodicTimeObserver: Any?

    var scrubState: PlayerScrubState = .reset {
        didSet {
            switch scrubState {
            case .reset:
                return
            case .scrubStarted:
                return
            case .scrubEnded(let seekTime):
                avPlayer.seek(to: CMTime(seconds: seekTime, preferredTimescale: 1000))
            }
        }
    }

    init(avPlayer: AVPlayer) {
        self.avPlayer = avPlayer
        super.init()

        addPeriodicTimeObserver()
        addTimeControlStatusObserver()
        addItemDurationPublisher()
        
        NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: .main) { (_) in
            self.audioFinishedPlaying = true

        }
        
        
    }

    deinit {
        removePeriodicTimeObserver()
        timeControlStatusKVOPublisher.cancel()
        itemDurationKVOPublisher.cancel()
        
        NotificationCenter.default.removeObserver(self)
    }

    func play() {
        self.avPlayer.play()
    }

    func pause() {
        self.avPlayer.pause()
    }

    fileprivate func addPeriodicTimeObserver() {
        self.periodicTimeObserver = avPlayer.addPeriodicTimeObserver(forInterval: time, queue: .main) { [weak self] (time) in
            guard let self = self else { return }

            self.observedTime = time.seconds

            switch self.scrubState {
            case .reset:
                self.displayTime = time.seconds
            case .scrubStarted:
                break
            case .scrubEnded(let seekTime):
                self.scrubState = .reset
                self.displayTime = seekTime
            }
        }
    }

    fileprivate func removePeriodicTimeObserver() {
        guard let periodicTimeObserver = self.periodicTimeObserver else {
            return
        }
        avPlayer.removeTimeObserver(periodicTimeObserver)
        self.periodicTimeObserver = nil
    }

    fileprivate func addTimeControlStatusObserver() {
        timeControlStatusKVOPublisher = avPlayer
            .publisher(for: \.timeControlStatus)
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: { [weak self] (newStatus) in
                guard let self = self else { return }
                self.timeControlStatus = newStatus
                }
        )
    }

    fileprivate func addItemDurationPublisher() {
        itemDurationKVOPublisher = avPlayer
            .publisher(for: \.currentItem?.duration)
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: { [weak self] (newStatus) in
                guard let newStatus = newStatus,
                    let self = self else { return }
                self.itemDuration = newStatus.seconds
                }
        )
    }

}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-06-14 19:54:27

在非SwiftUI的情况下,我通常建议让player成为可选的,然后加载它,但是,正如您可能已经发现的那样,您不能使@ObservedObject@StateObject成为可选的。

我建议对AudioPlayerAV进行重构,以便它在与init不同的功能中完成重要的工作。这样,您就可以自由地在任何时候加载内容。

例如:

代码语言:javascript
复制
struct ContentView: View {
    @StateObject var player: AudioPlayerAV = AudioPlayerAV()
    @State private var urlText = "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_5MG.mp3"
    
    var body: some View {
        TextField("", text: $urlText)
        Button(action: {
            if let url = URL(string: urlText) {
                player.loadPlayer(avPlayer: AVPlayer(url: url))
                player.play()
            } else {
                print("not valid")
            }
        }) {
            Text("Load")
        }
    }
}

struct Response: Codable {
    // ... some fields here
}

let timeScale = CMTimeScale(1000)
let time = CMTime(seconds: 0.5, preferredTimescale: timeScale)

enum PlayerScrubState {
    case reset
    case scrubStarted
    case scrubEnded(TimeInterval)
}


final class AudioPlayerAV: NSObject, ObservableObject {
    @Published var displayTime: TimeInterval = 0
    @Published var observedTime: TimeInterval = 0
    @Published var itemDuration: TimeInterval = 0
    @Published var audioFinishedPlaying: Bool = false
    @Published var timeControlStatus: AVPlayer.TimeControlStatus = .paused
    
    
    fileprivate var itemDurationKVOPublisher: AnyCancellable?
    fileprivate var timeControlStatusKVOPublisher: AnyCancellable?
    fileprivate var avPlayer: AVPlayer?
    fileprivate var periodicTimeObserver: Any?
    
    var scrubState: PlayerScrubState = .reset {
        didSet {
            switch scrubState {
            case .reset:
                return
            case .scrubStarted:
                return
            case .scrubEnded(let seekTime):
                avPlayer?.seek(to: CMTime(seconds: seekTime, preferredTimescale: 1000))
            }
        }
    }
    
    func loadPlayer(avPlayer: AVPlayer) {
        removeObservers()
        self.avPlayer = avPlayer
        
        addPeriodicTimeObserver()
        addTimeControlStatusObserver()
        addItemDurationPublisher()
        
        NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: .main) { (_) in
            self.audioFinishedPlaying = true
            
        }
    }
    
    func removeObservers() {
        removePeriodicTimeObserver()
        timeControlStatusKVOPublisher?.cancel()
        itemDurationKVOPublisher?.cancel()
        
        NotificationCenter.default.removeObserver(self)
    }
    
    deinit {
        removeObservers()
    }
    
    func play() {
        self.avPlayer?.play()
    }
    
    func pause() {
        self.avPlayer?.pause()
    }
    
    fileprivate func addPeriodicTimeObserver() {
        self.periodicTimeObserver = avPlayer?.addPeriodicTimeObserver(forInterval: time, queue: .main) { [weak self] (time) in
            guard let self = self else { return }
            
            self.observedTime = time.seconds
            
            switch self.scrubState {
            case .reset:
                self.displayTime = time.seconds
            case .scrubStarted:
                break
            case .scrubEnded(let seekTime):
                self.scrubState = .reset
                self.displayTime = seekTime
            }
        }
    }
    
    fileprivate func removePeriodicTimeObserver() {
        guard let periodicTimeObserver = self.periodicTimeObserver else {
            return
        }
        avPlayer?.removeTimeObserver(periodicTimeObserver)
        self.periodicTimeObserver = nil
    }
    
    fileprivate func addTimeControlStatusObserver() {
        guard let avPlayer = avPlayer else {
            return
        }
        timeControlStatusKVOPublisher = avPlayer
            .publisher(for: \.timeControlStatus)
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: { [weak self] (newStatus) in
                guard let self = self else { return }
                self.timeControlStatus = newStatus
            }
            )
    }
    
    fileprivate func addItemDurationPublisher() {
        guard let avPlayer = avPlayer else {
            return
        }
        itemDurationKVOPublisher = avPlayer
            .publisher(for: \.currentItem?.duration)
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: { [weak self] (newStatus) in
                guard let newStatus = newStatus,
                      let self = self else { return }
                self.itemDuration = newStatus.seconds
            }
            )
    }
    
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67976166

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档