【问题标题】:Postpone init() AVPlayer SwitUI推迟 init() AVPlayer SwiftUI
【发布时间】:2021-08-30 17:00:54
【问题描述】:

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

@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 中记录的元素上使用它。

//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
                }
        )
    }

}

【问题讨论】:

    标签: swiftui avplayer init


    【解决方案1】:

    在非 SwiftUI 的情况下,我通常建议将 player 设为可选项并稍后加载,但您可能已经发现,不能将 @ObservedObject@StateObject 设为可选项。

    我建议重构AudioPlayerAV,以便它在与init 不同的函数中完成重要工作。这样,您就可以在任何时候随意加载内容。

    例如:

    
    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
                }
                )
        }
        
    }
    

    【讨论】:

    • 谢谢你 jnpdx ,我刚刚想出了别的东西,如果你能告诉我它是否正确的话。 Init() 被替换为 override init(){ super.init) ) 。创建并初始化 func forceInit(avPayer : AVplayer)( self.avPlayer = avPlayer playerHasBeenINit = true addPeriodicTimeObserver() addTimeControlStatusObserver() addItemDurationPublisher() NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: .main ) { (_) in self.audioFinishedPlaying = true }
    • 如果okayerHAsBeen init = true,我会删除观察者。基本上我不会在创建类时初始化播放器,我会等待合适的时机
    • 在评论中阅读未格式化的代码非常困难,但您的解决方案基本上看起来像我的。不过,不需要 init 来调用 super.init
    • 感谢一切!
    猜你喜欢
    • 2020-09-15
    • 1970-01-01
    • 2023-03-09
    • 2020-08-10
    • 2020-03-05
    • 1970-01-01
    • 2017-07-05
    • 2020-10-20
    • 1970-01-01
    相关资源
    最近更新 更多