UIView Animation in Swift3 - ios

I'm trying to move a UIImageView to top, bottom, left and right. Animation should be 4 four times. Center image move towards top, then left position and right position. How can I achieve this using UIAnimation swift 3?
UIView.animate(withDuration: 0.1,
delay:0,
animations: {},
completion: {completion in})
How to move image to top, left, right and bottom using animation?

You should use a keyframe animation for this, as it allows you to specify the order for the animations. Below is a basic example:
let animationDuration = 2.0
let position: CGFloat = 50.0
let viewToAnimate = UIImageView()
UIView.animateKeyframes(withDuration: animationDuration, delay: 0.0, options: .calculationModeLinear, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1/3, animations: {
viewToAnimate.frame.origin.y = viewToAnimate.frame.origin.y + position
})
UIView.addKeyframe(withRelativeStartTime: 1/4, relativeDuration: 1/4, animations: {
viewToAnimate.frame.origin.y = viewToAnimate.frame.origin.y - (position * 2)
})
UIView.addKeyframe(withRelativeStartTime: 2/4, relativeDuration: 1/4, animations: {
viewToAnimate.frame.origin.x = viewToAnimate.frame.origin.x - position
})
UIView.addKeyframe(withRelativeStartTime: 3/4, relativeDuration: 1/4, animations: {
viewToAnimate.frame.origin.x = viewToAnimate.frame.origin.x + (position * 2)
})
}, completion: { completed in
})

UIView.animate(withDuration: 1, delay:0, animations: {
//top
self.imageView.frame.origin.y = self.imageView.frame.origin.y - 100
}, completion: {completion in
UIView.animate(withDuration: 1, delay:0, animations: {
//bottom
self.imageView.frame.origin.y = self.imageView.frame.origin.y + 100
}, completion: {completion in
UIView.animate(withDuration: 1, delay:0, animations: {
//left
self.imageView.frame.origin.x = self.imageView.frame.origin.x - 100
}, completion: {completion in
UIView.animate(withDuration: 1, delay:0, animations: {
}, completion: {completion in
//right
self.imageView.frame.origin.x = self.imageView.frame.origin.x + 100
})
})
})
})

UIView.animate(withDuration: 1, delay:0, animations: {
//top
self.tempVW.frame.origin.y = self.tempVW.frame.origin.y - 100
}, completion: {completion in
UIView.animate(withDuration: 1, delay:0, animations: {
//left
self.tempVW.frame.origin.x = self.tempVW.frame.origin.x - 100
}, completion: {completion in
UIView.animate(withDuration: 1, delay:0, animations: {
//bottom
self.tempVW.frame.origin.y = self.tempVW.frame.origin.y + 100
}, completion: {completion in
UIView.animate(withDuration: 1, delay:0, animations: { //right
self.tempVW.frame.origin.x = self.tempVW.frame.origin.x + 100
}, completion: {completion in
})
})
})
})

Related

How to animate a constantly flickering light in iOS?

I'm trying to build an iOS app with a neon light that flickers randomly and constantly like a real one would.
I'm not totally sure how to get the animation to repeat constantly, and I'm not sure how to get it to just run automatically and forever. I put this in the viewDidLoad, but I'm not sure if that's really the best place to put it?
UIImageView.animate(withDuration: 0.05, delay: 5.0, options: .repeat, animations: {
UIImageView.animate(withDuration: 0.05, delay: 2.0, animations: {
self.Aletter.alpha = 0.2
}) { (_) in
UIImageView.animate(withDuration: 0.05, delay: 0.0, animations: {
self.Aletter.alpha = 1.0
}, completion: { (_) in
UIImageView.animate(withDuration: 0.05, delay: 2.0, animations: {
self.Aletter.alpha = 0.6
}, completion: { (_) in
UIImageView.animate(withDuration: 0.05, delay: 0.0, animations: {
self.Aletter.alpha = 1.0
}, completion: { (_) in
UIImageView.animate(withDuration: 0.05, delay: 0.0, animations: {
self.Aletter.alpha = 0.6
}, completion: { (_) in
UIImageView.animate(withDuration: 0.05, delay: 0.0, animations: {
self.Aletter.alpha = 1.0
}, completion: { (_) in
UIImageView.animate(withDuration: 0.05, delay: 0.0, animations: {
self.Aletter.alpha = 0.6
}, completion: { (_) in
UIImageView.animate(withDuration: 0.05, delay: 0.0, animations: {
self.Aletter.alpha = 1.0
}, completion: { (_) in
})
})
})
})
})
})
})
}
})
This code will run my series of flickers, but only once. I need it to go continuously.
As suggested by Matt in the comments section, you can use something like this:
private func flicker() { [weak self] in
UIView.animate(withDuration: 0.05, animations: {
self?.Aletter.alpha = CGFloat.random(in: 0.1...1.0)
}) { _ in
// When this round of animations completes call the same method again to start the animations again with a new random value for alpha.
self?.flicker()
}
}
And just call flicker() in your viewDidLoad(). flicker method starts an animation with a random alpha value for Aletter view, and when this animation completes it calls itself again.
As a side note, use small first letters for your variables, Aletter should be aletter or maybe aLetter based on the context.

Unwanted button preview before animation during transition to second controller

In a navigation controller embedded project, using animations, I cannot avoid getting a "fast preview" of the buttons present in the next ViewController, where those buttons will slide from left border of the screen to the center.
So, when I tap in first VC, see for an instant the buttons in second VC, and then the second VC appears and the animation works (and it works fine)
In home controller the button is:
#objc func imageTapped(tapGestureRecognizer: UITapGestureRecognizer)
{
print("tapped")
//animate 1/2
UIView.animate(withDuration: 0.8, animations: { () -> Void in
self.mainButtonContainerView.center.y -= self.view.bounds.width //self.view.frame.origin
self.mainButtonContainerView.isUserInteractionEnabled = false
self.mainButtonContainerView.alpha = 0.0
})
//animate2/2
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1) ) {
self.pushShowNextVC_ChoiceVC(dataToPass: "")
}
}
Then, the animation in second VC ViewDidLoad is:
for singleView in coantinersOutCollection {
//animation
singleView.center.x -= self.view.bounds.width
singleView.layer.cornerRadius = singleView.bounds.size.height/2
singleView.backgroundColor = UIColor.white
singleView.layer.borderColor = Constants.skyBlue3Color.cgColor
singleView.layer.borderWidth = 1
}
for singleLabel in labesForButtons {
singleLabel.textColor = Constants.skyBlue3Color
singleLabel.font = UIFont.boldSystemFont(ofSize: 17.0)
}
for singleImage in self.iconButtonImages {
// singleImage.image = UIImage(named: "")
singleImage.backgroundColor = UIColor.clear
singleImage.layer.borderWidth = 1
singleImage.layer.borderColor = Constants.skyBlue3Color.cgColor
}
UIView.animate(withDuration: 1.5, delay: 0.6,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 1,
options: [], animations: {
self.containerDownloadButton.center.x += self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 1.5, delay: 0.7,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 1,
options: [], animations: {
self.containerUploadButton.center.x += self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 1.5, delay: 0.8,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 1,
options: [], animations: {
self.containerCallButton.center.x += self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 1.5, delay: 0.9,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 1,
options: [], animations: {
self.containerIdleButton.center.x += self.view.bounds.width
}, completion: nil)

How to let CATransaction repeat infinitely? - Swift

I have an animate function which contains CATransaction.begin()
I want this animation to be repeated infinitely or for a defined number of times.
How do I make that happen?
This is the animate function if you need to see the code:
private func animate(views: [UIView], duration: TimeInterval, intervalDelay: TimeInterval) {
CATransaction.begin()
CATransaction.setCompletionBlock {
print("COMPLETED ALL ANIMATIONS")
}
var delay: TimeInterval = 0.0
let interval = duration / TimeInterval(views.count)
for view in views {
let transform = view.transform
UIView.animate(withDuration: interval, delay: delay, options: [.curveEaseIn], animations: {
view.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}, completion: { (finished) in
UIView.animate(withDuration: interval, delay: 0.0, options: [.curveEaseIn], animations: {
view.transform = transform
}, completion: { (finished) in
})
})
delay += (interval * 2.0) + intervalDelay
}
CATransaction.commit()
}
I think CATransaction redundant there
If I understand what you want to achieve
UIView.animate(withDuration: interval, delay: delay, options: [.curveEaseIn, .autoreverse, .repeat], animations: {
self.views.forEach{$0.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)}
}, completion: nil)
EDIT: Recursive function to do pulse in circles
func pulse(index: Int) {
guard views.count > 0 else { return }
let resolvedIndex = (views.count < index) ? index : 0
let duration = 1.0
let view = views[resolvedIndex]
UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseIn,.autoreverse], animations: {
self.view.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}) { [weak self] _ in
self?.pulse(index: resolvedIndex + 1)
}
}

Chaining Animations in Swift

Recently, I have made some code which draws a face. I wanted to animate the face to shake back and forth. Currently I have this code. It rotates once to the right, then more to the left, and then to the original position. However, what if I wanted the head to infinitely shake back and forth(rotate bath and forth). Is it possible to make some sort of recursive function to do this?
#IBAction func shakeHead(_ sender: UITapGestureRecognizer) {
UIView.animate(
withDuration: 0.5,
animations: {
self.faceView.transform = self.faceView.transform.rotated(by: self.shakeAngle)
},
completion:{ finished in
if(finished){
UIView.animate(
withDuration: 0.5,
animations: {
self.faceView.transform = self.faceView.transform.rotated(by: -(self.shakeAngle)*2)
},
completion:{ finished in
if(finished){
UIView.animate(
withDuration: 0.5,
animations: {
self.faceView.transform = self.faceView.transform.rotated(by: self.shakeAngle)
},
completion: nil
)
}
}
)
}
}
)
}
You could call shakeHead from the final completion block.
#IBAction func shakeHead(_ sender: UITapGestureRecognizer) {
UIView.animate(
withDuration: 0.5,
animations: {
self.faceView.transform = self.faceView.transform.rotated(by: self.shakeAngle)
},
completion:{ finished in
if(finished){
UIView.animate(
withDuration: 0.5,
animations: {
self.faceView.transform = self.faceView.transform.rotated(by: -(self.shakeAngle)*2)
},
completion:{ finished in
if(finished){
UIView.animate(
withDuration: 0.5,
animations: {
self.faceView.transform = self.faceView.transform.rotated(by: self.shakeAngle)
},
completion: { finished in
shakeHead(sender)
}
)
}
}
)
}
}
)
}
Even though this is technically a recursive call, it's not a problem due to the asynchronous nature of the code.
Use animateKeyframes for a better chain animation experience
#IBAction func shakeHead(_ sender: UITapGestureRecognizer) {
UIView.animateKeyframes(withDuration: animationDuration, delay: 0, options: [], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
self.faceView.transform = self.faceView.transform.rotated(by: self.shakeAngle)
}
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
self.faceView.transform = self.faceView.transform.rotated(by: -(self.shakeAngle)*2)
}
UIView.addKeyframe(withRelativeStartTime: 1.0, relativeDuration: 0.5) {
self.faceView.transform = self.faceView.transform.rotated(by: self.shakeAngle)
}
}) { (isFinished) in
shakeHead(sender)
}
}

Consecutive fade in fade out animations

I'm trying to implement this animation where a UIButton fades out when a user saves a file. While the button fades out, a UIImageView of a check mark image fades in place of the button. Once the imageview has fully faded in, then it fades out and the button fades back in.
UIView.animateWithDuration(0.60, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.recButton.alpha = 0.0
}, completion: {
(value: Bool) in
UIView.animateWithDuration(0.60, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.checkView.alpha = 1.0
}, completion: {
(value: Bool) in
UIView.animateWithDuration(0.60, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.checkView.alpha = 0.0
}, completion: {
(value: Bool) in
UIView.animateWithDuration(0.60, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.recButton.alpha = 1.0
self.recButton.enabled = true
}, completion: nil)
})
})
})
While the method above does work, its not as smooth as I would like. Is there a better way to go about this?
You need four keyframes for the animation you want: button fade out, image fade in, image fade out, button fade in.
let button: UIButton
let checkmarkImage: UIImageView
button.alpha = 1.0
checkmarkImage = 0.0
UIView.animateKeyframesWithDuration(2.4, delay: 0, options: .CalculationModeLinear, animations: {
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.25, animations: {
button.alpha = 0.0
})
UIView.addKeyframeWithRelativeStartTime(0.25, relativeDuration: 0.25, animations: {
checkmarkImage.alpha = 1.0
})
UIView.addKeyframeWithRelativeStartTime(0.5, relativeDuration: 0.25, animations: {
checkmarkImage.alpha = 0.0
})
UIView.addKeyframeWithRelativeStartTime(0.75, relativeDuration: 0.25, animations: {
button.alpha = 1.0
})
}, completion: nil)
This question raised my curiosity - this keyframe thing is pretty cool.

Resources