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()
Related
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?
I created a function with Core Animation that animate the layer height from 0 to N and it's delayable.
extension CALayer {
func animate(to height: CGFloat, duration: Double, delay: Double) {
let animation: CABasicAnimation = .init(keyPath: "bounds.size.height")
animation.fromValue = 0
animation.toValue = height
animation.beginTime = CACurrentMediaTime() + delay
animation.duration: duration
animation.timingFunction = .init(name: kCAMediaTimingFunctionEaseInEaseOut)
// I want to improvement this part.
//
// self.isHidden = true
//
// DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
// self.isHidden = false
// }
self.bounds.size.height = height
self.add(animation, forKey: "bounds.size.height")
}
}
And the layer does transform well during animation, but it returns to original height before begin time and after finish. So I had to set isHidden of layer according to time to delay.
But I don't think it's a safe way. So I want to improvement that code.
What do you usually do in this case?
Try setting animation.fillMode = .backwards. I think that will make the animation will apply its fromValue to the layer until the animation's beginTime is reached.
I was trying to do a "flip x animation" for a UIButton. I have seen some documentation on how to do this animation but i couldn't find how to do it with repetition. For example, i want to flip the image of the UIButton and change it after a time interval of 5 sec with repetition.
Any help? Thanks.
You can use CAAnimationGroup to group an array of animations. Total animation for whole animationGroup is 6.0, flip animation takes place for 1.0 second. so remaining 5.0 seconds act as delay here.
EDIT: CATransaction.setCompletionBlock can be used but it will only call completion after all animations are completed. So now the flip will occur only for one time and on completion you will receive completion call, and update image view and initiate flip animation again.
Like this
imageView.flip {
// update imageView and call it again.
}
extension UIView {
func flip(completion: #escaping ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock {
// completion code here
completion()
}
let animationGroup = CAAnimationGroup()
animationGroup.duration = 6.0
animationGroup.repeatCount = 1
let easeInOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
let animation = CABasicAnimation(keyPath: "transform")
animation.duration = 1.0
animation.autoreverses = true
animation.fromValue = CATransform3DIdentity
animation.toValue = NSValue(caTransform3D: CATransform3DMakeRotation(CGFloat(M_PI), 0, 1, 0))
animation.timingFunction = easeInOut
animationGroup.animations = [animation]
layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
layer.add(animationGroup, forKey: "flip")
CATransaction.commit()
}
}
My ViewController has an UIImageView (lets name it img) as atribute, and in the beginning, it shows img in full screen, that is, its size is a rectangle similar to the ViewController bounds.
I'd like to do an animation with img, making it become a little circle (not just a rectangle with circular corners) and then move it to some corner of the ViewController.
How can I achieve this? I prefer not to send any code cos Im pretty sure I did it the wrong way (I tried to turn it into a square and then set a cornerRadius (with radius width/2), then making scale and translating position at once, but its a mess and works wrongly, so lets forget it)
Your solution sounds fine to me, but what might be going wrong, is the fact that animating corner radius is not supported by UIView.animateWithDuration as seen in the View Programming Guide for iOS. Therefor, the results are sometimes not whats expected.
You can try out the following code.
func animate() {
//Some properties that needs to be set on UIImageView
self.imageView.clipsToBounds = true
self.imageView.contentMode = .ScaleAspectFill
//Create animations
let cornerRadiusAnim = changeCornerRadiusAnimation()
let squareAnim = makeSquareAnimation()
let boundsAnim = changeBoundsAnimation()
let positionAnim = changePositionAnimation(CGPointMake(300,480))
//Use group for sequenced execution of animations
let animationGroup = CAAnimationGroup()
animationGroup.animations = [cornerRadiusAnim, squareAnim, boundsAnim, positionAnim]
animationGroup.fillMode = kCAFillModeForwards
animationGroup.removedOnCompletion = false
animationGroup.duration = positionAnim.beginTime + positionAnim.duration
imageView.layer.addAnimation(animationGroup, forKey: nil)
}
func makeSquareAnimation() -> CABasicAnimation {
let center = imageView.center
let animation = CABasicAnimation(keyPath:"bounds")
animation.duration = 0.1;
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = NSValue(CGRect: imageView.frame)
animation.toValue = NSValue(CGRect: CGRectMake(center.x,center.y,imageView.frame.width,imageView.frame.width))
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
return animation
}
func changeBoundsAnimation() -> CABasicAnimation {
let animation = CABasicAnimation(keyPath:"transform.scale")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = imageView.layer.mask?.valueForKeyPath("transform.scale")
animation.toValue = 0.1
animation.duration = 0.5
animation.beginTime = 0.1
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
return animation
}
func changeCornerRadiusAnimation() -> CABasicAnimation {
let animation = CABasicAnimation(keyPath:"cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = 0
animation.toValue = imageView.frame.size.width * 0.5
animation.duration = 0.1
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
return animation
}
func changePositionAnimation(newPosition: CGPoint) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = NSValue(CGPoint: imageView.layer.position)
animation.toValue = NSValue(CGPoint: newPosition)
animation.duration = 0.3
animation.beginTime = 0.6
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
return animation
}
// try like this, i hope it will work for you.
UIView.animateWithDuration(3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: { () -> Void in
img.layer.masksToBounds = true
img.layer.cornerRadius = img.frame.size.height / 2 // here img must be in square shape (height == width)
}) { (finished : Bool) -> Void in
}
option 1 call below method from code
// UIimageView Circle
class func roundImageView(imageView:UIImageView){
imageView.layer.borderWidth = 1.0
imageView.layer.masksToBounds = false
//imageView.layer.borderColor = UIColor.whiteColor().CGColor
imageView.layer.cornerRadius = imageView.frame.size.width/2
imageView.clipsToBounds = true
}
option 2 make user defined run time attributes from storyboard
view.layer.cornerRadius = 5.0f;
view.layer.masksToBounds = NO;
How do I make an object, for example an image, rotate forever?
UIView.animateWithDuration(2.0, animations: ({
self.Cube.transform = CGAffineTransformMakeRotation(320)
}))
Does anyone have a solution, would really appreciate some help.
Try this function to rotate a layer forever.
func rotateLayer(layer:CALayer,duration:CFTimeInterval){
let animation = CABasicAnimation(keyPath: "transform.rotation");
animation.fromValue = 0;
animation.duration = duration;
animation.toValue = CGFloat(M_PI*2);
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear);
animation.repeatCount = Float.infinity;
layer.addAnimation(animation, forKey: "rotation");
}
usage (the duration is time it takes the layer to rotate 360 degress):
rotateLayer(subView.layer, duration: 2);