My UIImageView does not animate across the screen until it reaches the end - swift

I am trying to bring the ball from one end of the screen to the other when the play button is clicked. My code does not redraw the ball until the ball reaches the right side of the screen.

You should enclose your code to move your ball inside a UIView.animate block.
UIView.animate(with: 1, animations: {
//Code which animates your view
})
And do not use sleep if you are meaning for the code to run after the 1 second animation. Use completion block inside UIView.animate.
UIView.animate(with: 1, animations: {
ball.center.x += balllVelocity
}, completion: (finished) in
if finished {
ball.center.x > view.frame.size.width {
gameRunning = false
}
}
}
There are lots of things you can do with UIView.animate, i suggest you go through the documentation to improve your animations.

Related

Xcode wait for animation to finish before executing next task

I have a layer that moves up when I swipe up and then the same layer moves down when I swipe down.
When I swipe up, I don’t want the user to be able to swipe down to activate that animation until the swipe up animation is complete and vice-versa.
How do I accomplish this? I’ve tried disabling the swipe gesture using “isEnabled” but no dice. Some other similar questions have answers that are from very long ago and the syntax is very different. I’m using the latest version of xcode.
This issue could be solved by adding boolean variable, IF Statement along with a DispatchQueue function to prevent another animation from occurring until the current animation has finished.
import UIKit
var animation_active = false
let animation_duration = 2 //For easy maintenance
func swipe_down() {
if animation_active == false {
animation_active == true
//Animation code goes here. Variable 'animation_active' should be used when performing the animation for easy matinence
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(animation_duration), execute: {
animation_active = false
})
}
}
func swipe_up() { //This is exactly the same as swipe_up. Yet it will prevent the swipe animation from occuring when the swipe down animation is occuruing thanks to the variable 'animation_active'
if animation_active == false {
animation_active == true
//Animation code goes here. Variable 'animation_active' should be used when performing the animation for easy matinence
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(animation_duration), execute: {
animation_active = false
})
}
}

UIViewPropertyAnimator AutoLayout Completion Issue

I'm using UIViewPropertyAnimator to run an array interactive animations, and one issue I'm having is that whenever the I reverse the animations I can't run the animations back forward again.
I'm using three functions to handle the animations in conjunction with a pan gesture recognizer.
private var runningAnimations = [UIViewPropertyAnimator]()
private func startInteractiveTransition(gestureRecognizer: UIPanGestureRecognizer, state: ForegroundState, duration: TimeInterval) {
if runningAnimations.isEmpty {
animateTransitionIfNeeded(gestureRecognizer: gestureRecognizer, state: state, duration: duration)
}
for animator in runningAnimations {
animator.pauseAnimation()
animationProgressWhenInterrupted = animator.fractionComplete
}
}
private func animateTransitionIfNeeded(gestureRecognizer: UIPanGestureRecognizer, state: ForegroundState, duration: TimeInterval) {
guard runningAnimations.isEmpty else {
return
}
let frameAnimator = UIViewPropertyAnimator(duration: duration, dampingRatio: 1) {
switch state {
case .expanded:
// change frame
case .collapsed:
// change frame
}
}
frameAnimator.isReversed = false
frameAnimator.addCompletion { _ in
print("remove all animations")
self.runningAnimations.removeAll()
}
self.runningAnimations.append(frameAnimator)
for animator in runningAnimations {
animator.startAnimation()
}
}
private func updateInteractiveTransition(gestureRecognizer: UIPanGestureRecognizer, fractionComplete: CGFloat) {
if runningAnimations.isEmpty {
print("empty")
}
for animator in runningAnimations {
animator.fractionComplete = fractionComplete + animationProgressWhenInterrupted
}
}
What I've noticed is after I reverse the animations and then call animateTransitionIfNeeded, frameAnimator is appended to running animations however when I call updateInteractiveTransition immediately after and check runningAnimations, it's empty.
So I'm led to believe that this may have to do with how swift handles memory possibly or how UIViewAnimating completes animations.
Any suggestions?
I've come to realize the issue I was having the result of how UIViewPropertyAnimator handles layout constraints upon reversal.
I couldn't find much detail on it online or in the official documentation, but I did find this which helped a lot.
Animator just animates views into new frames. However, reversed or not, the new constraints still hold regardless of whether you reversed the animator or not. Therefore after the animator finishes, if later autolayout again lays out views, I would expect the views to go into places set by currently active constraints. Simply said: The animator animates frame changes, but not constraints themselves. That means reversing animator reverses frames, but it does not reverse constraints - as soon as autolayout does another layout cycle, they will be again applied.
Like normal you set your constraints and call view.layoutIfNeeded()
animator = UIViewPropertyAnimator(duration: duration, dampingRatio: 1) {
[unowned self] in
switch state {
case .expanded:
self.constraintA.isActive = false
self.constraintB.isActive = true
self.view.layoutIfNeeded()
case .collapsed:
self.constraintB.isActive = false
self.constraintA.isActive = true
self.view.layoutIfNeeded()
}
}
And now, since our animator has the ability to reverse, we add a completion handler to ensure that the correct constraints are active upon completion by using the finishing position.
animator.addCompletion { [weak self] (position) in
if position == .start {
switch state {
case .collapsed:
self?.constraintA.isActive = false
self?.constraintB.isActive = true
self?.view.layoutIfNeeded()
case .expanded:
self?.constraintA.isActive = false
self?.constraintB.isActive = true
self?.view.layoutIfNeeded()
}
}
}
The animator operates on animatable properties of views, such as the frame, center, alpha, and transform properties, creating the needed animations from the blocks you provide.
This is the crucial part of the documentation.
You can properly animate:
frame, center, alpha and transform, so you would not be able to animate properly NSConstraints.
You should modify frames of views inside of addAnimations block

How can i interact with views while they keep animating in UIKit (swift 4)?

I have a set of shapes (UIViews), that can be moved on the screen using a UIPanGestureRecognizer, and that are supposed to wiggle continuously and forever after the user triggers the animation. But I want them to keep wiggling, even while the user picks one up and moves it.
What is practically happening is that when the user picks up a shape, it stops animating, and when he puts it back down, it doesn't even restart the animation.
Here's some code for the context:
func wiggle() {
UIView.animate(withDuration: 0.15, options: [.repeat, .autoreverse, .allowUserInteraction], animation: {
self.view.subviews.forEach { $0.transform = CGAffineTransform(rotationAngle: CGFloat.pi/8) })
}
func pickUpShape() {
[.....]
shape.transform = CGAffineTransform(scaleX: 2, scaleY: 2)
shape.layer.shadowOpacity = 0.7
}
func moveShape() {
//apply UIPanGestureRecognizer
}
func placeShape() {
[.....]
shape.transform = .identity
shape.layer.shadowOpacity = 0
}
What I want it to do is, if the user triggers the wiggle action, and picks a shape up and moves it, i want the shape to continue wiggling under the user's finger, and after he places it down.

turn animation 180 degrees in swift

I am using a curl up animation when swiping up, which makes a card fly away to the top - it works fine. I'd like to use the same animation, but to swipe down, so that the card flies away down. I guess I'd have to somehow turn the swipe up animation 180 degrees. Is that possible?
let views = (frontView: self.frontView, backView: self.frontView)
// set a transition style
let transitionOptions = UIViewAnimationOptions.TransitionCurlUp
UIView.transitionWithView(self.flashCardView, duration: 0.5, options: transitionOptions, animations: {
views.frontView.removeFromSuperview()
self.flashCardView.addSubview(views.frontView)
}, completion: { finished in
// any code entered here will be applied
})
The UIViewAnimationOptions struct has another option that will solve this for you:
let transitionOptions = UIViewAnimationOptions.TransitionCurlDown

Animation in core graphics

I followed this awesome Rey Wenderlich tutorial to make an Bezier arc and increment/decrement values. But how to animate the arch instead of just step-up and step-down?
http://www.raywenderlich.com/90690/modern-core-graphics-with-swift-part-1
I tried putting animation block in custom property declaration, which I dont think is the right place to do it and xcode doesn't let me do it anyway.
#IBInspectable var counter: Int = 5 {
didSet {
if counter <= NoOfGlasses {
//the view needs to be refreshed
UIView.animateWithDuration(0.2, animations: {
setNeedsDisplay()
}, completion:nil
)
}
}
}
Also tried to put the increment in animation block in View controller, didn't work.
#IBAction func btnPushButton(sender: AnyObject) {
UIView.animateWithDuration(0.2, animations: {
self.arcView.counter = self.arcView.counter + 10
self.counterLabel.text = String(self.arcView.counter)
}, completion:nil
)
}
Are you describing this sort of thing?
That's a simpler example - it's just a drawn triangle - but it's the same idea, if I'm understanding you correctly: we are animating the difference between one drawing and another.
Basically you have two choices. The easy way is to use CAShapeLayer, which animates for you automatically when you change its path. The other choice is to do what I'm doing here, which is to create a custom animatable property - in this case, a property representing the x-position of the bottom point of the triangle.