ios - 从 UITableViewCell 内的 url 播放音频

标签 ios swift avaudioplayer

当用户点击 UITableViewCell 内的播放按钮时如何播放音频文件?就像播放 Whatsapp 聊天消息中的音频一样,任何开源库都可以节省我的时间吗?

最佳答案

我已经创建了您想要的相同模块。我设计并创建了自定义表格 View 单元格,以从表格 View 中的包中加载音乐。

为其创建各自所需的变量。

// Music Related Variables
var musicKeyboardView                           : UIView!
var arrMusics                                   : [String] = []
var previousAudioSelectedIndexPath              : IndexPath!
var audioPlayer                                 : AVAudioPlayer?

根据arrMusics,您可以加载音乐的路径。我从 bundle 中加载它,如下所示:

func fetchMusicFromResource() {

    // First remove all music data from array
    // This code is required because every time switching keyboard will duplicate data.
    self.arrMusics.removeAll()

    // Fetch all the files paths from CustomKeyboard Target.
    // This code return array of string which contains paths for every resource inside CustomKeyboard.
    if let files = try? FileManager.default.contentsOfDirectory(atPath: Bundle.main.bundlePath) {

        // Take single file and check if it contains "mp3" string, then add to arrMusic.
        // Write this code if you know there are mp3 extension for song.
        // We know that resource contains mp3 for music file, so this code will work fine.
        // You need to change if this is dynamic, means music file with other extensions.
        // Make sure video file name not contains mp3 text, otherwise it will not work or may be crash the app.
        // If you will use music library songs, then you don't need this code.
        for file in files {
            if file.contains("mp3") {
                self.arrMusics.append(file)
            }
        }

        // Check if arrMusics contains elements more then 0 then, hide the label and show the table,
        // otherwise show label and hide table
        if self.arrMusics.count > 0 {
            self.tblMusic.isHidden = false
            self.lblEmptyMusic.isHidden = true
        } else {
            self.tblMusic.isHidden = true
            self.lblEmptyMusic.isHidden = false
        }

        // Set table rowHeight and estimatedRowHeight as TableViewAutomaticDimension and reload table.
        self.tblMusic.rowHeight = UITableViewAutomaticDimension
        self.tblMusic.estimatedRowHeight = UITableViewAutomaticDimension
        self.tblMusic.reloadData()
    }
}

因此,在 cellForRowAt indexPath 方法中,我为 playButton 添加目标,如下所示:

musicCell.btnPlay.tag = indexPath.row * 10
cell.btnPlay.addTarget(self, action: #selector(musicPlayButtonClicked(sender:)), for: .touchUpInside)

当点击播放按钮时,它将触发 musicPlayButtonClicked(sender:) 方法,其中包含以下代码来播放音乐。

@IBAction func musicPlayButtonClicked(sender: UIButton) {

    // Take the row by sender.tag / 10.
    // Previous we set tag of play button with indexPath.row * 10
    // So here we are deviding and get original row index and create indexPath for selected row.
    let indexPath = IndexPath(row: sender.tag / 10, section: 0)

    // This code check if previousAudioSelectedIndexPath not nil,
    // then execute code inside if block
    if previousAudioSelectedIndexPath != nil {

        // This code check indexPath is equal to previousAudioSelectedIndexPath.
        if (indexPath == previousAudioSelectedIndexPath) {

            // Set button's selected propery to true.
            sender.isSelected = false

            // Set previousAudioSelectedIndexPath to nil.
            previousAudioSelectedIndexPath = nil

            // Call method to play music for selected index.
            self.playMusicAt(index: sender.tag)

        } else {

            // Create cell for previousAudioSelectedIndexPath.
            let previousSelectedCell = self.tblMusic.cellForRow(at: previousAudioSelectedIndexPath)

            // Take the previousButton selected from previousSelectedCell and Set previousButton's isSelected property to false.
            if let previousButton = previousSelectedCell!.contentView.subviews[1] as? UIButton {
                previousButton.isSelected = false
            }

            // Set button's selected propery to true for current selected button.
            sender.isSelected = true

            // Assign current selected indexPath to previous selected indexPath.
            previousAudioSelectedIndexPath = indexPath

            // Call method to play music for selected index.
            self.playMusicAt(index: sender.tag)
        }

    } else {

        // If current button is selected then stop playing music,
        // Otherwise execute code of else block to play music.
        if sender.isSelected {

            // This code will stop the music for selected row.
            sender.isSelected = false
            previousAudioSelectedIndexPath = nil
            self.playMusicAt(index: sender.tag)

        } else {

            // This code will start to play the music for selected row.
            sender.isSelected = true
            previousAudioSelectedIndexPath = indexPath
            self.playMusicAt(index: sender.tag)
        }
    }
}

此代码将用于获取单元格按钮并设置与播放/暂停相关的相应图像。

然后将创建另一种方法来在从数组获取的路径上播放歌曲。看下面的方法。

func playMusicAt(index: Int) {

    // This will check is audioPlayer is not nil and audioPlayer is already playing,
    // Then first pause audioPlayer and remove it from memory.
    if audioPlayer != nil, (audioPlayer?.isPlaying)! {
        audioPlayer?.pause()
        audioPlayer = nil
    }

    // This line of code check previousAudioSelectedIndexPath is not nil,
    // then execute code with if block.
    if previousAudioSelectedIndexPath != nil {

        // This code copy song name from selected index
        let songName = self.arrMusics[index / 10].components(separatedBy: ".")

        // Create source path of song name with extension.
        // If it's failed to create url path,
        // then return from this line of code and stop to execute next lines of code.
        guard let url = Bundle.main.url(forResource: songName[0], withExtension: "mp3") else { return }

        do {

            // This will start the AVAudioSession for AVAudioSessionCategoryPlayback.
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)

            // This code will active AVAudioSession.
            try AVAudioSession.sharedInstance().setActive(true)

            /* The following line is required for the player to work on iOS 11. Change the file type accordingly*/
            audioPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)

            /* iOS 10 and earlier require the following line:
             player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMP3Layer3) */

            // Create new audio player,
            // if audio plyer is failed to create,
            // then return from this line of code and stop to execute next lines of code.
            guard let audioPlayer = audioPlayer else { return }

            // This code start playing song
            audioPlayer.play()

        } catch let error {
            // If audio session failed to start or AVAudioPlayer failed to initialised then throw the error.
            print(error.localizedDescription)
        }
    }
}

希望这段代码能对您有所帮助。

关于ios - 从 UITableViewCell 内的 url 播放音频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49187914/

相关文章:

iOS:Watch Kit 配对设备无法用于开发

iphone - autoresizingMask 和固定边距的问题

arrays - 在 Swift 中查找由特殊字符包裹的子字符串

ios - Swift 3 游戏音乐播放

ios - 裁剪音频文件

Swift:在歌曲的某些部分引发 Action (avaudioplayer)

ios - 标签栏 Controller 内的导航 Controller 不显示标题

ios - 在应用程序中访问 iOS 语音识别

ios - 更改 UISearchController 在 Swift 中的位置

ios - AirPlay 外部屏幕返回 (0, 0, 0, 0) 作为边界