Передача URL-адреса из контроллера представления таблицы в контроллер представления с помощью AVAudioPlayer - PullRequest
0 голосов
/ 22 марта 2020

В настоящее время я пытаюсь воспроизвести аудио из базы данных Firebase. У меня есть два контроллера представления, первый - контроллер табличного представления, и второй - контроллер представления с AVAudioPlayer. Я пытаюсь выяснить, как успешно воспроизводить аудио-URL, который захватывается из firebase, который затем передается на второй контроллер представления для воспроизведения, когда аудио выбирается в табличном представлении. Вот мой текущий код, который возвращает «Неустранимая ошибка: неожиданно обнаружил ноль при развертывании необязательного значения» на моем втором контроллере просмотра, когда звук выбирается из контроллера табличного просмотра.

TableViewController:

import UIKit
import Firebase

class HeaderViewTableViewController: UITableViewController {


    @IBOutlet weak var trackImage: UIImageView!
    @IBOutlet weak var trackName: UILabel!
    @IBOutlet weak var artistName: UILabel!
    @IBOutlet weak var trackHeaderImage: UIImageView!

    var image: UIImageView?
    var image2: UIImageView?

    var songs = [String]()
    var urls = [URL]()

    var song:SongPost?

    private let tableHeaderViewHeight: CGFloat = 350.0
    private let tableHeaderViewCutAway: CGFloat = 0.1

    var headerView: HeaderView!
    var headerMaskLayer: CAShapeLayer!

    override func viewDidLoad() {
        super.viewDidLoad()



        print(song)

        trackName.text = song?.title

        ImageService.getImage(withURL: song!.coverImage) { image in
         self.trackImage.image = image
            self.trackHeaderImage.image = image
        }
        artistName.text = song?.author.fullname




        tableView.tableFooterView = UIView()

        headerView = tableView.tableHeaderView as! HeaderView
        headerView.imageView = trackImage
        tableView.tableHeaderView = nil
        tableView.addSubview(headerView)

        tableView.contentInset = UIEdgeInsets(top: tableHeaderViewHeight, left: 0, bottom: 0, right: 0)
        tableView.contentOffset = CGPoint(x: 0, y: -tableHeaderViewHeight + 64)

        //cut away header view
        headerMaskLayer = CAShapeLayer()
        headerMaskLayer.fillColor = UIColor.black.cgColor
        headerView.layer.mask = headerMaskLayer

        let effectiveHeight = tableHeaderViewHeight - tableHeaderViewCutAway/2
        tableView.contentInset = UIEdgeInsets(top: effectiveHeight, left: 0, bottom: 0, right: 0)
        tableView.contentOffset = CGPoint(x: 0, y: -effectiveHeight)

        updateHeaderView()
    }

    func updateHeaderView() {
        let effectiveHeight = tableHeaderViewHeight - tableHeaderViewCutAway/2
        var headerRect = CGRect(x: 0, y: -effectiveHeight, width: tableView.bounds.width, height: tableHeaderViewHeight)

        if tableView.contentOffset.y < -effectiveHeight {
            headerRect.origin.y = tableView.contentOffset.y
            headerRect.size.height = -tableView.contentOffset.y + tableHeaderViewCutAway/2
        }

        headerView.frame = headerRect

        let path = UIBezierPath()
        path.move(to: CGPoint(x: 0, y:0))
        path.addLine(to: CGPoint(x: headerRect.width, y: 0))
        path.addLine(to: CGPoint(x: headerRect.width, y: headerRect.height))
        path.addLine(to: CGPoint(x: 0, y: headerRect.height - tableHeaderViewCutAway))

        headerMaskLayer?.path = path.cgPath
    }


      override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
          self.tableView.decelerationRate = UIScrollView.DecelerationRate.fast
      }

      override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
          return UITableView.automaticDimension
      }



    @IBAction func backButton(_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }

}

extension HeaderViewTableViewController {


    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return (song?.audioName.count)!
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "SongCell", for: indexPath) as! SongNameTableViewCell

        cell.set(song: self.song!)

        cell.songName.text = song?.audioName[indexPath.row]
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let vc = storyboard?.instantiateViewController(identifier: "ViewController2") as? ViewController2
        vc?.song = song
        vc?.audioName = (song?.audioName[indexPath.row])!

        self.present(vc!, animated: true, completion: nil)
        vc?.url = song?.audioUrl[indexPath.row]

    }

}


extension HeaderViewTableViewController {
    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        updateHeaderView()
    }
}

Второй ViewController:

import UIKit
import AVFoundation
import MediaPlayer
import Firebase

class ViewController2: UIViewController, UITabBarControllerDelegate {



    var song2 = [SongPost]()

    var song: SongPost?
    var audioName : String?
    var url:URL?

    @IBOutlet weak var progressBar: UISlider!
    @IBOutlet weak var downChevron: UIButton!
    var timer:Timer?
    var player = AVAudioPlayer()

    @IBOutlet weak var currentTime: UILabel!
    @IBOutlet weak var durationTime: UILabel!

    @IBOutlet weak var coverImage: UIImageView!
    @IBOutlet weak var backgroundImage: UIImageView!
    @IBOutlet weak var songName: UILabel!
    @IBOutlet weak var artistName: UILabel!
    var trackID: Int = 0
    var toggleState = 1

    var ref: DatabaseReference?


    @IBOutlet weak var playButton: UIButton!


    override func viewDidLoad() {
        super.viewDidLoad()

        setUI()


        tabBarController?.tabBar.barTintColor = UIColor.black
        tabBarController?.tabBar.isTranslucent = false

        tabBarController?.tabBar.tintColor = .white
        tabBarController?.tabBar.unselectedItemTintColor = .gray


        do {

            let songURL = url

            print(songURL)
            try player = AVAudioPlayer(contentsOf: songURL!)
            progressBar.maximumValue = Float(player.duration)
            timer = Timer.scheduledTimer(timeInterval: 0.00001, target: self, selector: #selector(changeSliderValueFollowPlayerCurtime), userInfo: nil, repeats: true)
            player.delegate = self

            player.play()


            MPNowPlayingInfoCenter.default().nowPlayingInfo = [
                MPMediaItemPropertyTitle : "The Box",
                MPMediaItemPropertyArtist: "Roddy Ricch",
                MPMediaItemPropertyPlaybackDuration: player.duration,

            ]
            UIApplication.shared.beginReceivingRemoteControlEvents()
            becomeFirstResponder()


        } catch {


        }

        do {
             try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: .defaultToSpeaker)
        } catch {

        }

    }


    func setUI() {

        ImageService.getImage(withURL: song!.coverImage) { image in
                       self.coverImage.image = image
                          self.backgroundImage.image = image
                      }
               songName.text = song?.title
               artistName.text = song?.author.fullname
    }


    override func remoteControlReceived(with event: UIEvent?) {
        if let event = event {
            if event.type == .remoteControl {
                switch event.subtype {
                case .remoteControlPlay:
                    player.play()
                case .remoteControlPause:
                    player.pause()
                case .remoteControlNextTrack:
                    print("next")
                case .remoteControlPreviousTrack:
                    print("previous")
                default:
                    print("error")
                }
            }
        }
    }

    @objc func changeSliderValueFollowPlayerCurtime() {

        let curValue = Float(player.currentTime)
        progressBar.value = curValue

    }


    @IBAction func sliderUsed(_ sender: Any) {
        player.pause()
        let curTime = progressBar.value
        player.currentTime = TimeInterval(curTime)
        player.play()
    }

    @IBAction func playPauseButtonTapped(_ sender: Any) {
        let playBtn = sender as! UIButton
               if toggleState == 1 {
                  player.pause()
                toggleState = 2
                playBtn.setImage(UIImage(named:"play 5.png"),for:UIControl.State.normal)

               } else {
                player.play()
                   toggleState = 1
                playBtn.setImage(UIImage(named:"pause 4.png"),for:UIControl.State.normal)

               }
    }


    @IBAction func nextButton(_ sender: Any) {


    }

    @IBAction func backButton(_ sender: Any) {
        if trackID != 0 || trackID > 0 {

            trackID -= 1
        }

        do {
            let audioPath = Bundle.main.path(forResource: "the box", ofType: "mp3")
            try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)
        } catch {


        }


    }

    @IBAction func downChevronTapped(_ sender: Any) {

        dismiss(animated: true, completion: nil)
    }

}

extension ViewController2: AVAudioPlayerDelegate {
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        if flag {
            do {

                    let audioPath = Bundle.main.path(forResource: "the box", ofType: "mp3")
                try self.player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)

                    progressBar.maximumValue = Float(player.duration)
                    timer = Timer.scheduledTimer(timeInterval: 0.00001, target: self, selector: #selector(changeSliderValueFollowPlayerCurtime), userInfo: nil, repeats: true)
                    player.delegate = self

                    player.play()
                } catch {

                }
        }
    }
}

SongPost Класс:

import Foundation

class SongPost {
    var id: String
    var author: UserProfile
    var title: String
    var coverImage: URL
    var audioUrl: [URL]
    var audioName : [String]
    var createdAt:Date



    init(id:String, author:UserProfile, title:String, coverImage:URL, audioUrl:[URL], audioName: [String] ,timestamp:Double) {
        self.id = id
        self.author = author
        self.coverImage = coverImage
        self.audioUrl = audioUrl
        self.audioName = audioName

        self.title = title
        self.createdAt = Date(timeIntervalSince1970: timestamp / 1000)
    }

}

ОБНОВЛЕНО

Fun c Настройка кода:

 func configure(song: SongPost, audioName: String, url:URL) {

          do {

                    let songURL = url

                    print(songURL)
        //            let audioPath = Bundle.main.path(forResource: "the box", ofType: "mp3")
            try player = AVAudioPlayer(contentsOf: songURL)
                    progressBar.maximumValue = Float(player.duration)
                    timer = Timer.scheduledTimer(timeInterval: 0.00001, target: self, selector: #selector(changeSliderValueFollowPlayerCurtime), userInfo: nil, repeats: true)
                    player.delegate = self

                    player.play()


                    MPNowPlayingInfoCenter.default().nowPlayingInfo = [
                        MPMediaItemPropertyTitle : "The Box",
                        MPMediaItemPropertyArtist: "Roddy Ricch",
                        MPMediaItemPropertyPlaybackDuration: player.duration,

                    ]
                    UIApplication.shared.beginReceivingRemoteControlEvents()
                    becomeFirstResponder()


                } catch {


                }

                do {
                     try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: .defaultToSpeaker)
                } catch {

                }

    }

1 Ответ

1 голос
/ 22 марта 2020

Избегайте использования распаковки в вашем коде, если вы точно не уверены, что необязательный параметр не равен nil.

Во время вызова instantiateViewController можно вызвать метод viewDidLoad ViewController2.

let vc = storyboard?.instantiateViewController(identifier: "ViewController2") 
                                                                       as? ViewController2

Это означает, что вы получите ошибку в строке, где вы развернете songURL, который равен нулю.

try player = AVAudioPlayer(contentsOf: songURL!)

Так что viewDidLoad не подходит для настройки вашего вида в вашем случае.

Создайте configure функцию в ViewController2, которая принимает параметры из предыдущего viewController, тогда вы можете настроить плеер при вызове configure, что означает, что ваши параметры не равны нулю.

class ViewController2: UIViewController, UITabBarControllerDelegate {

   func configure(song: SongPost, audioName : String, url:URL) {
       //setup your view
   }

   //...... other methods, viewDidload etc.
}

//

extension HeaderViewTableViewController {

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let vc = storyboard?.instantiateViewController(identifier: "ViewController2") 
                                                                  as? ViewController2
        self.present(vc!, animated: true, completion: nil)
        vc?.configure(
             song: song, 
             audioName: (song?.audioName[indexPath.row])!, 
             url: song?.audioUrl[indexPath.row])

    }

}
...