1

I have a detailed controller where AVPlayer is created, when I start the player, then I exit the controller and I enter that data about the song is not saved and when I click on play AVPlayer is created again. The question is how to make AVPlayer save all the data and do not have to delete the old player. Gif with the problem below

enter image description here

it is my code:

ViewControllerAudioInfo is controller in which I get data about songs

 func request(){

    let urlData = "https:---.com/local/apps/apple/library_detail.php/?idLibrary=\(detail!.id!)"


    var urlRequest = URLRequest(url: URL(string: urlData)!)
    urlRequest.timeoutInterval = 300



    let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in

        if error != nil{
            print(error ?? 0)
            return
        }

        DispatchQueue.main.async {

            let json = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String: Any]


            if let posts = json["FILE"] as? [AnyObject] {
                for post in posts {
                    var info = Modal()

                    info.AudioName = post["NAME"] as? String
                    info.UrlName = post["SRC"] as? String
                    info.ImageViewAudio = self.detail?.ImageView
                    info.AudioName = info.AudioName?.replacingOccurrences(of:".mp3", with: "")

                    self.mod.append(info)


                }
            }
        }

    }
    self.preloadEnd()
    task.resume()
}


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

     if segue.identifier == "audioDetail" {
        let destinationVC = segue.destination as! ViewControllerAudioDetail
        destinationVC.mod = mod
    }

    if segue.identifier == "list" {
        let destinationVC = segue.destination as! TableViewControllerAudioList
        destinationVC.mod = mod
    }
}

And detailController

import UIKit  
import AVFoundation

class ViewControllerAudioDetail: UIViewController {


static var avPlayer:AVPlayer?
var status = false
var timeSlider = false
fileprivate let seekDuration: Float64 = 10
fileprivate let seekDurationThirty: Float64 = 30
var sliderEndTime:Any!
var sliderDurationTime:Any!


var mod = [Modal]()

@IBOutlet weak var menuButton: UIBarButtonItem!


@IBOutlet weak var ImageView: UIImageView!

@IBOutlet weak var startTime: UILabel!
@IBOutlet weak var endTime: UILabel!
@IBOutlet weak var sliderSong: UISlider!
@IBOutlet weak var name: UILabel!

@IBOutlet weak var Volume: UISlider!
@IBOutlet weak var iconChange: UIButton!


override func viewDidLoad() {
    super.viewDidLoad()


    //кнопка назад
    let backItem = UIBarButtonItem()
    backItem.title = ""
    navigationItem.backBarButtonItem = backItem

    menu()


    sliderSong.minimumValue = 0
    sliderSong.maximumValue = 1
    sliderSong.setThumbImage(UIImage(named: "thumb.png"), for: .normal)



    name.sizeToFit()
    name.text = mod[thisSong].AudioName
    ImageView.image = mod[0].ImageViewAudio

    player(urlSong:mod[thisSong].UrlName!)

    self.timeSlider = true
    self.status = true
    Status()


    do {
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        let _ = try AVAudioSession.sharedInstance().setActive(true)
    } catch let error as NSError {
        print("an error occurred when audio session category.\n \(error)")
    }


}

func menu(){

    if revealViewController() != nil {

        menuButton.target = revealViewController()
        menuButton.action = #selector(SWRevealViewController.rightRevealToggle(_:))

        view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())


    }

}


@IBAction func sliderSong(_ sender: UISlider) {

        //перемотка аудиозвука
        let duration = CMTimeGetSeconds(ViewControllerAudioDetail.avPlayer!.currentItem!.asset.duration)
        let value = sliderSong.value
        let durationToSeek = Float(duration) * value

        ViewControllerAudioDetail.avPlayer?.seek(to: CMTimeMakeWithSeconds(Float64(durationToSeek),ViewControllerAudioDetail.avPlayer!.currentItem!.duration.timescale)) { [](state) in


            if (self.iconChange.currentImage?.isEqual(UIImage(named: "Play.png")))! {
                ViewControllerAudioDetail.avPlayer?.pause()
            } else if (self.iconChange.currentImage?.isEqual(UIImage(named: "Pause.png")))!{
                ViewControllerAudioDetail.avPlayer?.play()
            }
        }
}


@IBAction func volume(_ sender: UISlider) {

    ViewControllerAudioDetail.avPlayer?.volume = sender.value
}



@IBAction func minusThirtySec(_ sender: Any) {

    let playerCurrentTime = CMTimeGetSeconds((ViewControllerAudioDetail.avPlayer?.currentTime())!)
    var newTime = playerCurrentTime - seekDurationThirty

    if newTime < 0 {
        newTime = 0
    }
    let time2: CMTime = CMTimeMake(Int64(newTime * 1000 as Float64), 1000)
    ViewControllerAudioDetail.avPlayer?.seek(to: time2, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)


}
@IBAction func minusTenSec(_ sender: Any) {

    let playerCurrentTime = CMTimeGetSeconds((ViewControllerAudioDetail.avPlayer?.currentTime())!)
    var newTime = playerCurrentTime - seekDuration

    if newTime < 0 {
        newTime = 0
    }
    let time2: CMTime = CMTimeMake(Int64(newTime * 1000 as Float64), 1000)
    ViewControllerAudioDetail.avPlayer?.seek(to: time2, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)

}

@IBAction func plusTenSec(_ sender: Any) {

    guard let duration = ViewControllerAudioDetail.avPlayer?.currentItem?.duration else{
        return
    }
    let playerCurrentTime = CMTimeGetSeconds((ViewControllerAudioDetail.avPlayer?.currentTime())!)
    let newTime = playerCurrentTime + seekDuration

    if newTime < (CMTimeGetSeconds(duration) - seekDuration) {

        let time2: CMTime = CMTimeMake(Int64(newTime * 1000 as Float64), 1000)
        ViewControllerAudioDetail.avPlayer?.seek(to: time2, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)

    }

}
@IBAction func plusThirtySec(_ sender: Any) {

    guard let duration = ViewControllerAudioDetail.avPlayer?.currentItem?.duration else{
        return
    }
    let playerCurrentTime = CMTimeGetSeconds((ViewControllerAudioDetail.avPlayer?.currentTime())!)
    let newTime = playerCurrentTime + seekDurationThirty

    if newTime < (CMTimeGetSeconds(duration) - seekDuration) {

        let time2: CMTime = CMTimeMake(Int64(newTime * 1000 as Float64), 1000)
        ViewControllerAudioDetail.avPlayer?.seek(to: time2, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)

    }

}

@IBAction func Next(_ sender: Any) {

    ViewControllerAudioDetail.avPlayer?.removeTimeObserver(sliderEndTime)
    ViewControllerAudioDetail.avPlayer?.removeTimeObserver(sliderDurationTime)



    if thisSong == mod.count - 1 {
        thisSong = 0
    } else {
        thisSong += 1
    }


    if thisSong != mod.count{

        name.text = mod[thisSong].AudioName
        player(urlSong:mod[thisSong].UrlName!)

        Status()

    }

}
@IBAction func Back(_ sender: Any) {

    ViewControllerAudioDetail.avPlayer?.removeTimeObserver(sliderEndTime)
    ViewControllerAudioDetail.avPlayer?.removeTimeObserver(sliderDurationTime)



    if thisSong != 0 {
        thisSong -= 1
    } else {
        thisSong = mod.endIndex - 1
    }


        name.text = mod[thisSong].AudioName
        player(urlSong:mod[thisSong].UrlName!)

       Status()




}


func Status(){

    timeSlider = false

    if status == true {
        iconChange.setImage(UIImage(named:"Pause.png"), for: .normal)
        ViewControllerAudioDetail.avPlayer?.play()
    } else {
        iconChange.setImage(UIImage(named:"Play.png"), for: .normal)
        ViewControllerAudioDetail.avPlayer?.pause()
    }

}


@IBAction func Play(_ sender: Any) {


    if ViewControllerAudioDetail.avPlayer?.rate == 0 && status == false{
        status = true
        ViewControllerAudioDetail.avPlayer?.play()
        ViewControllerAudioDetail.avPlayer?.rate = 1.0
        iconChange.setImage(UIImage(named:"Pause.png"), for: .normal)


        if timeSlider == false {

        sliderDurationTime = ViewControllerAudioDetail.avPlayer?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, 1), queue: nil, using: {
                (CMTime) -> Void in

                self.updateProgressBar()
            })

        }


    } else {

        status = false
        ViewControllerAudioDetail.avPlayer?.rate = 0.0
        ViewControllerAudioDetail.avPlayer?.pause()
        iconChange.setImage(UIImage(named:"Play.png"), for: .normal)

    }

}




func player(urlSong:String) {

    let url = URL(string: urlSong)
    let playerItem = AVPlayerItem(url: url!)
    ViewControllerAudioDetail.avPlayer = AVPlayer(playerItem:playerItem)

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

    sliderDurationTime = ViewControllerAudioDetail.avPlayer?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, 1), queue: nil, using: {
        (CMTime) -> Void in

        self.updateProgressBar()
    })

     sliderEndTime = ViewControllerAudioDetail.avPlayer!.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), queue: DispatchQueue.main) { [weak self] (time) in

        let duration = CMTimeGetSeconds((ViewControllerAudioDetail.avPlayer!.currentItem!.asset.duration))
        self?.sliderSong.value = Float(CMTimeGetSeconds(time)) / Float(duration)

    }


    let duration = CMTimeGetSeconds(ViewControllerAudioDetail.avPlayer!.currentItem!.asset.duration)
    let minutesTextOut = Int(duration) / 60 % 60
    let secondsTextOut = Int(duration) % 60
    let strDuration = String(format:"%02d:%02d", minutesTextOut, secondsTextOut)
    endTime.text = strDuration



}

func playerDidFinishPlaying(note: NSNotification) {

    ViewControllerAudioDetail.avPlayer?.removeTimeObserver(sliderDurationTime)
    ViewControllerAudioDetail.avPlayer?.removeTimeObserver(sliderEndTime)
    NotificationCenter.default.removeObserver(self)


    if thisSong == mod.count - 1 {
        thisSong = 0
    } else {
        thisSong += 1
    }


    if thisSong != mod.count{

        name.text = mod[thisSong].AudioName
        player(urlSong:mod[thisSong].UrlName!)

        Status()

    }




}


func updateProgressBar(){
    let timeNow = Int(ViewControllerAudioDetail.avPlayer!.currentTime().value) / Int(ViewControllerAudioDetail.avPlayer!.currentTime().timescale)

    let minutesText = timeNow / 60
    let secondsText = timeNow % 60

    let duration = String(format:"%02d:%02d", minutesText, secondsText)
    startTime.text = duration

}



override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "toList" {
        let vc = segue.destination as! TableViewControllerAudioList
        vc.mod = mod
    }
}
Vasya2014
  • 203
  • 1
  • 5
  • 25

2 Answers2

1

Instead of globally declaring an AVPlayer, declare it as a static property on your ViewControllerAudioDetail to prevent it from being deinitalised (as this is better practice):

static var avPlayer:AVPlayer?

You should then restore the state of your player view components based on the state of this ViewControllerAudioDetail.avPlayer. You can determine if it is playing or not based on the information given here and can find out the current time using ViewControllerAudioDetail.avPlayer.currentTime(), for example.

Bradley Mackey
  • 6,777
  • 5
  • 31
  • 45
-1

Below is the code to check whether song is already playing or not....

        if  avPlayer != nil {
         if ((avPlayer.rate != 0) && (avPlayer.error == nil)) {
             // No need to initialise player again....
         } else {
             // initialise the code here...
             avPlayer = AVPlayer(playerItem:playerItem)
             avPlayer.play()
             avPlayer.actionAtItemEnd = .advance
             avPlayer.addObserver(self, forKeyPath: "currentItem", options: [.new, .initial] , context: nil)
        }
    }
Ashok Londhe
  • 1,491
  • 11
  • 29
  • if ((avPlayer.rate != 0) && (avPlayer.error == nil)) { When checking, the application breaks down – Vasya2014 Jul 13 '17 at 10:28
  • Can you share the crash log – Ashok Londhe Jul 13 '17 at 10:32
  • fatal error: unexpectedly found nil while unwrapping an Optional value – Vasya2014 Jul 13 '17 at 10:36
  • While creating object of AVPlayer ... create like ... var avPlayer = AVPlayer() – Ashok Londhe Jul 13 '17 at 10:38
  • ViewControllerAudioDetail.avPlayer.actionAtItemEnd = .advance return `AVPlayerActionAtItemEndAdvance is not allowed for a player of class AVPlayer'` and ViewControllerAudioDetail.avPlayer.addObserver(self, forKeyPath: "currentItem", options: [.new, .initial] , context: nil) return `observeValueForKeyPath:ofObject:change:context: message was received but not handled.` – Vasya2014 Jul 13 '17 at 10:47