So I've got an NSTimer running a function every 0.1 seconds.
No matter how many conditions & caveats I put in place, this sound (and others on the timer) play twice. The same sound triggered by a touch plays only once.
myTimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("flip:"), userInfo: nil, repeats: true)
Here's the function it triggers.
func flip(timer: NSTimer) {
if current_timer == -3.0 && rang_3 == false {
if rang_3 == false {
rang_3 = true
dialed_3()
NSLog("DIALED 3")
}
}
}
And here's the sound function I need triggered only ONCE.
func dialed_3() {
if sound_mute == false {
do {
dial_3 = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("3", ofType: "mp3")!))
dial_3.volume = 2
dial_3.numberOfLoops = 0
dial_3.prepareToPlay()
dial_3.play()
} catch {
print("Error")
}
}
}
I don't know about your other code, but with what you show, it runs 1 time.
A small suggestion for you: move initialization to global without run it everytime timer run.
Alright! So I moved the !rang_3 check to the fixed !sound_mute check & it worked. As a failsafe I replicated the sounds that are used during countdown & muted them after 1 play. So if they play twice, the user won't notice.
if !sound_mute && !rang_3 {
do {
//soundstuff
}
}
Related
I'm trying to set up a group of 16 nodes to shoot particles in sequences to form patterns.
I'm able to activate them all at once, with different particle systems on different nodes, but I can't get the nodes to activate in a sequential fashion.
The particle system itself is already looping active and inactive.
Maybe this is the wrong approach and I should use Actions, but I don't know.
There's a while (self.fWorksequence == 2) calling the Sequence2(), that does activate the particles on the nodes that I need, but it's all at the same time. I need to have them lag: Node f1 fires, wait 1 second, nodes f2 and f16 fire simultaneously, wait another second, nodes f3 and f15 fire simultaneously.
var fWCycle = 1
func Sequence2() {
if self.fWCycle == 1 {
sceneView.scene.rootNode.enumerateChildNodes { (node, _) in
if node.name == "f1" {
let fWorkBoom = self.launchFWork1(color: UIColor.red)
node.addParticleSystem(fWorkBoom)
}
}
} else if self.fWCycle == 31 {
sceneView.scene.rootNode.enumerateChildNodes { (node, _) in
if node.name == "f2" {
let fWorkBoom = self.launchFWork1(color: UIColor.red)
node.addParticleSystem(fWorkBoom)
}
if node.name == "f16" {
let fWorkBoom = self.launchFWork1(color: UIColor.red)
node.addParticleSystem(fWorkBoom)
}
}
}
...
//check the cycle stage
if self.fWCycle <= 100 {
self.fWCycle += 1
} else if self.fWCycle > 600 {
self.fWorkSequence = 1
}
}
*Edit
tried with
func Sequence2() {
timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
}
and then
var timer = Timer()
var nodeCounter = 0
#objc func timerAction() {
self.nodeCounter += 1
if self.nodeCounter == 1 {
self.sceneView.scene.rootNode.enumerateChildNodes { (node, _) in
if node.name == "f1" {
let fWorkBoom = self.launchFWork1(color: UIColor.red)
node.addParticleSystem(fWorkBoom)
}
}
}...
}
And now the memory crashes the app...
I couldn't figure out how to do it with code, tried Timers and Loops and either got nothing to work, or constant crashes due to memory issues.
So I ended up solving by animating the whole thing in Blender and then importing that file with all the correct times to xcode. Worked perfectly.
If anyone finds this in the future, I would still like to know how to do it by code.
Thank you!
I'm working on a music player in Swift. The user can play a track, pause the track, or move a slider to choose a place to start. These three functions work.
However, if a user plays a track, listens for 1:00, and hits pause: when they click play again, it will restart the track from 0:00 instead of respecting where they paused.
I am using AVAudioPlayer as a media player -- according to the documentation, .play() allows you to specify a start-point using atTime.
So I tried to use atTime and have it start where the slider is -- if a listener listens for 1:00, I want atTime to dynamically pick that up. I try to establish this time value in a var.
class MusicViewController: UIViewController {
// removed imports and viewdidload to condense
#IBAction func playDownload(_ sender: Any) {
// removed file download code to condense
do {
audioPlayer = try AVAudioPlayer(contentsOf: destinationUrl)
guard let player = audioPlayer else { return }
//here's where I try to capture a time value from the slider's progress
let checkTime = TimeInterval(Slider.value)
player.prepareToPlay()
player.play(atTime: checkTime)
} catch let error {
print(error.localizedDescription)
}
// updates slider with progress
Slider.value = 0.0
Slider.maximumValue = Float((audioPlayer?.duration)!)
audioPlayer.play()
timer = Timer.scheduledTimer(timeInterval: 0.0001, target: self, selector: #selector(self.updateSlider), userInfo: nil, repeats: true)
}
#IBAction func pause(_ sender: Any) {
if audioPlayer.isPlaying {
audioPlayer.pause()
} else {
audioPlayer.play()
}
}
#IBOutlet var Slider: UISlider!
#objc func updateSlider() {
Slider.value = Float(audioPlayer.currentTime)
print("Changing works")
}
}
So I am trying to capture progress and have .play respect that -- this code will run, but it does not work. No errors but after hitting play, it will go to the end of the track immediately now. No good.
I am new to Swift - any idea where I went wrong?
Something really odd is happening with my code.
I made a rather simple Timer function that is triggered by button.
The button calls a first method, then the method use another function to do the counting, then it triggers something when the time's up.
And everything works fine.
here's the code.
// This is the declaration of the launch button.
#IBAction func playLater(_ sender: Any) {
if isTimerRunning == false {
runTimer()
}
}
var seconds = 10
var timer = Timer()
var isTimerRunning = false
var resumeTapped = false
//the timer function that sets up the duration and launches the counting.
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(self.updateTimer)), userInfo: nil, repeats: true)
isTimerRunning = true
}
//this part is making sure that you go from 10 to 0 and when it's at 0, something happens. In this very case, it plays a song.
#objc func updateTimer() {
if seconds < 1 {
timer.invalidate()
isTimerRunning = false
playNow((Any).self)
} else {
seconds -= 1
timerLabel.text = timeString(time: TimeInterval(seconds))
timerLabel.text = String(seconds)
}
}
The thing is that I also want to be able to triger that same function from the Apple Watch.
I made a WCSession that is working well, the messages are passing, it's ok.
So I'm using this code to launch the same function when the user pushes a button on the apple Watch. That part of the code is on the same iOS swift file.
#available(iOS 9.0, *)
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
// do something
isTimerRunning = false
playLater(Any.self)
}
As you can see, I'm not even trying to add some code, I just call the same function used with the iOS button.
But this time, it's not working. The first part of the method is responding, I saw that runTimer() is working, but it's not going to updateTimer().
Maybe I'm missing something here, but, what is the difference ? Why if it comes from the push of a button it's working, and if it's called directly from "the inside" nothing happens ?
If you have any educated guesses, or even, clues, I'd be grateful.
Thanks !
I'm trying to add a countdown timer to an existing app. Naturally, I don't want this timer to stall the rest of the application so I wanted to use an asynchronous thread dedicated to the timer. This is my current code and it doesn't even get to the update function (I used print statements to test this), but does print "Got". Also, I'm trying to update a label with the correct time and you can't do that within the thread. The time variable is a class variable. Not sure if this is even the correct approach, any suggestions?
Edit: Running the timer on main queue doesn't work as it interferes with a pan gesture I already have on the app. Also, any proposed solutions to the timing inaccuracies of the Timer Class would also be great.
func startTimer() {
time = 30
let queue = DispatchQueue(label: "timer")
queue.async {
print("Got")
_ = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
}
}
func update() {
DispatchQueue.main.sync {
if(time >= 0) {
time -= 1
timer.text = String(time)
} else {
timer.text = "0"
}
}
}
Okay I'm trying to do something similar to iTunes not sure if it's still the same. It's when you click a song and it gives a sample of the audio file. This is my code looks.
The music file is like 2-3min long. I got the start time to start at 42sec seconds. However, the song finishes to the end. I'm trying to make the audio file a sample of 30sec. So it should start at 42sec and end at 1min and 12sec.
Would appreciate any help thanks.
Every time your audio sample starts playing, you can create a Timer object which will make your player stop in a given amount of time.
var audioPlayer = AVAudioPlayer()
var timer: Timer?
func prepareMusic() {
....
// Your code to start playing sample
audioPlayer.currentTime = 42
audioPlayer.play()
// Here we are stopping previous timer if there was any, and creating new one for 30 seconds. It will make player stop.
timer?.invalidate()
timer = Timer(fire: Date.init(timeIntervalSinceNow: 30), interval: 0, repeats: false) { (timer) in
if self.audioPlayer.isPlaying {
self.audioPlayer.stop()
}
}
RunLoop.main.add(timer!, forMode: .defaultRunLoopMode)
}
func musicButton(sender: UIButton) {
....
// If sample is stopped by user — stop timer as well
if audioPlayer.isPlaying {
audioPlayer.stop()
timer?.invalidate()
}
}
There is one more edge case I can think of — if you hide/close view controller, you might also want to stop that timer.
override func viewWillDisappear(_ animated: Bool) {
timer?.invalidate()
}