使用 swift 从本地 JSON 文件点击 table 视图中的按钮播放歌曲

Play songs by tapping the button in table view from local JSON file using swift

我有一个包含一些数据的本地 JSON 文件。需要知道如何从本地 JSON 加载音频并将其加载到 table 视图中的按钮。我的 JSON:

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

因此它应该将每首歌曲显示到 table 视图中的按钮。这是我的歌曲方法,它们可以在每一行中显示歌曲,但不是 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: "%20", with: " ")
                    mySong = mySong.replacingOccurrences(of: ".mp3", with: "")
                    songs.append(mySong)
                }
            }
        } catch {
            
        }
    }

这是我的 table 观点:

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 文件加载歌曲并将其显示在每一行上。

首先,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
    }
}

需要 CodingKeys 才能将 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{[=14=].deletingPathExtension().lastPathComponent} 
}

备注:

  • 切勿在文件系统 URL 上调用 absoluteString,您只需使用 path.
  • 即可获取路径
  • 将您的数据源数组命名为 loops 并将 cellforRow 中的项目命名为 loop。你的命名很混乱。