1-2 seconds delay in NStimer countdown - swift2

after looking online for a countDown implementation in swift 2, i couldn't find anyones that works in a countdown way. so i made my own however when it reaches second 01 it takes 2 seconds to become 59. for instance if the timer is on 05:01 it takes 2 seconds of lag or timer freeze, then it becomes 4:59.
it looks weird, i'm a complete beginner so my code is a disaster, here it is:
#IBOutlet var countDown: UILabel!
var currentSeconds = 59
var currentMins = 5
var timer = NSTimer()
#IBAction func start(sender: UIButton) {
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(ViewController.updateTime), userInfo: nil, repeats: true)
}
func updateTime() {
if (currentSeconds > 9) {
countDown.text = "0\(currentMins):\(currentSeconds)"
currentSeconds -= 1
} else if ( currentSeconds > 0) && (currentSeconds <= 9) {
countDown.text = "0\(currentMins):0\(currentSeconds)"
currentSeconds -= 1
} else {
currentMins -= 1
currentSeconds = 59
}
if (currentSeconds == 0) && (currentMins == 0) {
countDown.text = "time is up!"
timer.invalidate()
}
}
#IBAction func stop(sender: AnyObject) {
timer.invalidate()
}

Because you forgot to update the label:
if (currentSeconds > 9) {
countDown.text = "0\(currentMins):\(currentSeconds)"
currentSeconds -= 1
} else if ( currentSeconds > 0) && (currentSeconds <= 9) {
countDown.text = "0\(currentMins):0\(currentSeconds)"
currentSeconds -= 1
} else {
countDown.text = "0\(currentMins):00" // <-- missed this
currentMins -= 1
currentSeconds = 59
}
However, it'd be better if you use NSDateFormatter to format the number of seconds left instead of managing 2 separate variables:
class ViewController: UIViewController, UITextFieldDelegate {
var secondsLeft: NSTimeInterval = 359
var formatter = NSDateFormatter()
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(ViewController.updateTime), userInfo: nil, repeats: true)
formatter.dateFormat = "mm:ss"
formatter.timeZone = NSTimeZone(abbreviation: "UTC")!
}
func updateTime()
{
countDown.text = formatter.stringFromDate(NSDate(timeIntervalSince1970: secondsLeft))
secondsLeft -= 1
if secondsLeft == 0 {
countDown.text = "time is up!"
timer.invalidate()
}
}
}

Related

Use of local variable 'updateTimer' before its declaration Egg timer

I just started learning programming and the instructor asked us to make an egg timer app.
I tried to run her example solution but XCode shows an issue with the code at #selector(updateTimer). The error says Use of local variable 'updateTimer' before its declaration.
This is the code:
class ViewController: UIViewController {
#IBOutlet weak var progress: UILabel!
let eggTimes = ["Soft" :3, "Medium":4, "Hard":6]
var secondsRemaining = 60
var timer = Timer()
#IBAction func hardnessSelected(_ sender: UIButton) {
timer.invalidate()
let hardness = sender.currentTitle!
secondsRemaining = eggTimes[hardness]!
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector:
#selector(updateTimer), userInfo: nil, repeats: true)
func updateTimer() {
if secondsRemaining > 0 {
print("\(secondsRemaining) seconds left to finish")
secondsRemaining -= 1
}
else {
timer.invalidate()
progress.text = "DONE"
}
}
}
}
The issue you're having is that the func updateTimer is declared under the #selector and as such isn't "available yet" to put it simply. What you probably wanted is to move the function outside the hardnessSelected as such:
class ViewController: UIViewController {
#IBOutlet weak var progress: UILabel!
let eggTimes = ["Soft" :3, "Medium":4, "Hard":6]
var secondsRemaining = 60
var timer = Timer()
#IBAction func hardnessSelected(_ sender: UIButton) {
self.timer.invalidate()
let hardness = sender.currentTitle!
self.secondsRemaining = self.eggTimes[hardness]!
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTimer), userInfo: nil, repeats: true)
}
#objc func updateTimer() {
if self.secondsRemaining > 0 {
print("\(self.secondsRemaining) seconds left to finish")
self.secondsRemaining -= 1
}
else {
self.timer.invalidate()
self.progress.text = "DONE"
}
}
}
You also need to add #objc in front of the function name to expose it to ObjC runtime. You can read more about that here.

Progress of UIprogress view on a label in swift3

I am trying to display the progress of the progressview in swift3 on a label using the following code but the progress gets stuck on 10% and doesn't proceeds further.
Please HELP!!!
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(myprogressview),userInfo: nil, repeats: true)
}
func myprogressview(timer : Timer)
{
var count : Int = 0
count += 1
if (count <= 10)
{
Progress1.progress = Float(count)/10.0
progressLabel.text = String.init(format: "%d %%", count*10)
}
timer.invalidate()
}
You have to invalidate the timer in else condition because when function call first time timer invalidate and declaration of count outside the myprogressview() function because each time it become zero
var count : Int = 0
func myprogressview(timer : Timer)
{
count += 1
if (count <= 10)
{
print(count*10)
progress.progress = Float(count)/10.0
progressLable.text = String.init(format: "%d %%", count*10)
} else {
timer.invalidate()
}
}

Why doesnt my timer countdown to 0 in Swift?

I have this timer and it starts at 3 and it should countdown to 0 but it stops at 2. I don't get why it doesn't go all they way down to 0. Can you please let me know what Im doing wrong with my code. Thank you!
class GameScene: SKScene, SKPhysicsContactDelegate {
var timerToStartGame = 3
var timerCountDownLabel: SKLabelNode! = SKLabelNode()
override func didMoveToView(view: SKView) {
timerCountDownLabel = SKLabelNode(fontNamed: "TimeBurner")
timerCountDownLabel.fontColor = UIColor.whiteColor()
timerCountDownLabel.zPosition = 40
timerCountDownLabel.fontSize = 60
timerCountDownLabel.position = CGPointMake(self.size.width / 2.4, self.size.height / 1.5)
self.addChild(timerCountDownLabel)
var clock = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("countdown"), userInfo: nil, repeats: true)
}
func countdown() {
timerCountDownLabel.text = String(timerToStartGame--)
if timerToStartGame == 0 {
doAction()
}
}
}
The problem occurs because you decrease it after displaying using -- after the var. Move it to the front and start from 4.
timerCountDownLabel.text = String(--timerToStartGame)

Sigabrt swift Xcode (making a timer)

I'm trying to make a stopwatch, and I can't seem to understand why I am getting a sigabrt with my application. Sorry I didn't know what to include, so I included everything.
var timer = NSTimer()
var minutes: Int = 0
var seconds: Int = 0
var fractions: Int = 0
var startStopWatch: Bool = true
var StopwatchString: String = ""
#IBOutlet weak var display: UILabel!
#IBAction func start(sender: UIButton) {
if startStopWatch == true {
NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("updateStopwatch"), userInfo: nil, repeats: true)
startStopWatch = false
} else {
timer.invalidate()
startStopWatch = true
}
}
#IBAction func reset(sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad()
display.text = "00:00.00"
}
func updateStopWatch() {
fractions += 1
if fractions == 100 {
seconds += 1
fractions = 0
}
if seconds == 60 {
minutes += 1
seconds = 0
}
let fractionsString = fractions > 9 ? "\(fractions)" : "0\(fractions)"
let secondsString = seconds > 9 ? "\(seconds)" : "0\(seconds)"
let minutesString = minutes > 9 ? "\(minutes)" : "0\(minutes)"
StopwatchString = "\(minutesString):\(secondsString).\(fractionsString)"
display.text = StopwatchString
}
I cut and pasted your code into a project and when I tapped the start button the first line of the error message said:
-[My.ViewController updateStopwatch]: unrecognized selector sent to instance 0x7fe0034b3520
That tells you all you need to know. You send the updateStopwatch message to your view controller but it doesn't implement that method. I noticed though that your ViewController does implement an updateStopWatch method. Looks like a basic capitalization error to me.
Always, always, always, read at least the first line of the error message.
Here are some fixes:
+I changed the timer variable initially to nil - it will be saved in the start function.
+Your if statement in the start function looked like this:
if startStopWatch == true
But it should be like this:
if (startStopWatch)
Hope this helpes :)
var timer = nil
var minutes = 0
var seconds = 0
var fractions = 0
var startStopWatch = true
var StopwatchString = ""
#IBOutlet weak var display: UILabel!
#IBAction func start(sender: UIButton) {
if (startStopWatch == true) {
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("updateStopwatch"), userInfo: nil, repeats: true)
startStopWatch = false
//I added one thing. If the start button is pushed the text will change to stop. You can change this if you like.
sender.setTitle("Stop", forState: .Normal)
//
} else {
timer.invalidate()
startStopWatch = true
}
}
#IBAction func reset(sender: UIButton) {
display.text! = "00:00.00"
}
override func viewDidLoad() {
super.viewDidLoad()
display.text! = "00:00.00"
}
func updateStopWatch() {
fractions++
if (fractions == 100) {
seconds++
fractions = 0
}
if (seconds == 60) {
minutes++
seconds = 0
}
var fractionsString = fractions > 9 ? "\(fractions)" : "0\(fractions)"
var secondsString = seconds > 9 ? "\(seconds)" : "0\(seconds)"
var minutesString = minutes > 9 ? "\(minutes)" : "0\(minutes)"
StopwatchString = "\(minutesString):\(secondsString).\(fractionsString)"
display.text! = StopwatchString
}

removeFromParent() does not remove a node, but makes it invisible

I have a restartButton that must appear when two bodies collide and when it happens for the first time, all goes great - bodies collide--> restartButton appears--> I restart level by touching restartButton.
It was a "good, right restart" And here problem starts...
After level been restarted, if I touch at center of the screen(where restartButton must appear when is called) game crashes, saying following:
"Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent: name:'(null)' particleTexture: 'enemyPart.png' (100 x 100) position:{721.33929, 175.39999} accumulatedFrame:{{inf, inf}, {inf, inf}}'
** First throw call stack:
(0x2a0fa137 etc.)
libc++abi.dylib: terminating with uncaught exception of type NSException"
but restartButton is invisible and it couldn't even be there because no bodies have collided.
If after that "good restart" some enemy collide with player, restartButton appears for a moment and player, enemy1, enemy2, enemy3 are fadingOut from scene.
I would appreciate if someone can help
Here is code where you can see all that stuff:
import SpriteKit
import UIKit
let player = SKEmitterNode(fileNamed: "playerPart.sks")
let enemy1 = SKEmitterNode(fileNamed: "ePart.sks")
let enemy2 = SKEmitterNode(fileNamed: "ePart.sks")
let enemy3 = SKEmitterNode(fileNamed: "ePart.sks")
let restartButton = SKSpriteNode(imageNamed: "restartButton")
let playerCat: UInt32 = 0x1 << 0
let enemyCat: UInt32 = 0x1 << 1
class Level2: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
physicsWorld.contactDelegate = self
initWorld()
movements()
player.physicsBody = SKPhysicsBody(circleOfRadius: 50)
player.position = CGPointMake(819.2 , 693.8)
player.zPosition = 1
player.physicsBody?.categoryBitMask = playerCat
player.physicsBody?.contactTestBitMask = enemyCat
player.targetNode = self
self.addChild(player)
enemy1.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(102, 102))
enemy1.position = CGPointMake(819.2, 175.4)
enemy1.zPosition = 1
enemy1.physicsBody?.affectedByGravity = false
enemy1.physicsBody?.dynamic = true
enemy1.physicsBody?.allowsRotation = false
enemy1.physicsBody?.categoryBitMask = enemyCat
enemy1.physicsBody?.contactTestBitMask = playerCat
enemy1.physicsBody?.collisionBitMask = 0x0
enemy1.targetNode = self
enemy1.particleBirthRate = 150
enemy1.particleLifetime = 10
enemy1.particleLifetimeRange = 20
enemy1.particlePositionRange = CGVectorMake(50, 60)
enemy1.emissionAngle = 0
enemy1.emissionAngleRange = 0
enemy1.particleSpeed = 0
enemy1.particleSpeedRange = 0
enemy2.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(102, 102))
enemy2.position = CGPointMake(614.4, 386.6)
enemy2.zPosition = 1
enemy2.physicsBody?.affectedByGravity = false
enemy2.physicsBody?.dynamic = true
enemy2.physicsBody?.allowsRotation = false
enemy2.physicsBody?.categoryBitMask = enemyCat
enemy2.physicsBody?.contactTestBitMask = playerCat
enemy2.physicsBody?.collisionBitMask = 0x0
enemy2.targetNode = self
enemy2.particleBirthRate = 150
enemy2.particleLifetime = 10
enemy2.particleLifetimeRange = 20
enemy2.particlePositionRange = CGVectorMake(50, 60)
enemy2.emissionAngle = 0
enemy2.emissionAngleRange = 0
enemy2.particleSpeed = 0
enemy2.particleSpeedRange = 0
enemy3.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(102, 102))
enemy3.position = CGPointMake(409.6, 181.8)
enemy3.zPosition = 1
enemy3.physicsBody?.affectedByGravity = false
enemy3.physicsBody?.dynamic = true
enemy3.physicsBody?.allowsRotation = false
enemy3.physicsBody?.categoryBitMask = enemyCat
enemy3.physicsBody?.contactTestBitMask = playerCat
enemy3.physicsBody?.collisionBitMask = 0x0
enemy3.targetNode = self
enemy3.particleBirthRate = 150
enemy3.particleLifetime = 10
enemy3.particleLifetimeRange = 20
enemy3.particlePositionRange = CGVectorMake(50, 60)
enemy3.emissionAngle = 0
enemy3.emissionAngleRange = 0
enemy3.particleSpeed = 0
enemy3.particleSpeedRange = 0
func initWorld() {
self.addChild(enemy1)
self.addChild(enemy2)
self.addChild(enemy3)
}
func movements() {
let move11 = SKAction.moveTo(CGPointMake(819.2, 386.6), duration: 1.5)
let move12 = SKAction.moveTo(CGPointMake(614.4, 386.6), duration: 1.5)
let move13 = SKAction.moveTo(CGPointMake(614.4, 175.4), duration: 1.5)
let move14 = SKAction.moveTo(CGPointMake(819.2, 175.4), duration: 1.5)
let enemy1m = SKAction.sequence([move11, move12, move13, move14])
let enemy1move = SKAction.repeatActionForever(enemy1m)
let move21 = SKAction.moveTo(CGPointMake(614.4, 591.4), duration: 1.5)
let move22 = SKAction.moveTo(CGPointMake(409.6, 591.4), duration: 1.5)
let move23 = SKAction.moveTo(CGPointMake(409.6, 386.6), duration: 1.5)
let move24 = SKAction.moveTo(CGPointMake(614.4, 386.6), duration: 1.5)
let enemy2m = SKAction.sequence([move21, move22, move23, move24])
let enemy2move = SKAction.repeatActionForever(enemy2m)
let move31 = SKAction.moveTo(CGPointMake(409.6, 386.6), duration: 1.5)
let move32 = SKAction.moveTo(CGPointMake(204.8, 386.6), duration: 1.5)
let move33 = SKAction.moveTo(CGPointMake(204.8, 181.8), duration: 1.5)
let move34 = SKAction.moveTo(CGPointMake(409.6, 181.8), duration: 1.5)
let enemy3m = SKAction.sequence([move31, move32, move33, move34])
let enemy3move = SKAction.repeatActionForever(enemy3m)
enemy1.runAction(enemy1move)
enemy2.runAction(enemy2move)
enemy3.runAction(enemy3move)
}
func didBeginContact(contact: SKPhysicsContact) {
let collision:UInt32 = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask)
if collision == (playerCat | enemyCat) {
self.removeAllActions()
self.runAction(SKAction.waitForDuration(0.1), completion: {
self.runAction(SKAction.waitForDuration(0.2), completion:
{self.removeAllActions()
self.removeChildrenInArray([enemy1, enemy2, enemy3, player])})
restartButton.size = CGSizeMake(200, 200)
restartButton.position = CGPointMake(512, 384)
restartButton.zPosition = 1
self.addChild(restartButton)
})
}
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (restartButton .containsPoint(location)) {
restartButton.runAction(fadeAway)
restartButton.removeFromParent()
println(1)
self.runAction(SKAction.waitForDuration(1.5), completion: {
let repeatLevel = SKTransition.fadeWithDuration(2)
let level2 = Level2(fileNamed: "Level2")
self.view?.presentScene(level2, transition: repeatLevel)
})
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Your restartButton is having its size set for the first time when your playerCat and enemyCat first collide, which is fine.
Since the restartButton now has a size. You can check if your touch is within the bounds of the restartButton.
if (restartButton .containsPoint(location)) {
Like you have done so, but at no point do you check wether the restartButton is added to the scene.
A quick fix could possibly be:
if (restartButton.parent != nil && restartButton .containsPoint(location)) {
If dont specifically need to check if it with the bounds of the node. You could directly check using this instead. Which will eliminate the need to check for a parent.
if (self.nodeAtPoint(location) == restartButton) {
Another thing i noticed, in your collision detection, you never check if it has already collided. So you might run the same code multiple times, where you just keep removing all actions and then adding a new one.
You could add a simple have variable to prevent redudancy
var detectionMade = false
and reset at
override func didMoveToView(view: SKView) {
physicsWorld.contactDelegate = self
initWorld()
movements()
detectionMade = false
...
and set to true when first colliding and checking
if collision == (playerCat | enemyCat) && !detectionMade {
detectionMade = true
...
#martinmeincke I've done it! By doing THIS:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (restartButton .containsPoint(location) && restartButton.parent == nil) {
restartButton.runAction(fadeAway)
println(1)
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (restartButton .containsPoint(location)) {
restartButton.removeFromParent()
self.runAction(SKAction.waitForDuration(1.5), completion: {
let repeatLevel = SKTransition.fadeWithDuration(2)
let level2 = Level2(fileNamed: "Level2")
self.view?.presentScene(level2, transition: repeatLevel)
})
}
}
}

Resources