我已使用swift在iOS中构建了一个应用程序,其中列出了客户端的图像,歌曲和PDF。用户可以下载并使用这些文件。对于歌曲,功能是让用户下载文件,下载完成后,让他/她播放文件。这些歌曲会按专辑名称列出在UITableView
中,单击某个单元格就会调用.show
,以显示该专辑中其他歌曲的UITableView
。单击表中的单元格时,我检查文件是否已下载。如果已下载,则使用.show
打开一个包含所有媒体播放器控件和媒体元数据的视图控制器。
我已经实现了所有媒体控件,包括重复和随机播放功能,并且播放器为所有下载的曲目连续播放。当我在导航栏上单击时,会破坏AVPlayer
的实例,并且歌曲不会在后台播放。之所以这样做,是因为如果不这样做,歌曲会一直在后台播放,并且我不知道如何获得播放歌曲的播放控制。
这是我的媒体播放器的视图控制器
import Foundation
import UIKit
import AVFoundation
import MediaPlayer
class AudioPlayer: UIViewController, AVAudioPlayerDelegate {
var downloadedTracks = [URL]()
var playableAudioItems = [PlayableTrack]()
var playableAudioItemsUnshuffled = [PlayableTrack]()
var serviceAudioItems = [AudioItem]()
var currentTrack: PlayableTrack? = nil
var topLevelLabel = " "
var avPlayerItem: AVPlayerItem?
var player: AVPlayer?
var currentTrackIndex = 0
var commandCenter = MPRemoteCommandCenter.shared()
var mpic = MPNowPlayingInfoCenter.default()
var isRepeated = false
var isShuffled = false
@IBOutlet weak var albumArt: UIImageView!
@IBOutlet weak var rewindButton: UIButton!
@IBOutlet weak var playButton: UIButton!
@IBOutlet weak var forwardButton: UIButton!
@IBOutlet weak var titleTrack: UILabel!
@IBOutlet weak var progressSlider: UISlider!
@IBOutlet weak var previousTrack: UIButton!
@IBOutlet weak var nextTrack: UIButton!
@IBOutlet weak var albumName: UILabel!
@IBOutlet weak var shuffleButton: UIButton!
@IBOutlet weak var repeatButton: UIButton!
override func viewDidLoad(){
downloadedTracks = CommonUtils.fetchAudioPaths()
if (UserDefaults.standard.object(forKey: "AudioItems") as? Data) != nil {
self.serviceAudioItems = UserDefaults.standard.retrieve(object: Array<AudioItem>.self, fromKey: "AudioItems")!
}
if UserDefaults.standard.object(forKey: "TracksRepeatable") != nil {
self.isRepeated = UserDefaults.standard.bool(forKey: "TracksRepeatable")
}
if UserDefaults.standard.object(forKey: "TracksShuffled") != nil {
self.isShuffled = UserDefaults.standard.bool(forKey: "TracksShuffled")
}
self.mapValuesFromServiceToPlayable()
if self.isShuffled {
self.playableAudioItems.shuffle()
}
UIApplication.shared.beginReceivingRemoteControlEvents()
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
try AVAudioSession.sharedInstance().setActive(true)
} catch _ {
return print("error")
}
if currentTrack != nil {
self.currentTrackIndex = self.playableAudioItems.firstIndex(of: self.currentTrack!) ?? 0
}
}
func mapValuesFromServiceToPlayable() {
self.playableAudioItems = [PlayableTrack]()
for serviceTrack in self.serviceAudioItems {
let fileName = URL(string: serviceTrack.trackURL)!.lastPathComponent
for mp3FileURL in self.downloadedTracks {
if mp3FileURL.lastPathComponent == fileName {
let track = PlayableTrack(
albumName: serviceTrack.albumName,
albumArt: serviceTrack.albumArtURL,
trackName: serviceTrack.trackName,
trackURL: mp3FileURL)
self.playableAudioItems.append(track)
}
}
}
self.playableAudioItemsUnshuffled = self.playableAudioItems
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(_ animated: Bool) {
self.commandCenter = MPRemoteCommandCenter.shared()
self.mpic = MPNowPlayingInfoCenter.default()
self.initialisePlayerAndPlay()
}
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(self.finishedPlaying(myNotification:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.avPlayerItem)
}
override func viewWillDisappear(_ animated: Bool) {
self.player?.pause()
NotificationCenter.default.removeObserver(self)
self.destroyPlay()
}
func initialisePlayerAndPlay() {
let playableTrack = playableAudioItems[self.currentTrackIndex]
if self.player != nil {
if self.player!.rate != 0.0 {
self.destroyPlay()
}
self.progressSlider.setValue(0.0, animated: true)
let origPlayImage = UIImage(named: "baseline_play_circle_outline_black_48pt")
let tintedPlayImage = origPlayImage?.withRenderingMode(.alwaysTemplate)
self.playButton.setBackgroundImage(tintedPlayImage, for: .normal)
self.playButton.tintColor = KeyConstants.apsmDark
}
let imageURL = URL(string: playableTrack.albumArt.trimmingCharacters(in: .whitespacesAndNewlines))
self.albumArt.sd_setShowActivityIndicatorView(true)
self.albumArt.sd_setIndicatorStyle(.whiteLarge)
self.albumArt.sd_setImage(with: imageURL)
self.titleTrack.text = playableTrack.trackName
self.albumName.text = playableTrack.albumName
let origRewindImage = self.rewindButton.backgroundImage(for: .normal)
let tintedRewindImage = origRewindImage?.withRenderingMode(.alwaysTemplate)
self.rewindButton.setBackgroundImage(tintedRewindImage, for: .normal)
self.rewindButton.tintColor = KeyConstants.apsmDark
let origPlayImage = self.playButton.backgroundImage(for: .normal)
let tintedPlayImage = origPlayImage?.withRenderingMode(.alwaysTemplate)
self.playButton.setBackgroundImage(tintedPlayImage, for: .normal)
self.playButton.tintColor = KeyConstants.apsmDark
let origForwardImage = self.forwardButton.backgroundImage(for: .normal)
let tintedForwardImage = origForwardImage?.withRenderingMode(.alwaysTemplate)
self.forwardButton.setBackgroundImage(tintedForwardImage, for: .normal)
self.forwardButton.tintColor = KeyConstants.apsmDark
let origPreviousImage = self.previousTrack.backgroundImage(for: .normal)
let tintedPreviousImage = origPreviousImage?.withRenderingMode(.alwaysTemplate)
self.previousTrack.setBackgroundImage(tintedPreviousImage, for: .normal)
self.previousTrack.tintColor = KeyConstants.apsmDark
let origNextImage = self.nextTrack.backgroundImage(for: .normal)
let tintedNextImage = origNextImage?.withRenderingMode(.alwaysTemplate)
self.nextTrack.setBackgroundImage(tintedNextImage, for: .normal)
self.nextTrack.tintColor = KeyConstants.apsmDark
let origShuffledImage = self.shuffleButton.backgroundImage(for: .normal)
let tintedShuffledImage = origShuffledImage?.withRenderingMode(.alwaysTemplate)
self.shuffleButton.setBackgroundImage(tintedShuffledImage, for: .normal)
if self.isShuffled {
self.shuffleButton.tintColor = KeyConstants.apsmLight
} else {
self.shuffleButton.tintColor = KeyConstants.apsmDark
}
let origRepeatImage = self.repeatButton.backgroundImage(for: .normal)
let tintedRepeatImage = origRepeatImage?.withRenderingMode(.alwaysTemplate)
self.repeatButton.setBackgroundImage(tintedRepeatImage, for: .normal)
if self.isRepeated {
self.repeatButton.tintColor = KeyConstants.apsmLight
} else {
self.repeatButton.tintColor = KeyConstants.apsmDark
}
let asset = AVAsset(url: playableTrack.trackURL)
let assetKeys = [
"playable",
"hasProtectedContent"
]
self.avPlayerItem = AVPlayerItem(asset: asset,
automaticallyLoadedAssetKeys: assetKeys)
self.player = AVPlayer(playerItem: self.avPlayerItem)
let playerLayer=AVPlayerLayer(player: player!)
playerLayer.frame=CGRect(x: 0, y: 0, width: 300, height: 50)
self.view.layer.addSublayer(playerLayer)
self.progressSlider.minimumValue = 0
let duration : CMTime = self.avPlayerItem!.asset.duration
let seconds : Float64 = CMTimeGetSeconds(duration)
self.progressSlider.maximumValue = Float(seconds)
self.progressSlider.isContinuous = false
self.progressSlider.addTarget(self, action: #selector(self.playbackSliderValueChanged(_:)), for: .valueChanged)
self.player!.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: DispatchQueue.main) { (CMTime) -> Void in
if self.player != nil && self.player!.currentItem?.status == .readyToPlay {
let time : Float64 = CMTimeGetSeconds(self.player!.currentTime());
self.progressSlider!.value = Float ( time );
}
}
self.playTrack(self.playButton!)
var image = UIImage(named: "baseline_audiotrack_black_48pt")
if self.albumArt.image != nil {
image = self.albumArt.image
}
let albumArt = MPMediaItemArtwork.init(boundsSize: image!.size, requestHandler: { (size) -> UIImage in
return image!
})
self.mpic.nowPlayingInfo = [MPMediaItemPropertyTitle: playableTrack.albumName + playableTrack.trackName, MPMediaItemPropertyArtwork: albumArt]
self.commandCenter.playCommand.isEnabled = true
self.commandCenter.playCommand.addTarget(self, action: #selector(self.playTrackFromLock))
self.commandCenter.pauseCommand.isEnabled = true
self.commandCenter.pauseCommand.addTarget(self, action: #selector(self.pauseTrack))
self.commandCenter.stopCommand.isEnabled = true
self.commandCenter.stopCommand.addTarget(self, action: #selector(self.destroyPlay))
self.commandCenter.nextTrackCommand.isEnabled = true
self.commandCenter.nextTrackCommand.addTarget(self, action: #selector(self.clickNextTrack(_:)))
self.commandCenter.previousTrackCommand.isEnabled = true
self.commandCenter.previousTrackCommand.addTarget(self, action: #selector(self.clickPreviousTrack(_:)))
self.commandCenter.seekForwardCommand.isEnabled = true
}
@IBAction func playTrack(_ sender: Any) {
if self.player!.rate == 0.0
{
self.playTrackFromLock()
} else {
self.pauseTrack()
}
}
@objc func playTrackFromLock() {
self.player?.play()
let origPlayImage = UIImage(named: "baseline_pause_circle_outline_black_48pt")
let tintedPlayImage = origPlayImage?.withRenderingMode(.alwaysTemplate)
self.playButton.setBackgroundImage(tintedPlayImage, for: .normal)
self.playButton.tintColor = KeyConstants.apsmDark
}
@objc func pauseTrack() {
self.player?.pause()
let origPlayImage = UIImage(named: "baseline_play_circle_outline_black_48pt")
let tintedPlayImage = origPlayImage?.withRenderingMode(.alwaysTemplate)
self.playButton.setBackgroundImage(tintedPlayImage, for: .normal)
self.playButton.tintColor = KeyConstants.apsmDark
}
@IBAction func rewind(_ sender: Any) {
let preferredTimeScale : Int32 = 1
self.player?.seek(to: self.player!.currentTime()-CMTime(seconds: 5.0, preferredTimescale: preferredTimeScale))
}
@IBAction func forward(_ sender: Any) {
let preferredTimeScale : Int32 = 1
self.player?.seek(to: self.player!.currentTime()+CMTime(seconds: 5.0, preferredTimescale: preferredTimeScale))
}
@IBAction func clickNextTrack(_ sender: Any) {
self.player?.pause()
var toPlayNext = true
self.currentTrackIndex += 1
if self.currentTrackIndex >= self.playableAudioItems.count {
if self.isRepeated {
self.currentTrackIndex = 0
} else {
self.currentTrackIndex = self.playableAudioItems.count - 1
toPlayNext = false
}
}
if toPlayNext {
self.initialisePlayerAndPlay()
} else {
self.destroyPlay()
}
}
@IBAction func clickPreviousTrack(_ sender: Any) {
self.player?.pause()
var toPlayNext = true
self.currentTrackIndex -= 1
if self.currentTrackIndex < 0 {
if self.isRepeated {
self.currentTrackIndex = self.playableAudioItems.count - 1
} else {
self.currentTrackIndex = 0
toPlayNext = false
}
}
if toPlayNext {
self.initialisePlayerAndPlay()
} else {
self.destroyPlay()
}
}
@IBAction func clickSuffle(_ sender: Any) {
if self.isShuffled {
self.isShuffled = false
} else {
self.isShuffled = true
}
if self.isShuffled {
self.playableAudioItems.shuffle()
} else {
self.playableAudioItems = self.playableAudioItemsUnshuffled
}
let origShuffledImage = self.shuffleButton.backgroundImage(for: .normal)
let tintedShuffledImage = origShuffledImage?.withRenderingMode(.alwaysTemplate)
self.shuffleButton.setBackgroundImage(tintedShuffledImage, for: .normal)
if self.isShuffled {
self.shuffleButton.tintColor = KeyConstants.apsmLight
} else {
self.shuffleButton.tintColor = KeyConstants.apsmDark
}
UserDefaults.standard.set(self.isShuffled, forKey: "TracksShuffled")
}
@IBAction func clickRepeat(_ sender: Any) {
if self.isRepeated {
self.isRepeated = false
} else {
self.isRepeated = true
}
let origRepeatImage = self.repeatButton.backgroundImage(for: .normal)
let tintedRepeatImage = origRepeatImage?.withRenderingMode(.alwaysTemplate)
self.repeatButton.setBackgroundImage(tintedRepeatImage, for: .normal)
if self.isRepeated {
self.repeatButton.tintColor = KeyConstants.apsmLight
} else {
self.repeatButton.tintColor = KeyConstants.apsmDark
}
UserDefaults.standard.set(self.isRepeated, forKey: "TracksRepeatable")
}
@objc func playbackSliderValueChanged(_ playbackSlider:UISlider)
{
let seconds : Int64 = Int64(playbackSlider.value)
let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 5)
self.player!.seek(to: targetTime)
if self.player!.rate == 0
{
self.player?.play()
let origPlayImage = UIImage(named: "baseline_pause_circle_outline_black_48pt")
let tintedPlayImage = origPlayImage?.withRenderingMode(.alwaysTemplate)
self.playButton.setBackgroundImage(tintedPlayImage, for: .normal)
self.playButton.tintColor = KeyConstants.apsmDark
}
}
@objc func finishedPlaying(myNotification:NSNotification) {
self.clickNextTrack(self.nextTrack!);
}
@objc func destroyPlay() {
self.player?.pause()
self.avPlayerItem = nil
self.player = nil
self.mpic.nowPlayingInfo = [MPMediaItemPropertyTitle: ""]
self.commandCenter.playCommand.isEnabled = false
self.commandCenter.pauseCommand.isEnabled = false
self.commandCenter.nextTrackCommand.isEnabled = false
self.commandCenter.previousTrackCommand.isEnabled = false
}
}
这就是我所说的上述控制器的方式:
let nextViewController = storyboard?.instantiateViewController(withIdentifier: "AudioPlayer") as! AudioPlayer
nextViewController.currentTrack = currentTrack
self.show(nextViewController, sender: Any?.self)
我真正想要的是获取在后台播放的歌曲的AVPlayer
和AVPlayerItem
的实例,以便我可以重新控制播放。与Amazon Prime Music Player相似,其中播放器的后退按钮最小化,单击播放器后再次扩展。
答案 0 :(得分:0)
使用“ var avPlayerItem:AVPlayerItem?” 并 “音频播放器:AVPlayer?” 在类“ AudioPlayer” 之外作为全局变量,以在其他视图控制器上访问它