How do I change a play button to a pause button when tapped on? - ios

I need to change my play button to a pause button and vice versa when tapped on. I am still new to this, so I don't know how to identify buttons in the .swift file or how to change icons programmatically in the .swift file.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player = AVAudioPlayer()
var toggleState = 1
#IBAction func playPauseButton(sender: AnyObject) {
if toggleState == 1 {
player.play()
toggleState = 2
} else {
player.pause()
toggleState = 1
}
}
#IBAction func stopButton(sender: AnyObject) {
player.stop()
player.currentTime = 0
}
#IBAction func sliderChanged(sender: AnyObject) {
player.volume = sliderValue.value
}
#IBOutlet weak var sliderValue: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
var audioPath = NSBundle.mainBundle().pathForResource("StarWars", ofType: "mp3")!
var error: NSError?
player = AVAudioPlayer(contentsOfURL: NSURL(string: audioPath), error: &error)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

You can change the image of the button depending on the state:
#IBAction func playPauseButton(sender: AnyObject) {
var playBtn = sender as UIButton
if toggleState == 1 {
player.play()
toggleState = 2
playBtn.setImage(UIImage(named:"pause.png"),forState:UIControlState.Normal)
} else {
player.pause()
toggleState = 1
playBtn.setImage(UIImage(named:"play.png"),forState:UIControlState.Normal)
}
}
The sender object passed to the playPauseButton is your UIButton which calls the method. Because it is sent as an object of type AnyObject we cast it to a UIButton. If you do not want to cast it, and are sure only a UIButton will call this method you can simply replace AnyObject with UIButton in the function parameter.

Related

Trying to press one button then play audios in sequence or random

I am trying to press one button and the audio file plays one after another.
Here is my code below. I press play button and both sounds play at the same time.
I am trying to press play button and the sound starts and stop then the other sound file starts and stops. One button pressed for 2 sound files to press is my goal. Thank you
I am trying this in a loop but it is not working.
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var sliderValue: UISlider!
var player:AVAudioPlayer = AVAudioPlayer()
var player1:AVAudioPlayer = AVAudioPlayer()
#IBAction func play(_ sender: AnyObject) {
player.play()
player1.play()
}
#IBAction func pause(_ sender: AnyObject) {
player.pause()
}
#IBAction func stop(_ sender: AnyObject) {
player.stop()
player.currentTime = 0
}
#IBAction func sliderChanged(_ sender: AnyObject) {
player.volume = sliderValue.value
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let audioPath = Bundle.main.path(forResource: "sound1", ofType: "mp3")!
let audioPath1 = Bundle.main.path(forResource: "sound2", ofType: "mp3")!
do {
try player = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPath))
try player1 = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPath1))
} catch{
//Process Error here
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Of course you can't do it in a loop!
The correct way to play a sound file after another is to use the AVAudioPlayerDelegate.
In your view controller, conform to AVAudioPlayerDelegate:
class YourViewController: UIViewController, AVAudioPlayerDelegate {
// ...
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer,
successfully flag: Bool) {
}
}
As you can see, I added an audioPlayerDidFinishPlaying method to the controller.
So you want to play player first, then player1, right?
In viewDidLoad, add this line:
player.delegate = self
And in the method I just added, write:
player1.play()
And the other IBAction methods should be changed a little:
#IBAction func play(_ sender: AnyObject) {
player.play()
}
#IBAction func pause(_ sender: AnyObject) {
if player.isPlaying {
player.pause()
} else if player1.isPlaying {
player1.pause()
}
}
#IBAction func stop(_ sender: AnyObject) {
if player.isPlaying {
player.stop()
} else if player1.isPlaying {
player1.stop()
}
}
#IBAction func sliderChanged(_ sender: AnyObject) {
player.volume = sliderValue.value
player1.volume = sliderValue.value
}
If you can manage this also with another object AVQueuePlayer, you can find the documentation here and here a guide.

Swift - Stop avaudioplayer

I am trying to build a soundboard into an app and have figured out an efficient way of using tags to control playing the sounds. However I am now trying to integrate a pause button that can be used with the .stop() method on the AVAudioPlayer however I get an error with my current code:
EXC_BAD_ACCESS
This is what I am using at the moment, any ideas?
import UIKit
import AVFoundation
let soundFilenames = ["sound","sound2","sound3"]
var audioPlayers = [AVAudioPlayer]()
class SecondViewController: UIViewController {
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
for sound in soundFilenames {
do {
let url = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(sound, ofType: "mp3")!)
let audioPlayer = try AVAudioPlayer(contentsOfURL: url)
audioPlayers.append(audioPlayer)
} catch {
//Catch error thrown
audioPlayers.append(AVAudioPlayer())
}
}
}
#IBAction func buttonPressed(sender: UIButton) {
let audioPlayer = audioPlayers[sender.tag]
audioPlayer.play()
}
#IBAction func stop(sender: UIButton) {
audioPlayer.stop()
}
}
Your audioPlayer in stop function is not the playing player. You should assign it in buttonPressed function.
#IBAction func buttonPressed(sender: UIButton) {
audioPlayer = audioPlayers[sender.tag]
audioPlayer.play()
}
By the way, You can mark audioPlayer as a "?" property, it will be more efficient when init this Controller.
class SecondViewController: UIViewController {
var audioPlayer: AVAudioPlayer?
let enableMuiltPlayers = false
....
#IBAction func buttonPressed(sender: UIButton) {
if sender.tag < audioPlayers.count else {
print("out of range")
return
}
if enableMuiltPlayers {
audioPlayers[sender.tag].play()
} else {
audioPlayer?.stop()
//set the current playing player
audioPlayer = audioPlayers[sender.tag]
audioPlayer?.play()
}
}
#IBAction func stop(sender: UIButton) {
let wantToStopAll = false
if enableMuiltPlayers && wantToStopAll {
stopAll()
} else {
audioPlayer?.stop()
}
audioPlayer = nil
}
}
to stop all:
fun stopAll() {
for player in audioPlayers {
player.stop()
}
}
Your code may have other faults, but there's one thing sure:
You should not instantiate AVAudioPlayer using default initializer AVAudioPlayer().
Change this line:
var audioPlayer = AVAudioPlayer()
to:
var playingAudioPlayer: AVAudioPlayer?
And change this part:
} catch {
//Catch error thrown
audioPlayers.append(AVAudioPlayer())
}
to something like this:
} catch {
//Catch error thrown
fatalError("Sound resource: \(sound) could not be found")
}
(The latter part is very important to solve the issue. But I found it had become just a duplicate of some part of Hao's answer after I edited it...)
And start method:
#IBAction func start(sender: UIButton) {
let audioPlayer = audioPlayers[sender.tag]
audioPlayer.start()
playingAudioPlayer = audioPlayer
}
And stop should be:
#IBAction func start(sender: UIButton) {
playingAudioPlayer?.stop()
}
if audioPlayer != nil {
if audioPlayer.playing {
audioPlayer.stop()
}
}

Impossible to stop AVPlayer

I am currently testing the use of AVPlayer with audio streaming url, using Swift. There are play() and pause() methods, but the problem is that, pausing only, the stream remains cached in the device.
Here is my test code :
import UIKit
import AVFoundation
class ViewController: UIViewController {
let player = AVPlayer(URL: NSURL(string: "http://streaming.radio.rtl.fr/rtl-1-48-192")!)
#IBOutlet weak var btnPlay: UIButton!
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() {
player.play()
}
func stopPlayer() {
// player.currentItem = nil // Last thing I tried, but generate an error
player.pause()
}
}
Here are the issues when trying somethings :
player = nil : "Cannot assign a value of type 'NilLiteralCOnvertible' to a value of type 'AVPlayer'"
player.currentItem = nil : "Cannot assign to property: 'currentItem' is a get-only property"
I tried everything, even through AVQueuePlayer without any effective result. (obviously, since I only have one item in my case).
How to stop AVPlayer or destroy his instance ?
From this post I found the best solution to completely stop AVPlayer before you leave or start a new player:
videoPlayer.replaceCurrentItemWithPlayerItem(nil)
[Update] For SWIFT 3:
player.replaceCurrentItem(with: nil)
If you declare player as an optional variable, you can then set the player to nil to deallocate it.
Silly example but it shows what happens:
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")
}
}
}
SWIFT 3 Version:
player.replaceCurrentItem(with: nil)

recording audio in swift and passing the recorded audio to the next view controller

I am trying to record Audio, and pass the recorded audio, to the next view controller. Here is my code for recording Audio
class RecordSoundsViewController: UIViewController, AVAudioRecorderDelegate {
#IBOutlet weak var recording: UILabel!
#IBOutlet weak var recordButton: UIButton!
#IBOutlet weak var stopButton: UIButton!
var audioRecorder:AVAudioRecorder!
var recordedAudio : RecordedAudio!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
// enables record button
// hides the stop button
recordButton.enabled = true
stopButton.hidden = true
}
#IBAction func recordAudio(sender: UIButton) {
//Shows recording label
recording.hidden = false
//diabling record button
recordButton.enabled = false
stopButton.hidden = false
//Filepath Creation
let dirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String
let currentDateTime = NSDate()
let formatter = NSDateFormatter()
formatter.dateFormat = "ddMMyyyy-HHmmss"
let recordingName = formatter.stringFromDate(currentDateTime)+".wav"
let pathArray = [dirPath, recordingName]
let filePath = NSURL.fileURLWithPathComponents(pathArray)
println(filePath)
// Recording Session
var session = AVAudioSession.sharedInstance()
session.setCategory(AVAudioSessionCategoryPlayAndRecord, error: nil)
audioRecorder = AVAudioRecorder(URL: filePath, settings: nil, error: nil)
audioRecorder.delegate = self
audioRecorder.meteringEnabled = true
audioRecorder.prepareToRecord()
audioRecorder.record()
}
func audioRecorderDidFinishRecording(recorder: AVAudioRecorder!, successfully flag: Bool) {
// ToDo create recorded audio file
if(flag)
{ recordedAudio = RecordedAudio()
recordedAudio.filepathURL = recorder.url
recordedAudio.title = recorder.url.lastPathComponent
// ToDo Perform segue
self.performSegueWithIdentifier("stopRecording", sender: recordedAudio)
}
else {
println("Recording was unsuccessfull")
stopButton.hidden = true
recordButton.enabled = true
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue == "stopRecording") {
let PlaySoundsVC:PlaySoundsViewController = segue.destinationViewController as! PlaySoundsViewController
let data = sender as! RecordedAudio
PlaySoundsVC.receivedAudio = data
}
}
#IBAction func stopAudio(sender: UIButton) {
// Hides recording
recording.hidden = true
audioRecorder.stop()
var audioSession = AVAudioSession.sharedInstance()
audioSession.setActive(false, error: nil)
}
}
My Model class is ,
import Foundation
class RecordedAudio : NSObject{
var filepathURL :NSURL!
var title : String!
}
Here is how My second viewcontroller catch the data and uses it,
class PlaySoundsViewController: UIViewController {
var audioPlayer: AVAudioPlayer!
var receivedAudio: RecordedAudio!
func rateplay (rtt : Float32) {
audioPlayer.stop()
audioPlayer.rate = rtt
audioPlayer.currentTime = 0.0
audioPlayer.play()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// if var filePath = NSBundle.mainBundle().pathForResource("movie_quote", ofType: "mp3")
// {
// // if path is there for mp3
// let filepathurl = NSURL.fileURLWithPath(filePath)
//
// // println(receivedAudio.title)
//
// }
// else {
// println("Path is empty")
//
// }
audioPlayer = AVAudioPlayer(contentsOfURL: receivedAudio.filepathURL, error: nil)
audioPlayer.enableRate = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func playSlow(sender: UIButton) {
// play sloooowllyyyyy
audioPlayer.stop()
audioPlayer.rate = 0.5
audioPlayer.currentTime = 0.0
audioPlayer.play()
}
#IBAction func playFast(sender: UIButton) {
rateplay(1.5)
}
#IBAction func stopAudio(sender: UIButton) {
audioPlayer.stop()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
Untill I add the below code,
audioPlayer = AVAudioPlayer(contentsOfURL: receivedAudio.filepathURL, error: nil)
audioPlayer.enableRate = true
I was able to move to the second scene, which means, the audio is successfully recorded. But as soon as i access the data like "receivedAudio.filepathURL" I am getting the error,
fatal error: unexpectedly found nil while unwrapping an Optional value
In the prepareForSegue function of the RecordSoundsViewController you need to write segue.identifier == "stopRecording" as the condition.
Currently you have segue == "stopRecording".
Happy Coding!

Swift If Statement Not Working with UILabel

My if statement is not displaying what I want it to. Can anyone help? I want to display some text in a UILabel that will be decided from weather the random number is below or above 50.
Here is the code. The IF Statement is at the end.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var pianoSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("two_tone_nav", ofType: "mp3")!)
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
audioPlayer = AVAudioPlayer(contentsOfURL: pianoSound, error: nil)
audioPlayer.prepareToPlay()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var Number: UILabel!
#IBOutlet weak var BA50: UILabel!
#IBAction func Generate(sender: AnyObject) {
audioPlayer.play()
var Generate = Int(arc4random_uniform(100))
Number.text = String(Generate)
func AboveBelow50(){
if Generate>=50 {
self.BA50.text = "No"
}
else {
self.BA50.text = "Yes"
}
}
}
}
Put your function:
func AboveBelow50(Generate: Int){
if Generate>=50 {
self.BA50.text = "No"
} else {
self.BA50.text = "Yes"
}
Out side your IBAction, and change it this way.
And call inside your IBAction:
var Generate = Int(arc4random_uniform(100))
AboveBelow50(Generate)

Resources