How rotate .transitionCurlDown animation? - ios

How rotate .transitionCurlDown animation to get the rotation of the book sheet (horizontal, not vertical), same as this:
There is vertical page curl animation:
UIView.transition(with: self.myView, duration: 0.7,
options: .transitionCurlDown, animations: {
})
Important: I don't want to use transition between UIViewControllers or UIPageControllers, I want to apply animation on UIView on the current screen.

Solution:
func animatePage(side: PageCurl, textView: UITextView) {
UIView.animate(withDuration: 0.3, animations: {
let animation = CATransition()
animation.duration = 0.3
animation.startProgress = 0.0
animation.endProgress = 1
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.type = CATransitionType(rawValue: "pageCurl")
if side == .left {
animation.subtype = CATransitionSubtype(rawValue: "fromLeft")
} else {
animation.subtype = CATransitionSubtype(rawValue: "fromRight")
}
animation.isRemovedOnCompletion = false
animation.isRemovedOnCompletion = false
textView.layer.add(animation, forKey: "pageFlipAnimation")
})
}
Use:
animatePage(side: .left, textView: myTextView)

Just one note to the accepted answer. If you want to use it like book flip animation, change your imageView.image from cover to page, for example, before this line animatePage(side: .left, textView: myTextView)
So if you have cover image and page image, your code should be like:
imageView.image = UIImage(named: "page.png")
animatePage(side: .right, view: imageView)
And it will give your a nice looking flip animation from cover to page

Related

Animate shadow properties

I want to animate shadow properties for a view. Initially I tried:
func animateIn(delay: TimeInterval = 0) {
UIView.animate(withDuration: 0.2, delay: delay, options: .allowUserInteraction, animations: {
self.shadowView.layer.shadowColor = UIColor.black.cgColor
self.shadowView.layer.shadowOffset = CGSize(width: 15, height: 15)
self.shadowView.layer.shadowRadius = 20
self.shadowView.layer.shadowOpacity = 0.2
self.layoutIfNeeded()
}, completion: nil)
}
which doesnt work given that UIView.animate doesnt work with layers.
I tested the following post (How to animate layer shadowOpacity?), but it only specifies how to animate the shadow Opacity. Is there any way to animate all my shadow properties like I would with an animation blocl?
You are very close, you just create multiple CABasicAnimations for all the property changes and then you can use CAAnimationGroup
So let say you have 2 CABasicAnimation animations like:
let shadownOpacityAnimation = CABasicAnimation(keyPath: "shadowOpacity")
...
let shadowRadiusAnimation = CABasicAnimation(keyPath: "shadowRadius")
...
Then you can add them using CAAnimationGroup
let group = CAAnimationGroup()
group.duration = 0.2
group.repeatCount = 1
group.autoreverses = false
group.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
group.animations = [shadownOpacityAnimation, shadowRadiusAnimation]
layer.add(group, forKey: "allAnimations")
You can read more about it in this answer:
How can I create an CABasicAnimation for multiple properties?

How do i keep on rotating an image until a response from a server in swift

I am making an async call whenever a button is clicked now I want an image which is like a refresh icon to be rotating or spinning until I get a response from my server. What i have now only rotates pi that's 180 and when the response arrives too the image doesn't reset. Have pasted my sample code below:
//When the update button is clicked
#objc func updateHome(){
UIView.animate(withDuration: 1) {
self.updateIcon.transform = CGAffineTransform(rotationAngle: .pi)
}
getBalances()
}
//Inside getBalances function
DispatchQueue.main.async {
if responseCode == 0 {
let today = self.getTodayString()
print("Time: \(today)")
UIView.animate(withDuration: 1) {
self.updateIcon.transform = CGAffineTransform(rotationAngle: .pi)
}
}
This code rotates your UIImageView indefinitely using CABasicAnimation:
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(.pi * 2.0)
rotateAnimation.duration = 1.0 // Change this to change how many seconds a rotation takes
rotateAnimation.repeatCount = Float.greatestFiniteMagnitude
updateIcon.layer.add(rotateAnimation, forKey: "rotate")
And when you get your signal from your server you can call
updateIcon.layer.removeAnimation(forKey: "rotate")
to stop the animation
For continuous rotation, you can use CAKeyframeAnimation too like below.
#objc func updateHome() {
let animation = CAKeyframeAnimation(keyPath: "transform.rotation.z")
animation.duration = 1.0
animation.fillMode = kCAFillModeForwards
animation.repeatCount = .infinity
animation.values = [0, Double.pi/2, Double.pi, Double.pi*3/2, Double.pi*2]
let moments = [NSNumber(value: 0.0), NSNumber(value: 0.1),
NSNumber(value: 0.3), NSNumber(value: 0.8), NSNumber(value: 1.0)]
animation.keyTimes = moments
self.updateIcon.layer.add(animation, forKey: "rotate")
getBalances()
}
In your Async method (inside)DispatchQueue.main.async, you can stop like below. This will keep you to the original position of the image.
self.updateIcon.layer.removeAllAnimations()
One way you can handle this is to add an instance variable that tracks when the work is completed:
var finished: Bool = false
Then write a function that rotates the image and uses the completion handler to call itself to continue rotating:
func rotateImage() {
UIView.animate(withDuration: 1,
delay: 0.0,
options: .curveLinear
animations: {
self.updateIcon.transform = CGAffineTransform(rotationAngle: .pi)
},
completion: { completed in
if !self.finished {
self.rotateImage()
}
}
}
}
If you want to stop the animation instantly, you should set finished = true and call updateIcon.layer.removeAllAnimations()

Fading in/out a UILabel

My stackview contains 2 labels. I want to animate the hiding process like giving it a fade out animation when one of the labels is hidden or a fade in animation when one of the labels is unhidden. How would I do this?
You should animate the alpha of the label.
UIView.animate(withDuration: 1, animations: {
disclaimerLabel.alpha = 0
}
let flash = CABasicAnimation(keyPath: "opacity")
flash.duration = 0.5
flash.fromValue = 1
flash.toValue = 0.1
flash.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
flash.autoreverses = true
flash.repeatCount = 3
la1.layer.add(flash, forKey: nil)

How can I animate the frame and alpha of a UIButton's titleLabel simultaneously?

I would like animate the width and alpha of my UIButton's titleLabel. The alpha animation should start after 1 second.
I'm trying to do it with Core Animation:
let animSize = CAKeyframeAnimation(keyPath: "bounds")
animSize.values = [btn1.layer.frame,CGRect(origin: btn1.layer.frame.origin, size: CGSize(width: 300, height: btn1.layer.frame.height))]
animSize.duration = 3
animSize.isRemovedOnCompletion = false
animSize.fillMode = kCAFillModeForwards
btn1.layer.add(animSize, forKey: "bounds")
let animAlpha = CABasicAnimation(keyPath: "opacity")
animAlpha.fromValue = 0.0
animAlpha.toValue = 1.0
animAlpha.timeOffset = 1.0
animAlpha.isRemovedOnCompletion = false
animAlpha.fillMode = kCAFillModeForwards
btn1.titleLabel?.layer.add(animAlpha, forKey: "opacity")
When I run the animations separately, everything's working, but when I run them together, only the first animation ("bounds") is evaluating. I know I can do it with UIView.animate but in that case I will lose the "delay" option. I've also tried to put those animations in a CAAnimationGroup but nothing changed. I want to run those animations simultaneously therefore I can't use a completion block.
Thank you for any advice.
You won't lose the "delay" option with UIView.animate. You can use two animation blocks to achieve the same result:
UIView.animate(withDuration: 3) {
btn1.frame = CGRect(origin: btn1.frame.origin, size: CGSize(width: 300, height: btn1.frame.height))
}
UIView.animate(withDuration: 2, delay: 1, options: [], animations: {
btn1.alpha = 1
}, completion: nil)
You may also try it with autolayout by hooking the button's width constraint as IBOutlet
btn1.withCon.constant = 300
UIView.animate(withDuration: 3) {
self.view.layoutIfNeeded()
}
UIView.animate(withDuration: 2, delay: 1, options: [], animations: {
btn1.alpha = 1
}, completion: nil)

UIView.animateWithDuration: animation always stops at original position

I added an ImageView in the middle of my view (no constraints set).
With this code I want to move this ImageView from its original position to a new one.
Unfortunately, it moves from the new to its original position - why?
let opts = UIViewAnimationOptions.CurveEaseInOut
UIView.animateWithDuration(1, delay: 0, options: opts, animations: {
self.ivSun.center.x += 100
}, completion: nil)
Add this after completion of your animation.
self.ivSun.layer.position = self.ivSun.layer.presentationLayer().position
I have code that implemented in my game.
CATransaction.begin()
CATransaction.setCompletionBlock { () -> Void in
self.viewBall.layer.position = self.viewBall.layer.presentationLayer().position
}
}
var animation = CABasicAnimation(keyPath: "position")
animation.duration = ballMoveTime
animation.fromValue = NSValue(CGPoint: viewBall.layer.presentationLayer().position)
animation.toValue = NSValue(CGPoint: CGPointMake(self.borderRight, self.borderMiddle))
animation.removedOnCompletion = false
animation.fillMode = kCAFillModeForwards
viewBall.layer.addAnimation(animation, forKey: "transform")
CATransaction.commit()
Change position value as you need.
Try this one.
func animateImage()
{
UIView.animateWithDuration(3.0, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
self.imageView.center = CGPointMake(self.imageView.center.x+10, self.imageView.center.y+10)
}, completion: nil)
}
Without knowing more about the context and related code it's difficult to give an answer on "why" it's snapping back to its original position, but as an alternative you could set the completion handler with whatever you'd like as the final position/coordinate.
Generally I use constraints on my widgets/views, and I animate their values (versus animating the widgets' frame, for example) ... with excellent results.
This is due to autolayout. Constraints need to be changed and updated in the the animation as seen below:
- (IBAction)moveAction:(id)sender {
self.spaceConstraint.constant += 220;
[UIView animateWithDuration:0.5 animations:^{
[self.imageView layoutIfNeeded];
}];
}
var animation = CABasicAnimation(keyPath: "position")
animation.duration = ballMoveTime
animation.fromValue = NSValue(CGPoint: viewBall.layer.presentationLayer().position)
animation.toValue = NSValue(CGPoint: CGPointMake(self.borderRight, self.borderMiddle))
animation.removedOnCompletion = false
animation.fillMode = kCAFillModeForwards
viewBall.layer.addAnimation(animation, forKey: "transform")
CATransaction.commit()

Resources