Swift: Sound plays on simulator but not on device - ios

I'm building an app for iPhone/iPad, and I'm trying to solve a sound problem:
The app comes with a four minute countdown timer. When the coundown reaches 00:00, it triggers a short sound effect. This works fine on the different simulators, but my phone remains silent. The app is built for 7.1. I'm wondering if it's because of the code or if my phone isn't working (it does have sound issues). Does my code look okay? I hope someone can help with this.
Here it is:
import Foundation
import UIKit
import AVFoundation
class VC11 : UIViewController {
#IBOutlet weak var timerLabel: UILabel!
var timer = NSTimer()
var count = 240
var timerRunning = false
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
func nextPage(sender:UISwipeGestureRecognizer) {
switch sender.direction {
case UISwipeGestureRecognizerDirection.Left:
print("SWIPED LEFT")
self.performSegueWithIdentifier("seg11", sender: nil)
default:
break
}
var leftSwipe = UISwipeGestureRecognizer (target: self, action: Selector("nextPage"))
var rightSwipe = UISwipeGestureRecognizer (target: self, action: Selector("nextPage"))
leftSwipe.direction = .Left
rightSwipe.direction = .Right
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
}
}
func updateTime() {
count--
let seconds = count % 60
let minutes = (count / 60) % 60
let hours = count / 3600
let strHours = hours > 9 ? String(hours) : "0" + String(hours)
let strMinutes = minutes > 9 ? String(minutes) : "0" + String(minutes)
let strSeconds = seconds > 9 ? String(seconds) : "0" + String(seconds)
if hours > 0 {
timerLabel.text = "\(strHours):\(strMinutes):\(strSeconds)"
}
else {
timerLabel.text = "\(strMinutes):\(strSeconds)"
}
stopTimer()
}
#IBAction func startTimer(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
sender.setTitle("Running...", forState: .Normal)
}
func stopTimer()
{
if count == 0 {
timer.invalidate()
timerRunning = false
timerLabel.text = "04:00"
playSound()
count = 240
}
}
func playSound() {
var soundPath = NSBundle.mainBundle().pathForResource("Metal_Gong", ofType: "wav")
var soundURL = NSURL.fileURLWithPath(soundPath!)
self.audioPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
self.audioPlayer.play()
}
}

Make sure that your device is not triggered into silent mode.
Also, for short system sounds you can use System Sounds from AudioToolbox (https://developer.apple.com/library/ios/documentation/AudioToolbox/Reference/SystemSoundServicesReference/):
if let soundURL = NSBundle.mainBundle().URLForResource("Metal_Gong", withExtension: "wav") {
var mySound: SystemSoundID = 0
AudioServicesCreateSystemSoundID(soundURL, &mySound)
AudioServicesPlaySystemSound(mySound);
}

I just found out that my code does work - it's only because of my broken phone that no sound is played. Thank you for your help!

Related

How can I prevent the stopwatch from resetting after pause

When I hit Pause it pauses the timer. but then when I hit start, it resets to 0 instead of continue where it left off. How can I fix this?
I've tried adding a new button for reset. that works, but now I can't get the start button to keep counting after a pause. I've been struggling with getting the resume to work.
import UIKit
class TimerViewController: UIViewController {
#IBOutlet weak var lable: UILabel!
#objc var startTime = TimeInterval()
var timer = Timer()
override func viewDidLoad(){
super.viewDidLoad()
}
// Start Button
#IBAction func start(_ sender: UIButton) {
if (!timer.isValid) {
let aSelector = #selector(updateTime)
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: aSelector, userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate
}
}
// Pause Button
#IBAction func pause(_ sender: UIButton)
{
timer.invalidate()
}
// Reset Button
#IBAction func reset(_ sender: UIButton)
{
timer.invalidate()
lable.text = "00:00:00"
}
#objc func updateTime() {
let currentTime = NSDate.timeIntervalSinceReferenceDate
//Find the difference between current time and start time.
var elapsedTime: TimeInterval = currentTime - startTime
//calculate the minutes in elapsed time.
let minutes = UInt8(elapsedTime / 60.0)
elapsedTime -= (TimeInterval(minutes) * 60)
//calculate the seconds in elapsed time.
let seconds = UInt8(elapsedTime)
elapsedTime -= TimeInterval(seconds)
//find out the fraction of milliseconds to be displayed.
let fraction = UInt8(elapsedTime * 100)
//add the leading zero for minutes, seconds and millseconds and store them as string constants
let strMinutes = String(format: "%02d", minutes)
let strSeconds = String(format: "%02d", seconds)
let strFraction = String(format: "%02d", fraction)
//concatenate minuets, seconds and milliseconds as assign it to the UILabel
lable.text = "\(strMinutes):\(strSeconds):\(strFraction)"
}
}
keep the startTime variable from being modified until it is reset. maybe for example like this
var isReset: Bool = true
#IBAction func start(_ sender: UIButton) {
if (!timer.isValid) {
let aSelector = #selector(updateTime)
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: aSelector, userInfo: nil, repeats: true)
if isReset {
startTime = NSDate.timeIntervalSinceReferenceDate
isReset = false
}
}
}
#IBAction func reset(_ sender: UIButton) {
timer.invalidate()
lable.text = "00:00:00"
isReset = true
}
Cheers...

Add tone to Countdown Timer in XCODE 8 Swift 3

I am new to coding. I am trying to learn iOS development and I have created a simple countdown timer, that uses a slider to select the amount of time in seconds. I would like to add a tone to the final 9 secs of the countdown. I know how to add audio files, but I have "NO IDEA" how to add a countdown tone to the last 9 seconds of the timer.
#IBAction func startBtnTapped(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.counter), userInfo: nil, repeats: true)
SoundPlayer4.play()
//Hide Start and Slider Buttons
startBtn.isHidden = true
sliderCtrl.isHidden = true
stopBtn.isHidden = false
}
#IBAction func stopBtnTapped(_ sender: Any) {
timer.invalidate()
seconds = 180
sliderCtrl.setValue(180, animated: true)
timerLbl.text = "180"
SoundPlayer3.play()
//Show Start and Slider Buttons
sliderCtrl.isHidden = false
startBtn.isHidden = false
stopBtn.isHidden = true
}
#IBAction func sliderCrtlUsed(_ sender: UISlider) {
seconds = Int(sender.value)
timerLbl.text = String(seconds)
}
func counter(){
seconds -= 1
timerLbl.text = String(seconds)
if (seconds == 0){
timer.invalidate()
//Show Start and Slider Buttons
startBtn.isHidden = false
sliderCtrl.isHidden = false
stopBtn.isHidden = true
//Play Audio
SoundPlayer1.play()
}
}
So can anyone help me add a countdown tone to my project?
Thank you
Put this code in counter function:
if seconds < 10 {
var AudioURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Dog bark", ofType: "mp3")!)
var AudioPlayer = AVAudioPlayer()
do {
AudioPlayer = try AVAudioPlayer(contentsOfURL: AudioURL)
} catch _ as NSError {
fatalError()
}
AudioPlayer.play()
}
Then put the file here:
And that's it!
You can read more on this question here.

Pausing and Resuming a Timer in iOS

This is a bit of a common question but most answers don't seem to work. I have a timer in my app that and I can start and re-start the timer easily. I am trying to pause and resume the timer but for now resuming only continues the timer from a timer that's greater than the one I resumed it at. Which probably means it continues counting in the background. This is my code :
//Timer Variables
var startTime = NSTimeInterval()
var timer = NSTimer()
var isTiming = Bool()
var isPaused = Bool()
func updatedTimer() {
let currentTime = NSDate.timeIntervalSinceReferenceDate()
var elapsedTime: NSTimeInterval = currentTime - startTime
let minutes = UInt8(elapsedTime / 60.0)
elapsedTime -= (NSTimeInterval(minutes) * 60)
let seconds = UInt8(elapsedTime)
elapsedTime -= NSTimeInterval(seconds)
let strMinutes = String(format: "%02d", minutes)
let strSeconds = String(format: "%02d", seconds)
workoutTime.text = "\(strMinutes) : \(strSeconds)"
}
#IBAction func startButtonTapped(sender: AnyObject) {
if !timer.valid {
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: #selector(TimedWorkoutViewController.updatedTimer), userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate()
}
isTiming = true
isPaused = false
}
#IBAction func pauseAndContinueButtonTapped(sender: AnyObject) {
if isTiming == true && isPaused == false {
timer.invalidate() //Stop the Timer
isPaused = true //isPaused
isTiming = false //Stopped Timing
pauseButton.setTitle("RESUME", forState: UIControlState.Normal) //Set Button to Continue state
print(startTime)
} else if isTiming == false && isPaused == true {
if !timer.valid {
timer.invalidate()
//timer = nil
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: #selector(TimedWorkoutViewController.updatedTimer), userInfo: nil, repeats: true)
}
isPaused = false
isTiming = true
pauseButton.setTitle("PAUSE", forState: UIControlState.Normal) //Set Button to Continue state
}
}
I have a custom timer application and dealt with the same issue. There are many ways to address this. You may want to track pausedTime like you do elapsedTime and subtract that from your other variables. This gives you some flexibility as well to show totalTime vs. elapsedTime, etc... My function is quite a bit different, so I retrofitted it to your setup.
Basically, pausing is different because you can pause/resume multiple times. So you need to track previous pauses, and current pause state and subtract from elapsed time (or total time, or whatever you want).
I tested this code and it worked. Give it a try and let me know:
import UIKit
class TimedWorkoutViewController: UIViewController {
#IBOutlet weak var pauseButton: UIButton!
#IBOutlet weak var startButton: UIButton!
var startTime = NSTimeInterval()
var timer = NSTimer()
var isTiming = Bool()
var isPaused = Bool()
var pausedTime: NSDate? //track the time current pause started
var pausedIntervals = [NSTimeInterval]() //track previous pauses
override func viewDidLoad() {
super.viewDidLoad()
}
func updatedTimer() {
let currentTime = NSDate.timeIntervalSinceReferenceDate()
var pausedSeconds = pausedIntervals.reduce(0) { $0 + $1 } //calculate total time timer was previously paused
if let pausedTime = pausedTime {
pausedSeconds += NSDate().timeIntervalSinceDate(pausedTime) //add current pause if paused
}
var elapsedTime: NSTimeInterval = currentTime - startTime - pausedSeconds //subtract time paused
let minutes = Int(elapsedTime / 60.0)
elapsedTime -= (NSTimeInterval(minutes) * 60)
let seconds = Int(elapsedTime)
elapsedTime -= NSTimeInterval(seconds)
let strMinutes = String(format: "%02d", minutes)
let strSeconds = String(format: "%02d", seconds)
workoutTime.text = "\(strMinutes) : \(strSeconds)"
}
#IBAction func startButtonTapped(sender: AnyObject) {
if !timer.valid {
timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(TimedWorkoutViewController.updatedTimer), userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate()
}
isTiming = true
isPaused = false
pausedIntervals = [] //reset the pausedTimeCollector on new workout
}
#IBAction func pauseAndContinueButtonTapped(sender: AnyObject) {
if isTiming == true && isPaused == false {
timer.invalidate() //Stop the Timer
isPaused = true //isPaused
isTiming = false //Stopped Timing
pausedTime = NSDate() //asuuming you are starting a brand new workout timer
pauseButton.setTitle("RESUME", forState: UIControlState.Normal) //Set Button to Continue state
} else if isTiming == false && isPaused == true {
let pausedSeconds = NSDate().timeIntervalSinceDate(pausedTime!) //get time paused
pausedIntervals.append(pausedSeconds) // add to paused time collector
pausedTime = nil //clear current paused state
if !timer.valid {
timer.invalidate()
//timer = nil
timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(TimedWorkoutViewController.updatedTimer), userInfo: nil, repeats: true)
}
isPaused = false
isTiming = true
pauseButton.setTitle("PAUSE", forState: UIControlState.Normal) //Set Button to Continue state
}
}
}
Here is some code I once used to track time
class TimeTracker : NSObject {
private var startTime:NSTimeInterval?
private var timer:NSTimer?
private var elapsedTime = 0.0
private var pausedTimeDifference = 0.0
private var timeUserPaused = 0.0
var delegate:TimeTrackerDelegate?
func setTimer(timer:NSTimer){
self.timer = timer
}
func isPaused() -> Bool {
return !timer!.valid
}
func start(){
if startTime == nil {
startTime = NSDate.timeIntervalSinceReferenceDate()
newTimer()
}
}
func pause(){
timer!.invalidate()
timeUserPaused = NSDate.timeIntervalSinceReferenceDate()
}
func resume(){
pausedTimeDifference += NSDate.timeIntervalSinceReferenceDate() - timeUserPaused;
newTimer()
}
func handleTimer(){
let currentTime = NSDate.timeIntervalSinceReferenceDate()
elapsedTime = currentTime - pausedTimeDifference - startTime!
delegate!.handleTime(elapsedTime)
}
func reset(){
pausedTimeDifference = 0.0
timeUserPaused = 0.0
startTime = NSDate.timeIntervalSinceReferenceDate()
newTimer()
}
private func newTimer(){
timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target:self , selector: "handleTimer", userInfo: nil, repeats: true)
}
}

Reset or change button title at the end of countdown

I'm building an app for iPhone/iPad. My app has a countdown timer. The countdown start button is labled "Start", and it switches to "Running" when the button is pressed. When the countdown has reached 00:00, I want the button title to reset itself to "Start" or change it to "Restart", so that the user can start all over again.
I'm new to Swift, so I hope someone can help with this. Here's my code:
import Foundation
import UIKit
import AVFoundation
class VC11 : UIViewController {
#IBOutlet weak var timerLabel: UILabel!
var timer = NSTimer()
var count = 240
var timerRunning = false
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
func nextPage(sender:UISwipeGestureRecognizer) {
switch sender.direction {
case UISwipeGestureRecognizerDirection.Left:
print("SWIPED LEFT")
self.performSegueWithIdentifier("seg11", sender: nil)
default:
break
}
var leftSwipe = UISwipeGestureRecognizer (target: self, action: Selector("nextPage"))
var rightSwipe = UISwipeGestureRecognizer (target: self, action: Selector("nextPage"))
leftSwipe.direction = .Left
rightSwipe.direction = .Right
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
}
}
func updateTime() {
count--
let seconds = count % 60
let minutes = (count / 60) % 60
let hours = count / 3600
let strHours = hours > 9 ? String(hours) : "0" + String(hours)
let strMinutes = minutes > 9 ? String(minutes) : "0" + String(minutes)
let strSeconds = seconds > 9 ? String(seconds) : "0" + String(seconds)
if hours > 0 {
timerLabel.text = "\(strHours):\(strMinutes):\(strSeconds)"
}
else {
timerLabel.text = "\(strMinutes):\(strSeconds)"
}
stopTimer()
}
#IBAction func startTimer(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
sender.setTitle("Running...", forState: .Normal)
}
func stopTimer()
{
if count == 0 {
timer.invalidate()
timerRunning = false
timerLabel.text = "04:00"
playSound()
count = 240
}
}
func playSound() {
var soundPath = NSBundle.mainBundle().pathForResource("Metal_Gong", ofType: "wav")
var soundURL = NSURL.fileURLWithPath(soundPath!)
self.audioPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
self.audioPlayer.play()
}
}
Its simple, if you have button in storyBoard then just take outlet of the button from storyBoard, as you have taken for timerlabel, like this
#IBOutlet weak var yourButton: UIButton!
And then in your stopTimer() func just change the title of your button like this,
func stopTimer()
{
if count == 0 {
timer.invalidate()
timerRunning = false
timerLabel.text = "04:00"
playSound()
count = 240
yourButton.setTitle("Restart", forState: .Normal)// add this line in your code
}
}

Stopwatch app counter going too fast

I am just learning iOS and programming in general and I am making a very basic iOS stopwatch app. I got the stopwatch working however, when I press start more than once the timer begins to go faster so that it is no longer a second long (gif here). Also, my formatting seems to be off for the seconds part, if you have any suggestions there it would be appreciated. Here is my code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var minuteLabel: UILabel!
#IBOutlet weak var secondLabel: UILabel!
var timer = NSTimer()
var second = 0
var minute = 0
func updateTime() {
do{
if second != 59
{
second++
secondLabel.text = ".\(second)"
}
else
{
second = 0
minute++
secondLabel.text = "." + String(format:"$%.2f", second)
if minute < 10
{
minuteLabel.text = "0\(minute)"
}
else
{
minuteLabel.text = String(format:"$%.2f", minute)
}
}
}
}
#IBAction func resetButton(sender: AnyObject) {
timer.invalidate()
second = 0
minute = 0
secondLabel.text = ".00"
minuteLabel.text = "00"
}
#IBAction func stopButton(sender: AnyObject) {
timer.invalidate()
}
#IBAction func startButton(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
}
Thank you for your help!
You are not invalidating timer when startButton is called, so tapping "Start" multiple times is creating duplicate timers which call the same function, updateTime. Change startButton to look like this:
#IBAction func startButton(sender: AnyObject) {
if !timer.valid
{
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
}
}
For your second question about the formatting, you need to put a condition to check for the seconds being less than 10, similar to what you did with the minutes. You would put a 0 in front of the seconds. In updateTime:
if second < 10
{
second++
secondLabel.text = ".0\(second)"
}
else if second <= 59
{
second++
secondLabel.text = ".\(second)"
}
else
{
...
}
See NSTimer documentation for more information.
You can get the desired formatting by using "%02d" instead of %.2f. Using %.2f will ensure 2 digits after the decimal point which is not true in your case (you have both second and minute as integers). Also you can use get away with using one label (e.g., timerLabel) as shown below:
var second = 0
var minute = 0
func updateTime() {
let dFormat = "%02d"
second++
if second == 59{
minute++
second = 0
}
let s = "\String(format: dFormat, minute):\String(format: dFormat, second))"
timerLabel.text = s
}
Hope this helps!

Resources