如何使用Swift中的AVPlayer跟踪歌曲播放结束时间

35

如何用Swift中的AVPlayer最简单的方法跟踪歌曲何时播放完毕?

AVPlayer有没有在播放结束时调用的函数,还是我应该将计时器与AVPlayer类引用组合使用?


7
不要混淆AVPlayer和AVAudioPlayer - 下面的一些答案是针对AVAudioPlayer的。 - Fattie
8个回答

71

像这样的东西是可行的:

func play(url: NSURL) {
    let item = AVPlayerItem(URL: url)

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerDidFinishPlaying:", name: AVPlayerItemDidPlayToEndTimeNotification, object: item)

    let player = AVPlayer(playerItem: item)
    player.play()
}

func playerDidFinishPlaying(note: NSNotification) {
    // Your code here
}

不要忘记在完成时或在deinit中删除观察者!


1
当我测试它时,我遇到了这个错误:“终止应用程序,原因是未捕获异常'NSInvalidArgumentException',原因是:'-[myApp.MyViewController playerDidFinishPlaying]:发送给实例的未识别选择器”。你能帮我吗? - Ne AS
@Llg 在 "playerDidFinishPlaying:" 中忘记了冒号。 - LightMan

21

你需要创建一个实现 AVAudioPlayerDelegate 协议的对象,并将其用作 AVAudioPlayer 对象的代理。然后将它们链接在一起,例如:

audioPlayer = try! AVAudioPlayer(contentsOf: audioFileUrl)
audioPlayer.delegate = self

代理可以实现响应某些事件的方法。当音频播放完成时,此方法会触发:

func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
    // ...
}

12
他正在使用 AVPlayer 而不是 AVAudioPlayer。 - Edward
不要忘记添加 class ViewController: AVAudioPlayerDelegate { - Hope
1
非常好的答案!我正在使用AVAudioPlayer,代理工作得很好。 - multitudes
我也在使用AVAudioPlayer,并且使用了代理。接下来是下一个音频问题... - Rethunk

10

适用于Swift 4.2

func play(url: URL) {
    let item = AVPlayerItem(url: url)
    NotificationCenter.default.addObserver(self, selector: #selector(self.playerDidFinishPlaying(sender:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item)

    let player = AVPlayer(playerItem: item)
    player.play() 
}

@objc func playerDidFinishPlaying(sender: Notification) {
    // Your code here
}

7

Swift 3 的另一个版本

NotificationCenter.default.addObserver(self, selector: #selector(self.playerDidFinishPlaying(sender:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item)

func playerDidFinishPlaying(sender: Notification) {

    // Do Something
}

Mike Carpenter,我尝试了您的解决方案,但无法使其工作。请查看下一个“answer”的代码。 - Mason Ballowe
你收到了什么错误信息?我在几个项目中都使用这段代码,它可以编译并且没有任何问题地执行。 - Mike Carpenter

6
import AVFoundation

var AVPlayerCustom:AVAudioPlayer = AVAudioPlayer()


class PlayerModule: NSObject, AVAudioPlayerDelegate {

    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        print("Finish")
    }

    func playWithData(data: Data, proc: Int) {
        //print(data)

        do {

            AVPlayerCustom = try AVAudioPlayer(data: data)

            AVPlayerCustom.delegate = self as! AVAudioPlayerDelegate

            AVPlayerCustom.prepareToPlay()
            AVPlayerCustom.play()


        }
        catch {
            print("error1")
        }
    }
}

4

这里提供了一个更完整的解决方案:

import UIKit
import AVFoundation
import MediaPlayer


class ViewController: UIViewController,AVAudioPlayerDelegate {

    var player: AVAudioPlayer = AVAudioPlayer()

    @IBAction func play(_ sender: UIButton) {
        player.play()
        player.currentTime=14*60-10
        print(player.currentTime)
    }
    @IBAction func pause(_ sender: UIButton) {
        player.pause()
    }
    @IBAction func replay(_ sender: UIButton) {
        player.currentTime=0
    }


    override func viewDidLoad() {
        super.viewDidLoad()
        do{
            let audioPath = Bundle.main.path(forResource: "elon", ofType: "mp3")
            player = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: audioPath!))
            player.prepareToPlay()
            player.delegate = self
        }
        catch{
            print(error)
        }
    }

    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool){
        print(flag)
        print("here")
        if flag == true{

        }
    }


}

3

对于Swift3,您需要进行以下更改:

func play(url: NSURL) {
    let item = AVPlayerItem(URL: url)
    NotificationCenter.default.addObserver(self,selector:Selector("playerDidFinishPlaying"), name:  NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item)

    let player = AVPlayer(playerItem: item)
    player.play() 
}

func playerDidFinishPlaying() {
// Your code here
} 

-1

SwiftUI

在SwiftUI中,我无法使用@objc函数或委托。@objc公开的函数不能在结构体中使用。一种方法是创建一个类,或者更简单的是使用AVAudioPlayer实例属性isPlaying。请参见Apple文档

这是我用来检查音频是否已经播放完毕以相应地更新播放/暂停按钮的代码。首先,我将值保存在@State属性中:

@State private var playing: Bool = false

然后在onAppear中,我使用一个定时器来检查播放器是否仍在播放。无论如何,我都需要这个定时器来更新音频的进度。

.onAppear {
    [...]
    Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
        if !audioPlayer.isPlaying {
            playing = false
        }
    }
}

因此,当曲目播放完毕时,播放/暂停按钮会自动更新

Button(action: {
    if audioPlayer.isPlaying {
        playing = false
        self.audioPlayer.pause()
    } else if !audioPlayer.isPlaying {
        playing = true
        self.audioPlayer.play()
    }
}) {
Image(systemName: playing ? "pause.circle.fill" : "play.circle.fill")
    .resizable()
    .frame(width: 50, height: 50)
    .aspectRatio(contentMode: .fit)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接