How to stop an UIView animation in swift - swift

I'm working with Swift language. I wrote an animation for a timer that lasts for 30 seconds and is full. Now I want to stop this animation, but I do not know how! I want to be able to start again from the beginning. Animation.
Thanks if you have a solution or a method that helps me🙏

You can try
self.myView.layer.removeAllAnimations()

Two possible ways:
Somewhere store variable for total time. And in every timer repeat increase this value. When total time reaches 30 seconds, remove animation
When you start animation, set action which gets executed after specific time
First possible way:
var time: Double = 0
#objc func timerChangedValue() {
time += 1
if time == 30 {
view.layer.removeAllAnimations()
view.layoutIfNeeded()
}
}
Second possible way:
DispatchQueue.main.asyncAfter(deadline: .now() + 30) {
self.view.layer.removeAllAnimations()
self.view.layoutIfNeeded()
}

Related

Making a spriteNode fade away

Swift 5, iOS 14
Wrote this code to fade in a sprite node... works ok, but Xcode says this, which makes no sense since I am using timer within the loop.
Initialization of immutable value 'timer' was never used; consider replacing with assignment to '_' or removing it
let timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
node2Delete?.alpha -= 0.1
ghost.alpha += 0.1
if node2Delete?.alpha == 0 {
node2Delete?.removeFromParent()
timer.invalidate()
}
}
And it doesn't complain about the timer within the loop not being defined? Is this a bug or am I missing something?
Is there a better way to do this? Is the timer I set here actually being cancelled?
You want to use SKAction not Timer here, so you can replace all your code with the below.
Create two variables which hold the fade logic, one for fade in, the other for fade out.
Run fadeIn on your ghost node. The ghost node will gradually appear over the course of 5 seconds.
Run fadeOuton your node2Delete node, this will gradually fade the alpha to 0 over the course of 5 seconds. Once completed, we remove the node from it's parent.
let fadeOut = SKAction.fadeAlpha(to: 0.0, duration: 5.0)
let fadeIn = SKAction.fadeAlpha(to: 1.0, duration: 5.0)
ghost.run(fadeIn)
node2Delete.run(fadeOut){
self.node2Delete.removeFromParent()
}

action does not wait till function call ends

I have 2 actions that i put in a sequence. In the first action I am calling a method to calculate the new waiting time for the next action. The next action is just a wait for this duration, but the second action always executes straight away, so the time must be 0. I debugged it and in the method spawnFlowers i get the time returned as 3.5 seconds.
these are my 2 actions
let spawnFlowerAction = SKAction.run {
self.WaitTime = self.calculateWaitingTime()
}
let waitForNewFlower = SKAction.wait(forDuration: self.WaitTime)
I execute it this way:
let spawnSeq = SKAction.sequence([spawnFlowerAction, waitForNewFlower])
let spawnRepeat = SKAction.repeat(spawnSeq, count: 4)
self.run(spawnRepeat)
Result: 4 times spawned without waiting, printing 4 different calculated times in the console from the calculateWaitingTime function (in which the spawning happens)
What is a good way to fix this?
The problem is trying to dynamically change the values used within SKActions after the action has been created. For example when your WaitTime variable changes while running the spawnFlowerAction, the waitForNewFlower Action's wait time won't change dynamically because it doesn't reference WaitTime. Instead its wait value is whatever your variable WaitTime was when you declared let waitForNewFlower = SKAction.wait(forDuration: self.WaitTime) (Which I'm guessing was initially 0). Same concept goes with your other two spawn actions.
I usually use the dispatch Queue for things like these, but to use SKActions here's a function. Just call it once and input the number of times you want it to repeat.
func spawnRepeat(count: Int) {
//Put whatever code to spawn flower here
print("SPAWN FLOWER")
if count > 1 {
//Recalculate WaitTime
WaitTime = calculateWaitingTime()
let waitAction = SKAction.wait(forDuration: WaitTime)
run(waitAction, completion: { self.spawnRepeat(count: count - 1) })
}
}

Uneven typing in Spritekit SKAction sequence led typing animation

Hi I know there are plenty of questions on here about the timer, but nothing I can find about this specific issue. Thanks in advance for any help.
I am trying to use a sequence of SKActions to simulate a typing animation (in an SKLabelNode I've called actualLabel) that begins after a user touch.
I have the following:
var charArray = []
var labelText = ""
var calls = 0
And then, in touchesEnded:
if calls < charArray.count + 1 {
let wait = SKAction.wait(forDuration: 1)
let block = SKAction.run({
self.redoLabelText()
})
let sequence = SKAction.sequence([wait,block])
run(SKAction.repeatForever(sequence))
}
With my function as the following:
func redoLabelText() {
if labelText.characters.count < charArray.count {
labelText += charArray[calls]
actualLabel.text = labelText
calls += 1
}
}
And then touchesBegan resets all the initial variables and the process starts again. Everything works fine, except the typing is really choppy. Every time the user presses again, the typing gets faster. It's as if after one press it works fine, then after two etc there are more characters waiting in labelText at each second interval.
Thanks again, got me baffled.

How can I create delay inside while loop in Swift 2?

I would need help with this while loop - what I'm trying to do is slow down the whole process of removing and adding new circles while radius is changing every time this happens. I'm becoming really desperate, I've tried using both dispatch_after and sleep inside the loop (which I found online) but neither of them is suitable, they basically stop the whole app. If I put them in the while loop, nothing happens. Thanks in advance!
while radius < 100 {
self.removeAllChildren()
addCircle()
radius++
print(radius)
}
Basically you just need to do few simple things:
Wait for a certain duration and add a node to the scene
Repeat this step forever (or certain number of times)
Here is the example of how you can do it. The important part is action sequence. You create the step above, and repeat it forever. Each time you check radius value and based on that you stop the action (remove it by the key). And that's it. You can change spawning speed by changing action's duration parameter.
Using NSTimer might seem like an easier solution, but NSTimer doesn't respect node's (or scene's or view's) paused state. So, imagine this situation:
You start spawning of nodes
User receive phone call and app automatically goes to background
Because NSTimer is not automatically paused, the nodes will continue with spawning. So you have to take an additional step and invalidate/restart timer by your self. When using SKAction, this is done automatically. There are some other flaws of using NSTimer in SpriteKit, search SO about all that, there are some posts which covering all this.
import SpriteKit
class GameScene: SKScene{
var radius:UInt32 = 0
override func didMoveToView(view: SKView) {
startSpawning()
}
func startSpawning(){
let wait = SKAction.waitForDuration(0.5)
// let wait = SKAction.waitForDuration(1, withRange: 0.4) // randomize wait duration
let addNode = SKAction.runBlock({
[unowned self] in //http://stackoverflow.com/a/24320474/3402095 - read about strong reference cycles here
if(self.radius >= 30){
if self.actionForKey("spawning") != nil {
self.removeActionForKey("spawning")
}
}
self.radius++
let sprite = self.spawnNode()
sprite.position = CGPoint(x: Int(arc4random()) % 300, y: Int(arc4random()) % 300) // change this to randomize sprite's position to suit your needs
self.addChild(sprite)
})
//wait & add node
let sequence = SKAction.sequence([wait, addNode])
//repeat forever
runAction(SKAction.repeatActionForever(sequence), withKey: "spawning")
}
func spawnNode()->SKSpriteNode{
let sprite = SKSpriteNode(color: SKColor.purpleColor(), size: CGSize(width: 50, height: 50))
//Do sprite initialization here
return sprite
}
}
The sleep stops the whole app because you are running the loop on the main thread.
You can solve this problem in one of two ways...
1) Use an NSTimer. Set it to the amount of time you want to delay. Put the body of the loop in the timer's trigger function and disable the timer when it has been called enough times. This is the best solution.
2) Wrap the loop in a dispatch async block to a background thread and put a sleep in the loop. If you do this though, you will have to wrap the UI code in another dispatch async block that comes back to the main thread.

Swift/SpriteKit: Animate multiple buttons with small delay between each

I have several buttons (SKSpriteNodes) which I am trying to cycle through and animate, with a small delay between each. My code compiles, but when I run it - I only get a white screen and a crash with this error: "Message from debugger: Terminated due to memory issue". Here is my code:
var sequence = SKAction.sequence([animationUp, animationDown])
runAction(SKAction.repeatActionForever(SKAction.sequence([
SKAction.runBlock({
button1.runAction(sequence)
SKAction.waitForDuration(0.5)
button2.runAction(sequence)
SKAction.waitForDuration(0.5)
button3.runAction(sequence)
SKAction.waitForDuration(0.5)
}),
])))
So what I am trying to accomplish is an up/down animation on every button I'm drawing to the screen, with a 0.5 second delay between each button. The animation should run forever until I change the current screen. I had no problem animating these buttons simultaneously but I'd really like to add a uniform delay so that they don't all animate at the same time. Any ideas?
let waitAction = SKAction.waitForDuration(0.5)
let movementAction = SKAction.sequence([animateUp, animateDown])
let button1Block = SKAction.runBlock({
button1.runAction(movementAction)
})
let button2Block = SKAction.runBlock({
button2.runAction(movementAction)
})
let button3Block = SKAction.runBlock({
button3.runAction(movementAction)
})
let sequence = SKAction.sequence([button1Block,waitAction,button2Block, waitAction, button3Block, waitAction])
runAction(SKAction.repeatActionForever(sequence))
I'm not certain about how the SKAction.sequence initiates/destroys, but possibly your loop in .repeatActionForever(..) creates and destroys objects, whereas memory won't be released until the loop is allowed to complete.
Try to run your inner runAction block within an autoreleasepool { .. } block:
autoreleasepool {
runAction ...
}
But there is one situation we do need to autorelease, that is we
create a lot of objects in a method scope and want to release them
sooner. It is extremely useful when these objects turn to a pressure
on memory.
http://en.swifter.tips/autoreleasepool/