Thanks for visiting this question.
I recently started learning swift. I'm making an audio player ish app.
The nextButton works only for a couple of times, and then it just becomes silent. I'm trying to make an array loop over and over if nextButton is being pressed. So, it means when I press the "next" button, it will go to the next track and if tracks from array finished, it will play all track over again.
All I'm trying to achieve it to get the "next" button work without any stops.
Array and Audio Player:
let sound1 = NSURL(fileURLWithPath: Bundle.main.path(forResource: "1", ofType: "mp3")!)
let sound2 = NSURL(fileURLWithPath: Bundle.main.path(forResource: "2", ofType: "mp3")!)
let sound3 = NSURL(fileURLWithPath: Bundle.main.path(forResource: "3", ofType: "mp3")!)
lazy var soundArray: [NSURL] = [sound1, sound2, sound3]
var audioPlayer = AVAudioPlayer()
var run = true
var currentIndex = 0
func playRandomSound() {
let randNo = Int(arc4random_uniform(UInt32(soundArray.count)))
if run == true{
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
try AVAudioSession.sharedInstance().setActive(true)
try audioPlayer = AVAudioPlayer(contentsOf: soundArray[randNo] as URL)
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print(error)
}
}
}
nextButton:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
currentIndex = indexPath.row
}
#IBAction func nextButton(_ sender: Any) {
audioPlayer.stop()
print("Audio did stop")
if currentIndex + 1 < soundArray.count {
currentIndex += 1
playRandomSound()
}
}
Full Code:
import UIKit
import AVFoundation
extension UILabel {
func pushUp(_ text: String?) {
let animation:CATransition = CATransition()
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.type = CATransitionType.push
animation.subtype = CATransitionSubtype.fromTop
animation.duration = 1
self.layer.add(animation, forKey: CATransitionType.push.rawValue)
self.text = text
}
}
class HideShow: UIViewController, AVAudioPlayerDelegate{
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var moveOn: UILabel!
#IBOutlet weak var Logo: UILabel!
#IBOutlet weak var BGimage: UIImageView!
#IBOutlet weak var nextButton2: UIButton!
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var LayDown: UILabel!
#IBOutlet weak var audioChanger: UIButton!
#IBOutlet weak var buttonView: UIView!
let notification = NotificationCenter.default
let toImage = UIImage(named:"BG2.jpeg")
#IBAction func buttonTapped(_ sender: UIButton){
UIView.animate(withDuration: 2) {
self.LayDown.alpha = 1
}
UIView.transition(with: self.BGimage,
duration: 1,
options: .transitionCrossDissolve,
animations: { self.BGimage.image = self.toImage },
completion: nil)
LayDown.pushUp("CLOSE YOUR EYES")
self.nextButton.isHidden = true
self.nextButton2.isHidden = false
}
let sound1 = NSURL(fileURLWithPath: Bundle.main.path(forResource: "1", ofType: "mp3")!)
let sound2 = NSURL(fileURLWithPath: Bundle.main.path(forResource: "2", ofType: "mp3")!)
let sound3 = NSURL(fileURLWithPath: Bundle.main.path(forResource: "3", ofType: "mp3")!)
lazy var soundArray: [NSURL] = [sound1, sound2, sound3]
var audioPlayer = AVAudioPlayer()
var run = true
var currentIndex = 0
func playRandomSound() {
let randNo = Int(arc4random_uniform(UInt32(soundArray.count)))
if run == true{
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
try AVAudioSession.sharedInstance().setActive(true)
try audioPlayer = AVAudioPlayer(contentsOf: soundArray[randNo] as URL)
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print(error)
}
}
}
#IBAction func buttonTapped2(_ sender: Any) {
UIView.animate(withDuration: 1) {
self.nextButton2.alpha = 0
self.LayDown.alpha = 0
self.nextButton.alpha = 0
self.BGimage.alpha = 0
self.moveOn.alpha = 0
self.Logo.alpha = 0
self.audioChanger.isHidden = false
self.buttonView.isHidden = false
}
playRandomSound()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
currentIndex = indexPath.row
}
#IBAction func nextButton(_ sender: Any) {
audioPlayer.stop()
print("Audio did stop")
if currentIndex + 1 < soundArray.count {
currentIndex += 1
playRandomSound()
}
}
}
Let me know if you have any questions,
Thanks in advance!:)
Related
I have a button in my app that I want to play a series of sound files at random. I've followed instructions from similar posts about this, but when I hit the play button, it plays a random sound file upon starting the emulator, but repeatedly. It doesn't shuffle through them on each button press. Below is the full code I have for my ViewController. Thanks for any help!
import UIKit
import AVFoundation
import AudioToolbox
class ViewController: UIViewController, AVAudioPlayerDelegate {
var audioPlayerHyeeeehLong: AVAudioPlayer = AVAudioPlayer()
var audioPlayerHyeeeehShort: AVAudioPlayer = AVAudioPlayer()
var beWellAudioPlayer: AVAudioPlayer = AVAudioPlayer()
#IBOutlet weak var beWellButton: UIButton!
#IBOutlet weak var restoreMindBodyButton: UIButton!
#IBOutlet weak var spiritualCleanseButton: UIButton!
var randomIndex = 0
var soundFiles = ["sound1", "sound2", "sound3", "sound4", "sound5"]
override func viewDidLoad() {
super.viewDidLoad()
// play full spiritual cleanse
let hyeeeeh = Bundle.main.path(forResource: "hyeeeeh", ofType: "m4a")
do{
audioPlayerHyeeeehLong = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: hyeeeeh!))
}catch{
print(error)
}
// play restore mind body
let hyeeeehShort = Bundle.main.path(forResource: "hyeeeeh1", ofType: "m4a")
do{
audioPlayerHyeeeehShort = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: hyeeeehShort!))
}catch{
print(error)
}
// play be well
randomIndex = Int(arc4random_uniform(UInt32(soundFiles.count)))
let selectedFileName = soundFiles[randomIndex]
let beWell = Bundle.main.path(forResource: selectedFileName, ofType: "m4a")
do{
beWellAudioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: beWell!))
}catch{
print(error)
}
}
#IBAction func beWell(_ sender: Any) {
print("Be Well Button Pressed")
beWellAudioPlayer.play()
}
#IBAction func playShortHyeeh(_ sender: Any) {
audioPlayerHyeeeehShort.play()
}
#IBAction func playFullHyeeeh(_ sender: Any) {
audioPlayerHyeeeehLong.play()
}
}
EDIT:
Below is the fixed code. Everything works! Thanks for your help, R.B Niranjan!
import UIKit
import AVFoundation
import AudioToolbox
class ViewController: UIViewController, AVAudioPlayerDelegate {
var audioPlayerHyeeeehLong: AVAudioPlayer = AVAudioPlayer()
var audioPlayerHyeeeehShort: AVAudioPlayer = AVAudioPlayer()
var beWellAudioPlayer: AVAudioPlayer = AVAudioPlayer()
#IBOutlet weak var beWellButton: UIButton!
#IBOutlet weak var restoreMindBodyButton: UIButton!
#IBOutlet weak var spiritualCleanseButton: UIButton!
var randomIndex = 0
var soundFiles = ["sound1", "sound2", "sound3", "sound4", "sound5"]
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func beWell(_ sender: Any) {
randomIndex = Int(arc4random_uniform(UInt32(soundFiles.count)))
let selectedFileName = soundFiles[randomIndex]
let beWell = Bundle.main.path(forResource: selectedFileName, ofType: "m4a")
do{
beWellAudioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: beWell!))
}catch{
print(error)
}
if beWellAudioPlayer.isPlaying{
beWellAudioPlayer.pause()
}
beWellAudioPlayer.currentTime = 0
beWellAudioPlayer.play()
}
#IBAction func playShortHyeeh(_ sender: Any) {
let hyeeeehShort = Bundle.main.path(forResource: "hyeeeeh1", ofType: "m4a")
do{
audioPlayerHyeeeehShort = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: hyeeeehShort!))
}catch{
print(error)
}
if audioPlayerHyeeeehShort.isPlaying{
audioPlayerHyeeeehShort.pause()
}
audioPlayerHyeeeehShort.currentTime = 0
audioPlayerHyeeeehShort.play()
}
#IBAction func playFullHyeeeh(_ sender: Any) {
let hyeeeeh = Bundle.main.path(forResource: "hyeeeeh", ofType: "m4a")
do{
audioPlayerHyeeeehLong = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: hyeeeeh!))
}catch{
print(error)
}
if audioPlayerHyeeeehLong.isPlaying{
audioPlayerHyeeeehLong.pause()
}
audioPlayerHyeeeehLong.currentTime = 0
audioPlayerHyeeeehLong.play()
}
}
Selecting a random audio file and playing should be done on a button click.
#IBAction func beWell(_ sender: Any) {
print("Be Well Button Pressed")
randomIndex = Int(arc4random_uniform(UInt32(soundFiles.count)))
let selectedFileName = soundFiles[randomIndex]
let beWell = Bundle.main.path(forResource: selectedFileName, ofType: "m4a")
do{
beWellAudioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: beWell!))
}catch{
print(error)
}
beWellAudioPlayer.play()
}
I was doing the weather app, everything was fine until I add the CAGradientLayer transition to UIView. It still worked but the transition took so long, around 10-15 seconds. I'm using the Apixu API for the weather. One thing to notice is the audio was perfectly playing, right away. I have checked the duration and there was nothing wrong with animation:
import UIKit
import AVFoundation
class ViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var cityLbl: UILabel!
#IBOutlet weak var conditionLbl: UILabel!
#IBOutlet weak var degreeLbl: UILabel!
#IBOutlet weak var imgView: UIImageView!
#IBOutlet weak var gradientView: UIView!
let gradientLayer = CAGradientLayer()
let gradientLayer2 = CAGradientLayer()
let gradientLayer3 = CAGradientLayer()
var degree: Int!
var condition: String!
var imgURL: String!
var city: String!
var exists: Bool = true
var audioPlayer1 = AVAudioPlayer()
var audioPlayer2 = AVAudioPlayer()
var audioPlayer3 = AVAudioPlayer()
var audioPlayerSunny = AVAudioPlayer()
var audioPlayerSnow = AVAudioPlayer()
var audioPlayerThunder = AVAudioPlayer()
// Audio part.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
gradientLayer.frame = gradientView.bounds
gradientLayer.colors = [UIColor.blue.cgColor, UIColor.black.cgColor]
gradientView.layer.insertSublayer(gradientLayer, at: 0)
//gradient color view
gradientLayer2.colors = [UIColor.red.cgColor, UIColor.purple.cgColor]
//gradient color view 2
gradientLayer3.colors = [UIColor.orange.cgColor, UIColor.green.cgColor]
//gradient color view 3
searchBar.delegate = self
// Audio Code starts.
do {
audioPlayer1 = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "ambiance", ofType: "mp3")!))
audioPlayer1.prepareToPlay()
audioPlayer2 = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "guitar", ofType: "mp3")!))
audioPlayer2.prepareToPlay()
audioPlayer3 = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "rain", ofType: "mp3")!))
audioPlayer3.prepareToPlay()
audioPlayerSunny = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "sunny", ofType: "mp3")!))
audioPlayerSunny.prepareToPlay()
audioPlayerSnow = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "snow", ofType: "mp3")!))
audioPlayerSnow.prepareToPlay()
audioPlayerThunder = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "thunder", ofType: "mp3")!))
audioPlayerThunder.prepareToPlay()
}
catch {
print(error)
} // Audio Code ends.
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
searchBar.resignFirstResponder()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
let urlRequest = URLRequest(url: URL(string: "http://api.apixu.com/v1/current.json?key=494942fa74444eabb6973325170510&q=\(searchBar.text!.replacingOccurrences(of: " ", with: "%20"))")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
if let current = json["current"] as? [String : AnyObject] {
if let temp = current["temp_c"] as? Float {
self.degree = Int(temp.rounded())
}
if let condition = current["condition"] as? [String : AnyObject] {
self.condition = condition["text"] as! String
let icon = condition["icon"] as! String
self.imgURL = "http:\(icon)"
}
}
if let location = json["location"] as? [String : AnyObject] {
self.city = location["name"] as! String
}
if let _ = json["error"] {
self.exists = false
}
if self.condition == "Sunny" || self.condition == "Clear" {
self.audioPlayerSunny.currentTime = 0
self.audioPlayerSunny.play()
self.audioPlayer1.stop()
self.audioPlayer3.stop()
self.audioPlayer2.stop()
self.audioPlayerSnow.stop()
self.audioPlayerThunder.stop()
// Audio Code.
let colorChangeAnimation = CABasicAnimation(keyPath: "colors")
colorChangeAnimation.duration = 1.0
colorChangeAnimation.toValue = self.gradientLayer3.colors
colorChangeAnimation.fillMode = kCAFillModeForwards
colorChangeAnimation.isRemovedOnCompletion = false
self.gradientLayer.add(colorChangeAnimation, forKey: "colorChange")
//effects
} else if self.condition.lowercased().range(of:"cloudy") != nil || self.condition.lowercased().range(of:"overcast") != nil {
self.audioPlayer1.currentTime = 0
self.audioPlayer1.play()
self.audioPlayer2.stop()
self.audioPlayer3.stop()
self.audioPlayerSnow.stop()
self.audioPlayerSunny.stop()
self.audioPlayerThunder.stop()
// Audio Code.
let colorChangeAnimation = CABasicAnimation(keyPath: "colors")
colorChangeAnimation.duration = 1.0
colorChangeAnimation.toValue = self.gradientLayer2.colors
colorChangeAnimation.fillMode = kCAFillModeForwards
colorChangeAnimation.isRemovedOnCompletion = false
self.gradientLayer.add(colorChangeAnimation, forKey: "colorChange")
//effects
} else if self.condition.lowercased().range(of:"snow") != nil || self.condition.lowercased().range(of:"snowy") != nil || self.condition.lowercased().range(of:"sleet") != nil
{
self.audioPlayerSnow.currentTime = 0
self.audioPlayerSnow.play()
self.audioPlayer2.stop()
self.audioPlayer3.stop()
self.audioPlayer1.stop()
self.audioPlayerSunny.stop()
self.audioPlayerThunder.stop()
// Audio Code.
} else if self.condition.lowercased().range(of:"rain") != nil || self.condition.lowercased().range(of:"rainy") != nil || self.condition.lowercased().range(of:"drizzle") != nil
{
self.audioPlayer3.currentTime = 0
self.audioPlayer3.play()
self.audioPlayer2.stop()
self.audioPlayer1.stop()
self.audioPlayerSnow.stop()
self.audioPlayerSunny.stop()
self.audioPlayerThunder.stop()
// Audio Code.
} else if self.condition.lowercased().range(of:"thunder") != nil || self.condition.lowercased().range(of:"thundery") != nil {
self.audioPlayerThunder.currentTime = 0
self.audioPlayerThunder.play()
self.audioPlayer2.stop()
self.audioPlayer3.stop()
self.audioPlayerSnow.stop()
self.audioPlayerSunny.stop()
self.audioPlayer1.stop()
// Audio Code.
} else {
self.audioPlayer2.currentTime = 0
self.audioPlayer2.play()
self.audioPlayer1.stop()
self.audioPlayer3.stop()
self.audioPlayerSnow.stop()
self.audioPlayerSunny.stop()
self.audioPlayerThunder.stop()
// Audio Code.
}
DispatchQueue.main.async {
if self.exists{
self.degreeLbl.isHidden = false
self.conditionLbl.isHidden = false
self.imgView.isHidden = false
self.degreeLbl.text = "\(self.degree.description)°"
self.cityLbl.text = self.city
self.conditionLbl.text = self.condition
self.imgView.downloadImage(from: self.imgURL!)
}
else {
self.degreeLbl.isHidden = true
self.conditionLbl.isHidden = true
self.imgView.isHidden = true
self.cityLbl.text = "No matching city found"
self.exists = true
}
}
} catch let jsonError {
print(jsonError.localizedDescription)
}
}
}
task.resume()
}
}
extension UIImageView {
func downloadImage(from url: String) {
let urlRequest = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error == nil {
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
}
task.resume()
}
}
I have a tableview and have multiple cells in tableView.
Each cell has an item of AVAudioPlayer
And I face a problem.
I don't know how to manage the AVAudioPlayer.
When I play first AVAudioPlayer, and then play second AVAudioPlayer, the sound will overlap.
How to stop first AVAudioPlayer in my customized cell, and play second AVAudioPlayer?
Thanks.
This is my customized cell:
class TableViewCell: UITableViewCell {
#IBOutlet weak var myImageView: UIImageView!
#IBOutlet weak var myChatBubbleView: UIView!
#IBOutlet weak var myDateLabel: UILabel!
#IBOutlet weak var mySecondLabel: UILabel!
#IBOutlet weak var myRecordPlayerBtn: MenuButton!
private var timer:Timer?
private var elapsedTimeInSecond:Int = 0
var audioPlayer:AVAudioPlayer?
var message:ChatroomMessage?
var chatroomId:String = ""
var delegate:PlayRecordDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.backgroundColor = defaultBackgroundColor
self.tintColor = defaultChatroomCheckButtonColor
myImageView.layer.masksToBounds = true
myImageView.layer.cornerRadius = defaultIconRadius
myChatBubbleView.backgroundColor = defaultChatGreenBubbleColor
myChatBubbleView.layer.cornerRadius = defaultButtonRadius
myDateLabel.textColor = defaultChatTimeColor
mySecondLabel.textColor = defaultChatTimeColor
mySecondLabel.isHidden = true
myRecordPlayerBtn.imageView?.animationDuration = 1
myRecordPlayerBtn.imageView?.animationImages = [
UIImage(named: "img_myRocordPlaying1")!,
UIImage(named: "img_myRocordPlaying2")!,
UIImage(named: "img_myRocordPlaying3")!
]
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func loadByMessage(_ message:ChatroomMessage, chatroomId:String) {
self.message = message
self.chatroomId = chatroomId
myRecordPlayerBtn.addTarget(self, action: #selector(recordPlay), for: .touchUpInside)
}
func resetRecordAnimation() {
self.myRecordPlayerBtn.imageView!.stopAnimating()
self.myRecordPlayerBtn.isSelected = false
}
func recordPlay(_ sender: UIButton) {
self.myRecordPlayerBtn.imageView?.startAnimating()
let documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("\(chatroomId)/Record/")
let fileName = message?.content.substring(from: 62)
let fileURL = documentsDirectoryURL.appendingPathComponent(fileName!)
if FileManager.default.fileExists(atPath: fileURL.path) {
let asset = AVURLAsset(url: URL(fileURLWithPath: fileURL.path), options: nil)
let audioDuration = asset.duration
let audioDurationSeconds = CMTimeGetSeconds(audioDuration)
self.elapsedTimeInSecond = Int(audioDurationSeconds)
if audioPlayer?.isPlaying == true {
audioPlayer?.stop()
DispatchQueue.main.async {
self.resetTimer(second: self.elapsedTimeInSecond)
self.startTimer()
}
}
updateTimeLabel()
startTimer()
audioPlayer = try? AVAudioPlayer(contentsOf: fileURL)
audioPlayer?.delegate = self
audioPlayer?.play()
}else{
//don't have file in local
let recordUrl = URL(string: (message?.content)!)
URLSession.shared.downloadTask(with: recordUrl!, completionHandler: { (location, response, error) in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("audio"),
let location = location, error == nil
else { return }
do {
try FileManager.default.moveItem(at: location, to: fileURL)
let asset = AVURLAsset(url: URL(fileURLWithPath: fileURL.path), options: nil)
let audioDuration = asset.duration
let audioDurationSeconds = CMTimeGetSeconds(audioDuration)
self.elapsedTimeInSecond = Int(audioDurationSeconds)
DispatchQueue.main.async {
self.updateTimeLabel()
self.startTimer()
}
self.audioPlayer = try? AVAudioPlayer(contentsOf: fileURL)
self.audioPlayer?.delegate = self
self.audioPlayer?.play()
} catch {
print(error)
}
}).resume()
}
}
func startTimer() {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { (timer) in
self.elapsedTimeInSecond -= 1
self.updateTimeLabel()
})
}
func resetTimer(second:Int) {
timer?.invalidate()
elapsedTimeInSecond = second
updateTimeLabel()
}
func updateTimeLabel() {
let seconds = elapsedTimeInSecond % 60
let minutes = (elapsedTimeInSecond/60) % 60
mySecondLabel.isHidden = false
mySecondLabel.text = String(format: "%02d:%02d", minutes,seconds)
}
}
extension TableViewCell:AVAudioPlayerDelegate {
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
let documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("\(Id)/Record/")
let fileName = message?.content.substring(from: 62)
let fileURL = documentsDirectoryURL.appendingPathComponent(fileName!)
if FileManager.default.fileExists(atPath: fileURL.path) {
let asset = AVURLAsset(url: URL(fileURLWithPath: fileURL.path), options: nil)
let audioDuration = asset.duration
let audioDurationSeconds = CMTimeGetSeconds(audioDuration)
DispatchQueue.main.async {
self.resetTimer(second: Int(audioDurationSeconds))
self.myRecordPlayerBtn.imageView!.stopAnimating()
self.myRecordPlayerBtn.imageView?.image = #imageLiteral(resourceName: "img_myRocordDefault")
}
}
}
}
Probably first initialize to check if your player is playing
if audioPlayer != nil{
if audioPlayer?.isPlaying == true {
audioPlayer?.stop()
DispatchQueue.main.async {
self.resetTimer(second: self.elapsedTimeInSecond)
self.startTimer()
}
}
}
If you don't want to play two audio track at the same time, you should use a shared instance of AVAudioPlayer
It will be better for performances and you can define the instance as static var in your controller. It will be accessible in each cell.
I have developed a music palyer application, and I used a shared instance in the MusicPlayManager:
class MusicPlayManager{
var player : AVAudioPlayer?
static let sharedInstance = MusicPlayManager.init()
private override init() {
super.init()
}
// something else, such as palyNext, playPrevious methods
}
In your viewController,you can use MusicPlayManager.sharedInstance.player
When I press pause and then play, the music playing again how to solve this problem?
I know that I need to use seekTime(: CMTime), but I don't get
Gif
It's all code
class ViewControllerAudioDetail: UIViewController {
#IBOutlet var list: TableViewControllerAudioList!
#IBOutlet weak var playChange: UIButton!
#IBOutlet weak var timeAudio: UILabel!
#IBOutlet weak var playbackSlider: UISlider!
var player = AVPlayer()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "БИБЛИОТЕКА"
}
#IBAction func play(_ sender: Any) {
if player.rate == 0 {
let url = URL(string: ViewControllerAudioDetail.urlAudio[0])
let playerItem = AVPlayerItem(url: url!)
player = AVPlayer(playerItem:playerItem)
player.rate = 1.0;
player.play()
playChange.setImage(UIImage(named:"Pause.png"), for: UIControlState.normal)
let duration : CMTime = playerItem.asset.duration
let seconds : Float64 = CMTimeGetSeconds(duration)
playbackSlider.tintColor = UIColor.green
let _ = player.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), queue: DispatchQueue.main) { [weak self] (time) in
self?.playbackSlider.value = Float(CMTimeGetSeconds(time)) / Float(seconds)
}
} else {
playChange.setImage(UIImage(named:"Play.png"), for: UIControlState.normal)
player.pause()
}
}
#IBAction func audioPlaybackSlider(_ sender: Any) {
//перемотка аудиозвука
let duration : CMTime = player.currentItem!.duration
let totalDuration : Float64 = CMTimeGetSeconds(duration)
let value = self.playbackSlider.value
let durationToSeek = Float(totalDuration) * value
player.seek(to: CMTimeMakeWithSeconds(Float64(durationToSeek),player.currentItem!.duration.timescale)) { [](state) in
}
}
}
The problem in your code is, you always create a new player object inside your play method. That is why it always starts from the beginning of the audio.
This should solve your problem
class ViewControllerAudioDetail: UIViewController {
#IBOutlet var list: TableViewControllerAudioList!
#IBOutlet weak var playChange: UIButton!
#IBOutlet weak var timeAudio: UILabel!
#IBOutlet weak var playbackSlider: UISlider!
var player:AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "БИБЛИОТЕКА"
setupAudioPlayer()
}
func setupAudioPlayer(){
let url = URL(string: ViewControllerAudioDetail.urlAudio[0])
let playerItem = AVPlayerItem(url: url!)
player = AVPlayer(playerItem:playerItem)
playbackSlider.tintColor = UIColor.green
let _ = player!.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), queue: DispatchQueue.main) { [weak self] (time) in
self?.updateSlider(time: time)
}
}
func updateSlider(time:CMTime){
let duration = CMTimeGetSeconds(player!.currentItem!.asset.duration)
self.playbackSlider.value = Float(CMTimeGetSeconds(time)) / Float(duration)
}
#IBAction func play(_ sender: Any) {
if player?.rate == 0 {
player?.rate = 1.0;
player?.play()
playChange.setImage(UIImage(named:"Pause.png"), for: UIControlState.normal)
} else {
playChange.setImage(UIImage(named:"Play.png"), for: UIControlState.normal)
player?.pause()
}
}
#IBAction func audioPlaybackSlider(_ sender: Any) {
//перемотка аудиозвука
let duration = CMTimeGetSeconds(player!.currentItem!.asset.duration)
let value = self.playbackSlider.value
let durationToSeek = Float(duration) * value
self.player?.seek(to: CMTimeMakeWithSeconds(Float64(durationToSeek),player!.currentItem!.duration.timescale)) { [](state) in
}
}
}
I'm quite new to swift and I want to implement a piece of code that helps me reach the next song when the one that is currently playing ends.
I tried to copy the code inside my "#IBAction func nextAction" (which works fine):
#IBAction func nextAction(sender: AnyObject) {
self.nextTrack()
}
func nextTrack() {
if trackId == 0 || trackId < 4 {
if shuffle.on {
trackId = Int(arc4random_uniform(UInt32(library.count)))
}else {
trackId += 1
}
if let coverImage = library[trackId]["coverImage"]{
coverImageView.image = UIImage(named: "\(coverImage).jpg")
}
songTitleLabel.text = library[trackId]["title"]
artistLabel.text = library[trackId]["artist"]
audioPlayer.currentTime = 0
progressView.progress = 0
let path = NSBundle.mainBundle().pathForResource("\(trackId)", ofType: "mp3")
if let path = path {
let mp3URL = NSURL(fileURLWithPath: path)
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: mp3URL)
audioPlayer.play()
NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(PlayerViewController.updateProgressView), userInfo: nil, repeats: true)
progressView.setProgress(Float(audioPlayer.currentTime/audioPlayer.duration), animated: false)
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
}
And tried to put it inside an if condition like this (inside the viewDidLoad):
if audioPlayer.currentTime >= audioPlayer.duration {
self.nextTrack()
}
I don't have any errors but at runtime this method isn't working and the song ends without playing the next one.
To make the situation more clear here's my controller:
import UIKit
import AVFoundation
class PlayerViewController: UIViewController {
#IBOutlet weak var coverImageView: UIImageView!
#IBOutlet weak var progressView: UIProgressView!
#IBOutlet weak var songTitleLabel: UILabel!
#IBOutlet weak var artistLabel: UILabel!
#IBOutlet weak var shuffle: UISwitch!
var trackId: Int = 0
var library = MusicLibrary().library
var audioPlayer: AVAudioPlayer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if let coverImage = library[trackId]["coverImage"]{
coverImageView.image = UIImage(named: "\(coverImage).jpg")
}
songTitleLabel.text = library[trackId]["title"]
artistLabel.text = library[trackId]["artist"]
let path = NSBundle.mainBundle().pathForResource("\(trackId)", ofType: "mp3")
if let path = path {
let mp3URL = NSURL(fileURLWithPath: path)
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: mp3URL)
audioPlayer.play()
NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(PlayerViewController.updateProgressView), userInfo: nil, repeats: true)
progressView.setProgress(Float(audioPlayer.currentTime/audioPlayer.duration), animated: false)
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
override func viewWillDisappear(animated: Bool) {
audioPlayer.stop()
}
func updateProgressView(){
if audioPlayer.playing {
progressView.setProgress(Float(audioPlayer.currentTime/audioPlayer.duration), animated: true)
}
}
#IBAction func playAction(sender: AnyObject) {
if !audioPlayer.playing {
audioPlayer.play()
}
}
#IBAction func stopAction(sender: AnyObject) {
audioPlayer.stop()
audioPlayer.currentTime = 0
progressView.progress = 0
}
#IBAction func pauseAction(sender: AnyObject) {
audioPlayer.pause()
}
#IBAction func fastForwardAction(sender: AnyObject) {
var time: NSTimeInterval = audioPlayer.currentTime
time += 5.0
if time > audioPlayer.duration {
stopAction(self)
}else {
audioPlayer.currentTime = time
}
}
#IBAction func rewindAction(sender: AnyObject) {
var time: NSTimeInterval = audioPlayer.currentTime
time -= 5.0
if time < 0 {
stopAction(self)
}else {
audioPlayer.currentTime = time
}
}
#IBAction func previousAction(sender: AnyObject) {
if trackId != 0 || trackId > 0 {
if shuffle.on {
trackId = Int(arc4random_uniform(UInt32(library.count)))
}else {
trackId -= 1
}
if let coverImage = library[trackId]["coverImage"]{
coverImageView.image = UIImage(named: "\(coverImage).jpg")
}
songTitleLabel.text = library[trackId]["title"]
artistLabel.text = library[trackId]["artist"]
audioPlayer.currentTime = 0
progressView.progress = 0
let path = NSBundle.mainBundle().pathForResource("\(trackId)", ofType: "mp3")
if let path = path {
let mp3URL = NSURL(fileURLWithPath: path)
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: mp3URL)
audioPlayer.play()
NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(PlayerViewController.updateProgressView), userInfo: nil, repeats: true)
progressView.setProgress(Float(audioPlayer.currentTime/audioPlayer.duration), animated: false)
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
}
#IBAction func swipeDownAction(sender: AnyObject) {
self.close()
}
#IBAction func closeAction(sender: AnyObject) {
self.close()
}
#IBAction func nextAction(sender: AnyObject) {
self.nextTrack()
}
func close() {
self.dismissViewControllerAnimated(true, completion: nil)
}
func nextTrack() {
if trackId == 0 || trackId < 4 {
if shuffle.on {
trackId = Int(arc4random_uniform(UInt32(library.count)))
}else {
trackId += 1
}
if let coverImage = library[trackId]["coverImage"]{
coverImageView.image = UIImage(named: "\(coverImage).jpg")
}
songTitleLabel.text = library[trackId]["title"]
artistLabel.text = library[trackId]["artist"]
audioPlayer.currentTime = 0
progressView.progress = 0
let path = NSBundle.mainBundle().pathForResource("\(trackId)", ofType: "mp3")
if let path = path {
let mp3URL = NSURL(fileURLWithPath: path)
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: mp3URL)
audioPlayer.play()
NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(PlayerViewController.updateProgressView), userInfo: nil, repeats: true)
progressView.setProgress(Float(audioPlayer.currentTime/audioPlayer.duration), animated: false)
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
}
}
All the code is written in Xcode 7.3.1
You should use AVAudioPlayer delegate method audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) which is called when the audio player finishes playing a sound.
Make your PlayerViewController confirm to AVAudioPlayerDelegate protocol like this:
class PlayerViewController: UIViewController, AVAudioPlayerDelegate {
Make sure to set self as the delegate of the audioPlayer you create, to do that in your viewDidLoad,previousAction and nextTrack method you need to add
audioPlayer.delegate = self
after this line:
audioPlayer = try AVAudioPlayer(contentsOfURL: mp3URL)
Now you can use the delegate method to know when the audio is finished playing and go to the next track, just add this inside your class:
func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
if flag {
self.nextTrack()
} else {
// did not finish successfully
}
}