实时更新滑块值 AVAudioPlayer SwiftUI

Updating slider value in real time AVAudioPlayer SwiftUI

所以我有一个class调用的音频设置如下:

class audioSettings: ObservableObject {
    
    var audioPlayer: AVAudioPlayer?
    var playing = false
    var playValue: TimeInterval = 0.0
    var playerDuration: TimeInterval = 146
    var timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
    
    
    func playSound(sound: String, type: String) {
        if let path = Bundle.main.path(forResource: sound, ofType: type) {
            do {
                if playing == false {
                    if (audioPlayer == nil) {
                        
                        
                        audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
                        audioPlayer?.prepareToPlay()
                        
                        audioPlayer?.play()
                        playing = true
                    }
                    
                }
                if playing == false {
                    
                    audioPlayer?.play()
                    playing = true
                }
                
                
            } catch {
                print("Could not find and play the sound file.")
            }
        }
        
    }
    
    
    
    func stopSound() {
        //   if playing == true {
        audioPlayer?.stop()
        audioPlayer = nil
        playing = false
        playValue = 0.0
        
        
        //   }
    }
    
    func pauseSound() {
        if playing == true {
            audioPlayer?.pause()
            playing = false
            
            
        }
    }
    
    
    func changeSliderValue() {
        if playing == true {
            pauseSound()
            audioPlayer?.currentTime = playValue
            
        }
        
        if playing == false {
            
            
            audioPlayer?.play()
            playing = true
        }
    }
}

当我在视图中实现代码时,我成功播放了文件。我成功地停止了它,我也成功地移动了滑块并在音频中获得了新的位置。

我遇到困难的地方在于如何让滑块在播放音频时实时更新。如果我点击播放,在我点击暂停之前,滑块将不会执行任何操作。此时它将移动到正确的位置。

查看代码如下:

struct myExperienceFearChunk: View {
    
    
    @ObservedObject var audiosettings = audioSettings()
    
    
    var body: some View {
        
            
            HStack {
                Button(action: {
                    if (self.playButton == Image(systemName: "play.circle")) {
                        print("All Done")
                        self.audiosettings.playSound(sound: "audio file", type: "mp3")
                        self.audiosettings.timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
                        self.playButton = Image(systemName: "pause.circle")
                        
                    } else {
                        
                        self.audiosettings.pauseSound()
                        self.playButton = Image(systemName: "play.circle")
                        
                        
                    }

                    
                }) {
                    self.playButton
                        .foregroundColor(Color.white)
                        .font(.system(size: 44))
                }
                Button(action: {
                    print("All Done")
                    self.audiosettings.stopSound()
                    self.playButton = Image(systemName: "play.circle")
                    self.audiosettings.playValue = 0.0
                    
                }) {
                    self.stopButton
                        .foregroundColor(Color.white)
                        .font(.system(size: 44))
                    
                }
            }
            Slider(value: $audiosettings.playValue, in: TimeInterval(0.0)...audiosettings.playerDuration, onEditingChanged: { _ in
                self.audiosettings.changeSliderValue()
            })
                .onReceive(audiosettings.timer) { _ in
                    
                    if self.audiosettings.playing {
                        if let currentTime = self.audiosettings.audioPlayer?.currentTime {
                            self.audiosettings.playValue = currentTime
                            
                            if currentTime == TimeInterval(0.0) {
                                self.audiosettings.playing = false
                            }
                        }
                        
                    }
                    else {
                        self.audiosettings.playing = false
                        self.audiosettings.timer.upstream.connect().cancel()
                    }
            }
        }
    }
}

我知道 .onReceive 是我点击暂停时滑块更新的地方,因为没有它滑块根本不会更新,除非被拖动。

关于如何让滑块在播放音频时更新设置 playValue 的任何想法。

谢谢。

这里是固定代码。使用 Xcode 11.4 / iOS 13.4

测试

修复的主要内容是发布 @Published var playValue 以便观察到的对象有机会收到有关 audioSettings 中的变化的通知,从而更新 Slider... 其他是次要的.

class audioSettings: ObservableObject {
    
    var audioPlayer: AVAudioPlayer?
    var playing = false
    @Published var playValue: TimeInterval = 0.0
    var playerDuration: TimeInterval = 146
    var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    
    func playSound(sound: String, type: String) {
        if let path = Bundle.main.path(forResource: sound, ofType: type) {
            do {
                if playing == false {
                    if (audioPlayer == nil) {
                        
                        
                        audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
                        audioPlayer?.prepareToPlay()
                        
                        audioPlayer?.play()
                        playing = true
                    }
                    
                }
                if playing == false {
                    
                    audioPlayer?.play()
                    playing = true
                }
                
                
            } catch {
                print("Could not find and play the sound file.")
            }
        }
        
    }

    func stopSound() {
        //   if playing == true {
        audioPlayer?.stop()
        audioPlayer = nil
        playing = false
        playValue = 0.0
        //   }
    }
    
    func pauseSound() {
        if playing == true {
            audioPlayer?.pause()
            playing = false
        }
    }
    
    func changeSliderValue() {
        if playing == true {
            pauseSound()
            audioPlayer?.currentTime = playValue
            
        }
        
        if playing == false {
            audioPlayer?.play()
            playing = true
        }
    }
}

struct myExperienceFearChunk: View {
    @ObservedObject var audiosettings = audioSettings()
    @State private var playButton: Image = Image(systemName: "play.circle")
    
    var body: some View {
        
        VStack {
        HStack {
            Button(action: {
                if (self.playButton == Image(systemName: "play.circle")) {
                    print("All Done")
                    self.audiosettings.playSound(sound: "filename", type: "wav")
                    self.audiosettings.timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
                    self.playButton = Image(systemName: "pause.circle")
                    
                } else {
                    
                    self.audiosettings.pauseSound()
                    self.playButton = Image(systemName: "play.circle")
                }
            }) {
                self.playButton
                    .foregroundColor(Color.white)
                    .font(.system(size: 44))
            }
            Button(action: {
                print("All Done")
                self.audiosettings.stopSound()
                self.playButton = Image(systemName: "play.circle")
                self.audiosettings.playValue = 0.0
                
            }) {
                Image(systemName: "stop.circle")
                    .foregroundColor(Color.white)
                    .font(.system(size: 44))
            }
        }
        Slider(value: $audiosettings.playValue, in: TimeInterval(0.0)...audiosettings.playerDuration, onEditingChanged: { _ in
            self.audiosettings.changeSliderValue()
        })
            .onReceive(audiosettings.timer) { _ in
                
                if self.audiosettings.playing {
                    if let currentTime = self.audiosettings.audioPlayer?.currentTime {
                        self.audiosettings.playValue = currentTime
                        
                        if currentTime == TimeInterval(0.0) {
                            self.audiosettings.playing = false
                        }
                    }
                    
                }
                else {
                    self.audiosettings.playing = false
                    self.audiosettings.timer.upstream.connect().cancel()
                }
            }
        }
    }
}