Can't get AVFoundation Pause () working in CollectionView - swift

In my app I have a collectionViewCell that plays music, In the collectionViewCell class I made the player variable
var player: AVAudioPlayer?
I also tried:
var player = AVAudioplayer()
I made a method pauseMusic ()
func pauseMusic (){
if player != nil {
if player!.playing == true{
player!.pause()
}
}
}
When I call this pauseMusic() method from my FirstViewController in the UIBarButtonItem action, my app crashes
#IBAction func pauseButton(sender: UIBarButtonItem) {
var colVW = CollectionViewCell()
colVW.pauseMusic()
}
I also uploaded this screenshot that shows the error when it crashes
http://nl.tinypic.com/r/t66zir/8
Is anybody familiar with this crash?
I would really appreciate if you could give me some advice!
Additional code:
This func checks which song has to be played, this func lives in the CollectionView. Because of the link between this func and the CollectionViewCell class, I can't move the AVPlayer somewhere else..
func prepareAudios() {
var songIndexString = (imageOutlet.titleLabel?.text)!
//println(songIndexString)
var songIndex = (songIndexString.toInt())!
println("songIndex: \(songIndex)")
if musicStopped == true {
rateButton()
println("empty")
} else if var filePath = NSBundle.mainBundle().pathForResource(audioArray[songIndex], ofType: "mp3"){
var filePathUrl = NSURL.fileURLWithPath(filePath)
player = AVAudioPlayer(contentsOfURL: filePathUrl, error: nil)
player!.play()
} else {
println("Path for audio file not found")
}
}

Related

Adding Audio Controls in iOS App using Swift in Xcode

I have added background music to my app using the following code:-
Now I am trying to add audio controls to my app using the following methods, but after I click on another view controller and come back to the main view controller the play button is not working. Any help will be highly appreciated Thanks!
var toggleState = 1
var music: AVAudioPlayer!
override func viewDidLoad() {
super.viewDidLoad()
playMusic()
}
func playMusic() {
if let musicURL = Bundle.main.url(forResource: "Adding", withExtension: "mp3") {
if let audioPlayer = try? AVAudioPlayer(contentsOf: musicURL) {
music = audioPlayer
music.numberOfLoops = -1
music.play()
}
}
}
#IBAction func playButtonPressed(_ sender: Any) {
if (toggleState == 1) {
music.pause()
toggleState = 2
}
else {
music.play()
toggleState = 1
}
}

How can I tap to close AVPlayer in Swift 5?

I'm creating an extremely simple game, and I've hidden the video controls.
#IBAction func playVideo1(_ sender: Any) {
// play video connected to button 1
guard let firstVideo = Bundle.main.path(forResource: "Video1", ofType:"mp4") else {
debugPrint("Video not found")
return
}
// create an AVPlayer, passing it mp4
let player = AVPlayer(url: URL(fileURLWithPath: firstVideo))
// Create a new AVPlayerViewController and pass it a reference to the player.
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
// Modally present the player and call the player's play() method when complete.
present(controller, animated: true) {
player.play()
}
} // end playVideo1
One of the two options would be OK.
Option 1: Tap to close the video.
Option 2: Have the AVPlayer close automatically at the end of the video.
I appreciate any help.
Thanks!
You can add a tap gesture recognizer to AVPlayerViewController's view (for closing on tap), or you could subscribe to AVPlayerItemDidPlayToEndTime notification (for closing when video ends playing). Something like this:
#IBAction func playVideo1(_ sender: Any) {
// play video connected to button 1
guard let firstVideo = Bundle.main.path(forResource: "Video1", ofType:"mp4") else {
debugPrint("Video not found")
return
}
// create an AVPlayer, passing it mp4
let player = AVPlayer(url: URL(fileURLWithPath: firstVideo))
// Create a new AVPlayerViewController and pass it a reference to the player.
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
//for closing when video ends
NotificationCenter.default.addObserver(self, selector: #selector(closePlayer), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: controller.player?.currentItem)
//for closing on tap
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(closePlayer))
controller.view.addGestureRecognizer(tapGestureRecognizer)
// Modally present the player and call the player's play() method when complete.
present(controller, animated: true) {
player.play()
}
} // end playVideo1
#objc func closePlayer() {
dismiss(animated: true)
//if you go notification route, don't forget to remove observer
NotificationCenter.default.removeObserver(self)
}
For closing the videoPlayer when on user tap you can use the following-
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(closePlayerOnTouch))
controller.view.addGestureRecognizer(tapGestureRecognizer)
and then add this after the button -
#objc func closePlayerOnTouch() {
dismiss(animated: true)
NotificationCenter.default.removeObserver(self)
}
#Predrag's answer is really awesome though.
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var btnPlay: UIButton!
var player:AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPress(sender: AnyObject) {
if (btnPlay.titleLabel?.text == "Play") {
initPlayer()
btnPlay.setTitle("Stop", forState: UIControlState.Normal)
} else {
stopPlayer()
btnPlay.setTitle("Play", forState: UIControlState.Normal)
}
}
func initPlayer() {
if let play = player {
print("playing")
play.play()
} else {
print("player allocated")
player = AVPlayer(URL: NSURL(string: "http://streaming.radio.rtl.fr/rtl-1-48-192")!)
print("playing")
player!.play()
}
}
func stopPlayer() {
if let play = player {
print("stopped")
play.pause()
player = nil
print("player deallocated")
} else {
print("player was already deallocated")
}
}
}

How to continuing playing music when user move through the app. [Not in background mode]

I'm making a musicPlayer app in Swift, which is based on Tab Bar Controller and now is creating the NowPlayingViewController which present the playing scene as below screenshot showed. This NowPlayingViewController will be opened when I click the table row from another tableViewController, and the song was played.
My question is that when I dismiss this View Controller to go back the previous view or move to other view of Tab Bar Controller, the music playing is stoped. After some googles, someone suggests to use singleton to make a shared session for music playing.
/// here is my project's storyboard
/// I change my code to add one Audio Manager.swift as the singleton class. However I'm not sure if I'm doing the right thing, need help. Also don't find method about getting playingSong variable from NowPlayingVewController...
import AVKit
class AudioManager {
// use Singleton pattern keep the music continuing when user move through the App.
static let sharedInstance = AudioManager()
var audioPlayer: AVAudioPlayer!
var playingSong: SongData?
private init() {
// config for audio background playing
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers])
print("Playback OK")
try AVAudioSession.sharedInstance().setActive(true)
print("Session is Active")
} catch {
print(error)
}
}
func startMusic() {
do {
// how to retrieve the playingSong variable from NowPlayingViewController?
// playingSong =
try audioPlayer = AVAudioPlayer(contentsOf: playingSong!.url)
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print("could not load file")
}
}
func pauseMusic() {
audioPlayer.pause()
}
}
/// NowPlayViewController.swift, in case it is not fully aligned with above picture because it is just an example image to show the playing scene.
import UIKit
import AVKit
class NowPlayingViewController: UIViewController {
var playingSong: SongData?
var audioPlayer: AVAudioPlayer!
var isPlaying = true
var timer:Timer!
#IBOutlet weak var songTitle: UILabel!
#IBOutlet weak var songAlbum: UILabel!
#IBOutlet weak var songArtwork: UIImageView!
#IBOutlet weak var playOrPauseButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
songTitle.text = playingSong?.songName
songAlbum.text = playingSong?.albumName
songArtwork.image = playingSong?.albumArtwork
// start play music in AudioManager sharedInstance
AudioManager.sharedInstance.startMusic()
}
#IBAction func NaviBack(_ sender: UIButton) {
print("press button")
// not sure about this, use present modally of segue,
// so need to use dismiss to return to the previous scene/view.
self.dismiss(animated: true, completion: nil)
}
#IBAction func PlayOrPauseMusic(_ sender: UIButton) {
if isPlaying {
print("isPlaying")
AudioManager.sharedInstance.pauseMusic()
isPlaying = false
playOrPauseButton.setTitle("Pause", for: .normal)
} else {
print("isPaused")
AudioManager.sharedInstance.startMusic()
isPlaying = true
playOrPauseButton.setTitle("Play", for: .normal)
}
}
}
/// Sending data to NowPlayingViewController from tableViewController using Segue present modally.
// define segue to the transition to NowPlaying View
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "playingSong" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let controller = segue.destination as! NowPlayingViewController
if resultSearchController.isActive {
controller.playingSong = filteredTableData[indexPath.row]
} else {
controller.playingSong = tableData[indexPath.row]
}
}
}
}
Oops, I found my AudioManger is actually worked by getting the playingSong: SongData from tableViewController when user click the cell row in this view. I declare this playingSong as static var in order to access/use it from AudioManager.
Now music is playing when user navigation through the Tab Bar Controller, update my code as below. Next step could be add a button on the left/right top of navi view to re-present the playing scene:)
Err, I found that it still has issue, the play and resume for the current song is working, but if I select another song in the tableViewController, then it still plays the previous song in NowPlayingView. The reason could be the init() only once, so I need to find a way to re-assign value to sharedInstance when select another cell row.
import AVKit
class AudioManager {
// use Singleton pattern keep the music continuing when user move through the App.
static let sharedInstance = AudioManager()
var audioPlayer: AVAudioPlayer!
var playingSong: SongData?
private init() {
// config for audio background playing
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers])
print("Playback OK")
try AVAudioSession.sharedInstance().setActive(true)
print("Session is Active")
} catch {
print(error)
}
do {
playingSong = SongsTableViewController.playingSong
try audioPlayer = AVAudioPlayer(contentsOf: playingSong!.url)
audioPlayer.prepareToPlay()
} catch {
print("could not load file")
}
}
func playMusic() {
audioPlayer.play()
}
func pauseMusic() {
audioPlayer.pause()
}
}

Toggling a system sound - swift

I want to make it so that a button can be clicked, which will start repeatedly playing a system sound over and over (without overlapping the sounds) until the button is clicked again - so essentially, the button will toggle the repeated playing of an audio services system sound on/off.
If anyone would know how to do this I would greatly appreciate it.
Thanks
To do this use an AVAudioplayer with infinite repeat, and then pause/play it when button is pressed.
First make a audioPlayer variable, outside of any function:
var audioPlayer : AVAudioPlayer!
Then initialise the audioPlayer inside of viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
guard let url = Bundle.main.path(forResource: "myOwnSound", ofType: "wav") else { //your own file name AND extension type of course
print("file not found")
return
}
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
try audioPlayer = AVAudioPlayer(contentsOf: URL(string: url)!)
audioPlayer!.prepareToPlay()
audioPlayer.numberOfLoops = -1
} catch let error as NSError {
print("error while initialising sound: \(error)")
}
}
Then inside a function linked to your button, in my case I named that function buttonPressed:
#IBAction func buttonPress(_ sender:AnyObject){
if audioPlayer.isPlaying {
audioPlayer.pause()
}
else {
audioPlayer.play()
}
}
You can use AVFoundation and AVAudioPlayer to manage music and
sound inside you application
Import following
import AVFoundation
Create a property of AVAudioPlayer to manage play/stop and add following code inside the scope of your music button
var player : AVAudioPlayer?
#IBAction func openListAction(_ sender: UIButton) {
// moveToGame()
if sender.isSelected {
sender.isSelected = false
player?.stop()
}
else {
sender.isSelected = true
let bundle = Bundle.init(for: self.classForCoder)
let path = bundle.path(forResource: "soundFileName", ofType : "soundFileExtension")! //.mp3, .caf etc
let url = URL(fileURLWithPath : path)
do {
player = try AVAudioPlayer(contentsOf: url)
player?.play()
} catch {
print ("Error to load music")
}
}
}

AVPlayer will not stop playing when going back to the previous view controller (SWIFT)

I have two View Controllers a TableViewController where i have a list of musics and a UIViewController where it displays the music details and plays the music. The music automatically plays when the view is loaded and pauses when the pause button is pressed. However whenever I go back to the previous TableViewController to select another music, the music continues to play. And if i select another music, both of them are playing together
override func viewDidLoad() {
super.viewDidLoad()
timeLabel.text = "00:00"
if let object = currentObject {
audioTitle.text = object["audioTitle"] as? String
let days = object["daysActive"] as! Int
daysActive.text = "Powertalks: Day \(days)"
var initialThumbnail = UIImage(named: "trc_app_icon.png")
audioImage.image = initialThumbnail
if let thumbnail = object["image"] as? PFFile {
audioImage.file = thumbnail
audioImage.loadInBackground()
}
if let audioFile = object["audioFile"] as? PFFile {
if let audioPath: String = audioFile.url {
audioPlayer = AVPlayer(URL: NSURL(string: audioPath))
audioSlider.minimumValue = 0
audioSlider.maximumValue = Float(CMTimeGetSeconds(audioPlayer.currentItem.asset.duration))
audioSlider.value = Float(CMTimeGetSeconds(audioPlayer.currentTime()))
audioPlayer.volume = volumeSlider.value
playAudio()
}
}
}
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateSlider"), userInfo: nil, repeats: true )
}
You have to pause the player when the view disappears. Although AVPlayer doesn't have a stop method, you can set the rate to 0.0 (or use pause()) and set currentItem to nil to achieve the same effect. Try using the below code (not tested)
override func viewWillDisappear(animated: Bool) {
audioPlayer.pause()
audioPlayer.currentItem = nil
}
I tested the code below which works fine:
override func viewWillDisappear(animated: Bool) {
audioPlayer.pause()
audioPlayer.currentItem = nil
}
You could also use:
audioPlayer.stop()