Воспроизводите песни нажатием кнопки в табличном представлении из локального файла JSON с помощью swift

#ios #json #swift #uitableview #parsing

Вопрос:

У меня есть локальный файл JSON с некоторыми данными. Нужно знать, как загрузить аудио из локального JSON и загрузить его на кнопку в табличном представлении. Мой JSON:

 {
    "beatPack":
    {
        "loops": [
            {
                "name": "Away we go",
                "producer": "Tubular Kingz",
                "count": "28",
                "genre": "Lo-fi Hip Hop",
                "imagename": "beatpackone"
                "songName": "alien.mp3"
            }
        ]
    }
}
 

Таким образом, он должен отображать каждую песню на кнопке в табличном представлении. Вот мои методы песни, они могут отображать песню в каждой строке, но не из JSON и не в правильной нумерации, которая мне нужна:

 func playLoop(songName: String) {
        let url = Bundle.main.url(forResource: songName, withExtension: ".mp3")  // you should check it for errors
        audioPlayer = try! AVAudioPlayer(contentsOf: url!)  // of course you should catch the error and deal with it...
        audioPlayer.play()
        print(songName)
    }
    
    func gettingSongName() {
        let folderURL = URL(fileURLWithPath: Bundle.main.resourcePath!)
        
        do {
            let songPath = try FileManager.default.contentsOfDirectory(at: folderURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
            
            for song in songPath {
                var mySong = song.absoluteString
                
                if mySong.contains(".mp3") {
                    let findString = mySong.components(separatedBy: "/")
                    mySong = findString[findString.count - 1]
                    mySong = mySong.replacingOccurrences(of: " ", with: " ")
                    mySong = mySong.replacingOccurrences(of: ".mp3", with: "")
                    songs.append(mySong)
                }
            }
        } catch {
            
        }
    }
 

Вот мой вид таблицы:

 extension BPLibraryViewController: UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate {
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 180
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return loopsName.count
    }
    
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: S.newOneCell, for: indexPath) as! BeatPackLibraryCell
        
        let loopsCount = loopsName[indexPath.row]  //Instance of our arrays from JSON
        
        cell.loopNameLabel.text = loopsCount.name
        cell.producerNameLabel.text = loopsCount.producer
        cell.loopsCountLabel.text = String(loopsName.count)
        cell.genreLabel.text = loopsCount.genre
        cell.firstBeatButtonLabel.setImage(UIImage(named: loopsCount.imagename), for: .normal)
        
        cell.delegate = self
        cell.selectionStyle = .none
        cell.tag = indexPath.row
        
        if let playingCell = currentPlayingIndex, playingCell == indexPath.row {
            cell.firstBtnOutlet.setImage(UIImage(named: "pauseButtonPack.png"), for:
                                            .normal)
        } else {
            cell.firstBtnOutlet.setImage(UIImage(named: "playButtonPack.png"), for:
                                            .normal)
        }
        return cell
    }
 

Кнопка, которая воспроизводит песню в каждом ряду:

 extension BPLibraryViewController: BeatPackLibraryDelegate {
    
    func didTapFirstSong(cell: BeatPackLibraryCell) {
        let indexPath = self.tableView.indexPath(for: cell)
        if currentPlayingIndex == cell.tag {
            audioPlayer.pause()
            currentPlayingIndex = nil
        } else { //IF PAUSE BUTTON
            playLoop(songName: songs[cell.tag])
            currentPlayingIndex = cell.tag
        }
        tableView.reloadData()
        print("Done")
    }
 

Итак, как загрузить песни из локального файла JSON и отобразить их в каждой строке.

Комментарии:

1. Неясно, о чем вы спрашиваете, есть ли у вас mp3-файлы в вашем основном пакете или нет, и если да, то почему вы не можете получить к ним доступ?

2. Да, но главная проблема заключается в том, как правильно получить доступ к ним в табличном представлении, чтобы они отображались в каждой строке?

3. Что означает отображение песни в каждой строке ?

4. Моя ошибка, я отредактировал, он должен воспроизводить песню в каждом ряду. Проверьте скриншот, у нас есть инопланетянин, и он должен воспроизводить «песню инопланетян». Так как же я могу это сделать?

Ответ №1:

Во-первых, метод playLoop не может работать, потому что расширение файла в withExtension параметре должно быть указано без точки.

Мое предложение состоит в том, чтобы добавить вычисляемое свойство в Loop структуру, чтобы, например, получить URL-адрес в пакете приложений

 struct Loop : Decodable {
    let name, producer, count, genre, imagename, songName : String
    private enum CodingKeys : String, CodingKey { case name, producer, count, genre, imagename, songName }
    
    var songURL : URL {
        let components = songName.components(separatedBy: ".")
        guard components.count == 2, 
             let url = Bundle.main.url(forResource: components[0], withExtension: components[1]) else { fatalError("Audio file is missing") }
        return url
    }
}
 

Кодовые ключи необходимо исключить songURL из декодирования.

Если все аудиофайлы являются файлами mp3, вы даже можете опустить расширение в JSON, и вычисляемое свойство станет намного короче

 struct Loop : Decodable {
    let name, producer, count, genre, imagename, songName : String
    private enum CodingKeys : String, CodingKey { case name, producer, count, genre, imagename, songName }
    
   var songURL : URL {
        guard let url = Bundle.main.url(forResource: songName, withExtension: "mp3") else { fatalError("Audio file is missing") }
        return url
   }
}
 

Теперь вы можете изменить playLoop метод на

 func playLoop(songURL: URL) {
    audioPlayer = try! AVAudioPlayer(contentsOf: songURL)
    audioPlayer.play()
    print(songURL.lastPathComponent)
}
 

и воспроизведите песню с помощью метода нажатия

 else { //IF PAUSE BUTTON
     playLoop(songURL: loopsName[indexPath.row].songURL)
     currentPlayingIndex = cell.tag
}
 

Этот метод gettingSongName() не нужен. Кстати это можно написать в 2 строчки

 func gettingSongName() {
    guard let mp3URLs = Bundle.main.urls(forResourcesWithExtension: "mo3", subdirectory: nil) else { return }
    songs = mp3URLs.map{$0.deletingPathExtension().lastPathComponent} 
}
 

Примечания:

  • Никогда не вызывайте absoluteString URL-адрес файловой системы, вы можете просто получить путь с path помощью .
  • Назовите свой массив источников данных loops и элемент в cellforRow loop нем . Ваше имя довольно сбивает с толку.

Комментарии:

1. Получение этого: Поток 1: Фатальная ошибка: Отсутствует аудиофайл

2. Когда я попробовал решение с mp3, у меня возникла ошибка. Но теперь я попробовал это: var songURL : URL { пусть компоненты = Имя песни.компоненты(разделено: «.») . количество компонентов == 2, пусть url = Bundle.main.url(для источника: компоненты[0], с расширением: компоненты[1]) иначе { Фатальная ошибка(«Отсутствует аудиофайл») } возвращает url }