I ran the code below and the label was moved perfectly. But I want to increase the value of the label at the same time with each move. I have tried many ways but it doesn't work.
UIView.animateKeyframes(withDuration: 6, delay: 0, options: [], animations: {
UIView.addKeyframe(withRelativeStartTime: (0 / 6), relativeDuration: (2 / 6), animations: {
self.label.transform = label.transform.translatedBy(x: 0, y: 50) //OK
//I want the value of the label to be increased from 0 to 5 (exactly equal to the duration of this key frame)
})
UIView.addKeyframe(withRelativeStartTime: (2 / 6), relativeDuration: (2 / 6), animations: {
self.label.transform = label.transform.translatedBy(x: 50, y: 0) //OK
//Then I want the value of the label to decrease from 5 to 0
})
}, completion: nil)
Can you help me? Thanks in advance!
You will not be able to do it within the animation block, you can use Timer and change label's text property with it.
Example, in your case I would do it like this:
var tick = 0
let duration: TimeInterval = 6
let countTo = 5
Timer.scheduledTimer(withTimeInterval: duration / (2 * Double(countTo)), repeats: true) { timer in
tick += 1
let mod = tick % countTo
if tick >= countTo, tick < countTo * 2 {
label.text = String(countTo - mod)
} else {
label.text = String(mod)
}
if tick >= countTo * 2 {
timer.invalidate()
}
}
UIView.animateKeyframes(withDuration: duration, delay: 0, options: [], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5, animations: {
label.transform = label.transform.translatedBy(x: 0, y: 50)
})
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5, animations: {
label.transform = label.transform.translatedBy(x: 50, y: 0)
})
}, completion: nil)
This way you can set duration you need and to what number you want to count to and back to 0 within the duration.
Related
I'm having an issue trying to touch a label that keeps animating because during the animation it becomes an image (set the animation on repeat). Is there a way to touch it during animation (I cant't use UITapGestureRecognizer) ?
func animatePro() {
UIView.animate(withDuration: 2, delay: 0, options: [.autoreverse, .repeat], animations: {
//1.5 times it's normal size
self.proLabel.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
}){ (finished) in
self.proLabel
.transform = CGAffineTransform.identity
}
}
func animateRev() {
UIView.animate(withDuration: 2, delay: 0, options: [.repeat], animations: {
//1.5 times it's normal size
self.revLabel.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
}){ (finished) in
UIView.animate(withDuration: 2, delay: 0, animations: {
self.revLabel.transform = CGAffineTransform.identity
})
}
}
Add allowUserInteraction
options: [.allowUserInteraction,.autoreverse, .repeat]
I want to make an animation that move a background image from right to left than left to right.
I use this code but not works
UIView.animate(withDuration: 5, delay: 0, options: .repeat ,animations: { () -> Void in
imageView.transform = CGAffineTransform(translationX: -imageView.frame.width + self.view.bounds.width, y: 0)
UIView.animate(withDuration: 5, delay: 1, options: .repeat ,animations: { () -> Void in
imageView.transform = CGAffineTransform(translationX: +imageView.frame.width - self.view.bounds.width, y: 0)
})
})
I solved with this
UIView.animate(withDuration: 30.0, delay: 0, options: [.repeat, .autoreverse], animations: {
imageView.transform = CGAffineTransform(translationX: -imageView.frame.width + self.view.bounds.width, y: 0)
}, completion: nil)
Use the following code if you want to use the legacy animate(withDuration:animations:) API. However, you need to wait for the first animation to be finished - this means you have to use the completion block.
However, your value for translationX does not makes sense as well.
UIView.animate(withDuration: 1, animations: {
self.imageView.transform = CGAffineTransform(translationX: -self.imageView.frame.width, y: 0)
}) { _ in
UIView.animate(withDuration: 1) {
self.imageView.transform = CGAffineTransform.identity
}
}
I would strongly recommend having a look at the "new" way of doing Animations: UIViewPropertyAnimator(https://developer.apple.com/documentation/uikit/uiviewpropertyanimator)
I have a function I am using to animate 4 UILabels. The animation is chained and I am trying to reduce repeated code by creating a recursive function that accepts the label as an argument, on completion calls itself with the next label.
fileprivate func handleAnimations(firstLabel: UILabel, secondLabel: UILabel) -> Void {
UIView.animate(withDuration: 1, delay: 2, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {
firstLabel.alpha = 1
}) { (_) in
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {
secondLabel.alpha = 1
}) { (_) in
UIView.animate(withDuration: 0.5, delay: 3, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
firstLabel.alpha = 0
secondLabel.alpha = 0
}, completion: { (_) in
self.handleAnimations(firstLabel: self.introTextLabelThree, secondLabel: self.introTextLabelFour)
})
}
}
}
Is this the best way to achieve this? If so, how I prevent the final complete block calling itself over and over with the final 2 labels?
The effect I am trying to achieve is:
labelOne - fades in
labelTwo - fades in
labelOne and labelTwo - fade out
labelThree - fades in
labelFour - fades in
labelThree and labelFour - fade out
After labelThree and labelFour I would be looking to implement another function call.
Do not do this. Please use animateKeyframes. This will allow you to properly chain animations without the dreaded pyramid of doom.
UIView.animateKeyframes(withDuration: 5.0, delay: 0, options: [.calculationModeCubic], animations: {
// Add animations
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1.0/6.0, animations: {
self.introTextLabel.alpha = 1
})
UIView.addKeyframe(withRelativeStartTime: 1.0/6.0, relativeDuration: 1.0/5.0, animations: {
self.introTextLabelTwo.alpha = 1
})
UIView.addKeyframe(withRelativeStartTime: 2.0/6.0, relativeDuration: 1.0/5.0, animations: {
self.introTextLabel.alpha = 0
self.introTextLabelTwo.alpha = 0
})
UIView.addKeyframe(withRelativeStartTime: 3.0/6.0, relativeDuration: 1.0/5.0, animations: {
self.introTextLabelThree.alpha = 1
})
UIView.addKeyframe(withRelativeStartTime: 4.0/6.0, relativeDuration: 1.0/5.0, animations: {
self.introTextLabelFour.alpha = 1
})
UIView.addKeyframe(withRelativeStartTime: 5.0/6.0, relativeDuration: 1.0/5.0, animations: {
self.introTextLabelThree.alpha = 0
self.introTextLabelFour.alpha = 0
})
}, completion:{ _ in
// fire off whatever other method you want here
})
I did that...
ivImageLoadingInside.center = CGPoint(x: 25, y: 0)
UIView.animate(withDuration: 2, delay: 0, options: .repeat, animations: {
self.ivImageLoadingInside.center = CGPoint(x: 80, y: 0)
}, completion: nil)
But always stops in the middle.
Here is the image with the animation
private func animation(viewAnimation: UIView) {
UIView.animate(withDuration: 2, animations: {
viewAnimation.frame.origin.x = +viewAnimation.frame.width
}) { (_) in
UIView.animate(withDuration: 2, delay: 1, options: [.curveEaseIn], animations: {
viewAnimation.frame.origin.x -= viewAnimation.frame.width
})
}
}
self.ivImageLoadingInside.center = CGPoint(x: 80 - self.ivImageLoadingInside.bounds.width / 2, y: 0)
I got it! You have to put the constraints and do this...
if animate {
ivImageLoadingInside.frame = CGRect(x: 0 - 160, y: 123, width: 160, height: 12)
UIView.animate(withDuration: 1.2, delay: 0, options: .repeat, animations: {
self.ivImageLoadingInside.frame = CGRect(x: ScreenSize.SCREEN_WIDTH, y: 123, width: 160, height: 12)
}, completion: nil)
} else {
ivImageLoadingInside.layer.removeAllAnimations()
}*
Look at this link http://mathewsanders.com/prototyping-iOS-iPhone-iPad-animations-in-swift/
I am having an issue with an SKAction sequence I set up.
The goal of this sequence is to take an array of 9 sprite nodes and display one at a time. This sequence will then show the node to the right or to the left of the current node depending on which button is pressed (right button or left button).
I am experiencing something interesting here. It seems to work as it is now, but if I press button left or button right multiple times fast, it seems like it cannot keep up and the process fails. I don't get an error, the sprite nodes just don't display or don't display correctly.
I have attached my code below. Because it is working when I cycle through slowly, I believe it is a timing issue and maybe I need to add a completion block. I tried doing this but was unsuccessful.
My question is, does anything obvious stick out that looks wrong here and do you think a completion block could possibly solve the issue?
func pressedButtonRight(){
if currentDisplayedWorld < 8 {
var currentWorld:SKSpriteNode = worldArray[currentDisplayedWorld] as SKSpriteNode
var nextWorld:SKSpriteNode = worldArray[currentDisplayedWorld + 1] as SKSpriteNode
nextWorld.position = CGPointMake(self.frame.size.width, 50)
self.addChild(nextWorld)
let move = SKAction.moveByX(-self.frame.size.width, y: 0,
duration: 1.0, delay: 0,
usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5)
//currentWorld.runAction(move)
nextWorld.runAction(move)
currentWorld.removeFromParent()
currentDisplayedWorld++
}else if currentDisplayedWorld == 8 {
}
}
func pressedButtonLeft(){
if currentDisplayedWorld > 0 {
var currentWorld:SKSpriteNode = worldArray[currentDisplayedWorld] as SKSpriteNode
var previousWorld:SKSpriteNode = worldArray[currentDisplayedWorld - 1] as SKSpriteNode
previousWorld.position = CGPointMake(-self.frame.size.width, 50)
self.addChild(previousWorld)
let moveBack = SKAction.moveByX(self.frame.size.width, y: 0,
duration: 1.0, delay: 0,
usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5)
//currentWorld.runAction(moveBack)
previousWorld.runAction(moveBack)
currentWorld.removeFromParent()
currentDisplayedWorld--
}else if currentDisplayedWorld == 0 {
}
}
Worked out perfect. I posted my final code below for all to see. Thanks again for the help.
func pressedButtonRight(){
if currentDisplayedWorld < 8 {
if self.moving == false {
self.moving = true
var currentWorld:SKSpriteNode = self.worldArray[currentDisplayedWorld] as SKSpriteNode
var nextWorld:SKSpriteNode = self.worldArray[currentDisplayedWorld + 1] as SKSpriteNode
nextWorld.position = CGPointMake(self.frame.size.width, 50)
self.addChild(nextWorld)
let move = SKAction.moveByX(-self.frame.size.width, y: 0, duration: 1.0, delay: 0,usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5)
//currentWorld.runAction(move)
nextWorld.runAction(move, completion: {
self.moving = false
})
currentWorld.removeFromParent()
currentDisplayedWorld++
}
}else if currentDisplayedWorld == 8 {
}
}
func pressedButtonLeft(){
if currentDisplayedWorld > 0 {
if self.moving == false {
self.moving = true
var currentWorld:SKSpriteNode = self.worldArray[currentDisplayedWorld] as SKSpriteNode
var previousWorld:SKSpriteNode = self.worldArray[currentDisplayedWorld - 1] as SKSpriteNode
previousWorld.position = CGPointMake(-self.frame.size.width, 50)
self.addChild(previousWorld)
let moveBack = SKAction.moveByX(self.frame.size.width, y: 0, duration: 1.0, delay: 0,
usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5)
//currentWorld.runAction(moveBack)
previousWorld.runAction(moveBack, completion: {
self.moving = false
})
currentWorld.removeFromParent()
currentDisplayedWorld--
}
}else if currentDisplayedWorld == 0 {
}
}