使用 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
。你的命名很混乱。
我有一个包含一些数据的本地 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
。你的命名很混乱。