使用 AVAudioPlayer 进行 UISlider 更新

问题描述 投票:0回答:4

我正在制作一个简单的音频播放器应用程序,它允许用户使用 UISlider 更改他们在曲目中的位置。

它不做什么:滑块不会根据曲目的进度进行更新。我在这里发现了一个类似的问题,第二个答案响应于

AVAudioPlayer
——建议使用
currentTime
来更新滑块。我想尝试这样做。

这是我的代码:

import UIKit
import AVFoundation

class MusicViewController: UIViewController {

    ....

    @IBOutlet var Slider: UISlider!

    @IBAction func changeAudioTime(_ sender: Any) {
        Slider.maximumValue = Float(audioPlayer.duration)
        audioPlayer.stop()
        audioPlayer.currentTime = TimeInterval(Slider.value)
        audioPlayer.prepareToPlay()
        audioPlayer.play()
    }

    func updateSlider(){

        Slider.value = Float(audioPlayer.currentTime)
        print("Changing works")
    }

第一个功能运行良好——用户可以更改他们所在的位置。但是,对我来说第二个函数应该根据 AudioPlayer 的

Slider.value
更改
currentTime
。它不会这样做。

它也没有打印,所以我相信它没有被触发。

我是 Swift 新手,所以我可能错过了一些东西。有任何想法吗?

ios swift swift3 avaudioplayer
4个回答
3
投票

测试代码:-

添加定时器变量

var timer: Timer?

在开始播放音频的函数中:

    slidderrr.value = 0.0
    slidderrr.maximumValue = Float((player?.duration)!)
    audioPlayer.play()
    timer = Timer.scheduledTimer(timeInterval: 0.0001, target: self, selector: #selector(self.updateSlider), userInfo: nil, repeats: true)

当您不想更新滑块时,不要忘记使计时器无效

func stopPlayer() {
    player?.stop()
    timer?.invalidate()
    print("Player and timer stopped")
  }

2
投票

您需要设置一个

CADisplayLink
并在每次调用其选择器时更新滑块的位置。以下是整个
AVAudioPlayer
-
UISlider
绑定的完成方式:

// 1
@IBOutlet weak var slider: UISlider! {
    didSet {
        slider.isContinuous = false

        slider.addTarget(self, action: #selector(didBeginDraggingSlider), for: .touchDown)
        slider.addTarget(self, action: #selector(didEndDraggingSlider), for: .valueChanged)
    }
}

// 2
var player: AVAudioPlayer

// 3
lazy var displayLink: CADisplayLink = CADisplayLink(target: self, selector: #selector(updatePlaybackStatus))

// 4
func startUpdatingPlaybackStatus() {
    displayLink.add(to: .main, forMode: .common)
}

func stopUpdatingPlaybackStatus() {
    displayLink.invalidate()
}

// 5
@objc
func updatePlaybackStatus() {
    let playbackProgress = Float(player.currentTime / player.duration)

    slider.setValue(playbackProgress, animated: true)
}

// 6
@objc
func didBeginDraggingSlider() {
    displayLink.isPaused = true
}

@objc
func didEndDraggingSlider() {
    let newPosition = player.duration * Double(slider.value)
    player.currentTime = newPosition

    displayLink.isPaused = false
}
  1. UISlider 的出口。确保其最小值和最大值分别设置为 0 和 1,并为其
    touchDown
    valueChanged
    事件添加操作,以便在用户分别开始和结束拖动滑块时收到通知。另外,将其
    isContinuous
    属性设置为
    false
    ,这样只有当用户释放滑块时才会报告
    valueChanged
    事件。此设置也可以使用 Interface Builder 和 IBActions 完成;
  2. 对 AVAudioPlayer 的引用,只是为了清楚起见;
  3. 存储 CADisplayLink 实例的属性。由于我们使用
    self
    作为目标,因此CADisplayLink的初始化应该在初始化父类之后进行,以便能够正确引用它。为此,我让它变得懒惰;
  4. 启动和停止更新 UI 的方法。当播放状态发生变化时,您应该调用他们。
  5. 屏幕刷新时调用的方法,更新UI的逻辑就存放在这里。
  6. 以相当简单的逻辑处理滑块事件的方法 - 当用户开始拖动滑块时暂停显示链接,因此其值在用户交互期间不会更新;将音频搜索到所选位置,并在用户释放滑块时恢复显示链接。

来源 - 我的博客文章


0
投票

你尝试过吗

func setValue(Float, animated: Bool)
Sets the slider’s current value, allowing you to animate the change visually.

所以类似

slider.setValue(index, animated: true)

可以在这里找到

https://developer.apple.com/documentation/uikit/uislider

让我知道这是否有效!


0
投票

Apple 建议的解决方案是将 periodicTimeObserver 添加到 AVPlayer 对象。

代码:

private(set) var duration: TimeInterval = 0.0
private(set) var currentTime: TimeInterval = 0.0

private let player = AVPlayer()
private var timeObserver: Any?


/// Adds an observer of the player timing.
private func addPeriodicTimeObserver() {
    // Create a 0.5 second interval time.
    let interval = CMTime(value: 1, timescale: 2)
    timeObserver = player.addPeriodicTimeObserver(forInterval: interval,
                                                  queue: .main) { [weak self] time in
        guard let self else { return }
        // Update the currentTime and duration values.
        // Update your slider or time-elapsed label values
        currentTime = time.seconds
        duration = player.currentItem?.duration.seconds ?? 0.0
    }
}


/// Removes the time observer from the player.
private func removePeriodicTimeObserver() {
    guard let timeObserver else { return }
    player.removeTimeObserver(timeObserver)
    self.timeObserver = nil
}

如果您想了解更多信息,请参阅Apple 的原始文档

© www.soinside.com 2019 - 2024. All rights reserved.