Search code examples
iosswiftuitableviewavaudioplayer

How to manage the AVAudioPlayer isPlaying in each tableviewCell?


I have a tableview and have multiple cells in tableView.
Each cell has an item of AVAudioPlayer
And I face a problem.
I don't know how to manage the AVAudioPlayer.
When I play first AVAudioPlayer, and then play second AVAudioPlayer, the sound will overlap.
How to stop first AVAudioPlayer in my customized cell, and play second AVAudioPlayer?
Thanks.

This is my customized cell:

class TableViewCell: UITableViewCell {

@IBOutlet weak var myImageView: UIImageView!
@IBOutlet weak var myChatBubbleView: UIView!
@IBOutlet weak var myDateLabel: UILabel!
@IBOutlet weak var mySecondLabel: UILabel!
@IBOutlet weak var myRecordPlayerBtn: MenuButton!
private var timer:Timer?   
private var elapsedTimeInSecond:Int = 0
var audioPlayer:AVAudioPlayer?
var message:ChatroomMessage?
var chatroomId:String = ""
var delegate:PlayRecordDelegate?

override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code

    self.backgroundColor = defaultBackgroundColor
    self.tintColor = defaultChatroomCheckButtonColor

    myImageView.layer.masksToBounds = true 
    myImageView.layer.cornerRadius = defaultIconRadius

    myChatBubbleView.backgroundColor = defaultChatGreenBubbleColor
    myChatBubbleView.layer.cornerRadius = defaultButtonRadius

    myDateLabel.textColor = defaultChatTimeColor

    mySecondLabel.textColor = defaultChatTimeColor
    mySecondLabel.isHidden = true

    myRecordPlayerBtn.imageView?.animationDuration = 1
    myRecordPlayerBtn.imageView?.animationImages =  [
    UIImage(named: "img_myRocordPlaying1")!,
    UIImage(named: "img_myRocordPlaying2")!,
    UIImage(named: "img_myRocordPlaying3")!
    ]
}

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    // Configure the view for the selected state
}

func loadByMessage(_ message:ChatroomMessage, chatroomId:String) {
    self.message = message
    self.chatroomId = chatroomId

    myRecordPlayerBtn.addTarget(self, action: #selector(recordPlay), for: .touchUpInside)
}

func resetRecordAnimation() {
    self.myRecordPlayerBtn.imageView!.stopAnimating()
    self.myRecordPlayerBtn.isSelected = false
}

func recordPlay(_ sender: UIButton) {

    self.myRecordPlayerBtn.imageView?.startAnimating()

    let documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("\(chatroomId)/Record/")

    let fileName = message?.content.substring(from: 62)
    let fileURL = documentsDirectoryURL.appendingPathComponent(fileName!)
    if FileManager.default.fileExists(atPath: fileURL.path) {

        let asset = AVURLAsset(url: URL(fileURLWithPath: fileURL.path), options: nil)
        let audioDuration = asset.duration
        let audioDurationSeconds = CMTimeGetSeconds(audioDuration)
        self.elapsedTimeInSecond = Int(audioDurationSeconds)

        if audioPlayer?.isPlaying == true {

            audioPlayer?.stop()

            DispatchQueue.main.async {
                self.resetTimer(second: self.elapsedTimeInSecond)
                self.startTimer()
            }

        }

        updateTimeLabel()
        startTimer()

        audioPlayer = try? AVAudioPlayer(contentsOf: fileURL)
        audioPlayer?.delegate = self
        audioPlayer?.play()

    }else{
        //don't have file in local
        let recordUrl = URL(string: (message?.content)!)
        URLSession.shared.downloadTask(with: recordUrl!, completionHandler: { (location, response, error) in

            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let mimeType = response?.mimeType, mimeType.hasPrefix("audio"),
                let location = location, error == nil
                else { return }
            do {
                try FileManager.default.moveItem(at: location, to: fileURL)

                let asset = AVURLAsset(url: URL(fileURLWithPath: fileURL.path), options: nil)
                let audioDuration = asset.duration
                let audioDurationSeconds = CMTimeGetSeconds(audioDuration)

                self.elapsedTimeInSecond = Int(audioDurationSeconds)

                DispatchQueue.main.async {
                    self.updateTimeLabel()
                    self.startTimer()
                }

                self.audioPlayer = try? AVAudioPlayer(contentsOf: fileURL)
                self.audioPlayer?.delegate = self
                self.audioPlayer?.play()

            } catch {
                print(error)
            }
        }).resume()
    }
}

func startTimer() {

    timer?.invalidate()
    timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { (timer) in
        self.elapsedTimeInSecond -= 1
        self.updateTimeLabel()
    })
}

func resetTimer(second:Int) {
    timer?.invalidate()
    elapsedTimeInSecond = second
    updateTimeLabel()
}

func updateTimeLabel() {

    let seconds = elapsedTimeInSecond % 60
    let minutes = (elapsedTimeInSecond/60) % 60

    mySecondLabel.isHidden = false
    mySecondLabel.text = String(format: "%02d:%02d", minutes,seconds)
}
}

extension TableViewCell:AVAudioPlayerDelegate {

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

    let documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("\(Id)/Record/")
    let fileName = message?.content.substring(from: 62)
    let fileURL = documentsDirectoryURL.appendingPathComponent(fileName!)

    if FileManager.default.fileExists(atPath: fileURL.path) {

        let asset = AVURLAsset(url: URL(fileURLWithPath: fileURL.path), options: nil)
        let audioDuration = asset.duration
        let audioDurationSeconds = CMTimeGetSeconds(audioDuration)

        DispatchQueue.main.async {
            self.resetTimer(second: Int(audioDurationSeconds))
            self.myRecordPlayerBtn.imageView!.stopAnimating()
            self.myRecordPlayerBtn.imageView?.image = #imageLiteral(resourceName: "img_myRocordDefault")
        }
    }
}
}

Solution

  • Probably first initialize to check if your player is playing

    if audioPlayer != nil{
                if audioPlayer?.isPlaying == true {
    
                    audioPlayer?.stop()
    
                    DispatchQueue.main.async {
                        self.resetTimer(second: self.elapsedTimeInSecond)
                        self.startTimer()
                    }
    
                }
            }