Uiview Animatewithduration Doesn't Animate Cornerradius Variation

UIView animateWithDuration doesn't animate cornerRadius variation

tl;dr: Corner radius is not animatable in animateWithDuration:animations:.



What the documentation says about view animations.

As the section on Animations in the "View Programming Guide for iOS" says

Both UIKit and Core Animation provide support for animations, but the level of support provided by each technology varies. In UIKit, animations are performed using UIView objects

The full list of properties that you can animate using either the older

[UIView beginAnimations:context:];
[UIView setAnimationDuration:];
// Change properties here...
[UIView commitAnimations];

or the newer

[UIView animateWithDuration:animations:];

(that you are using) are:

  • frame
  • bounds
  • center
  • transform (CGAffineTransform, not the CATransform3D)
  • alpha
  • backgroundColor
  • contentStretch

As you can see, cornerRadius is not in the list.

Some confusion

UIView animations is really only meant for animating view properties. What confuses people is that you can also animate the same properties on the layer inside the UIView animation block, i.e. the frame, bounds, position, opacity, backgroundColor. So people see layer animations inside animateWithDuration and believe that they can animate any view property in there.

The same section goes on to say:

In places where you want to perform more sophisticated animations, or animations not supported by the UIView class, you can use Core Animation and the view’s underlying layer to create the animation. Because view and layer objects are intricately linked together, changes to a view’s layer affect the view itself.

A few lines down you can read the list of Core Animation animatable properties where you see this one:

  • The layer’s border (including whether the layer’s corners are rounded)

So to animate the cornerRadius you need to use Core Animation as you've already said in your updated question (and answer). I just added tried to explain why its so.



Some extra clarification

When people read the documentations that says that animateWithDuration is the recommended way of animating it is easy to believe that it is trying to replace CABasicAnimation, CAAnimationGroup, CAKeyframeAnimation, etc. but its really not. Its replacing the beginAnimations:context: and commitAnimations that you seen above.

Change the frame of an UIView and update cornerRadius

func changeBounds()
{
let animation = CABasicAnimation()
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = NSValue(CGRect: self.theView.frame)
animation.toValue = NSValue(CGRect: CGRectInset(self.theView.frame, 40, 40))
animation.duration = 1.0
self.theView.layer.addAnimation(animation, forKey: "bounds")

CATransaction.begin()
CATransaction.setDisableActions(true)
self.theView.layer.frame = CGRectInset(self.theView.frame, 40, 40)
CATransaction.commit()
}

func changeCornerRadius()
{
let animation = CABasicAnimation(keyPath:"cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = 0
animation.toValue = self.theView.frame.size.width/2
animation.duration = 1.0
self.theView.layer.addAnimation(animation, forKey: "cornerRadius")
self.theView.layer.cornerRadius = self.theView.frame.size.width/2
}

This seems to work for me, just call like this.

self.changeBounds()
self.changeCornerRadius()

Set the animation key to "bounds" instead of "frame". Or you can add these two animation to an animation group.

Animate UIView and keep corner radius as a circle

If you only want to scale your view size, then you can use below code.

[UIView animateWithDuration:2.0 animations:^{

con.transform = CGAffineTransformMakeScale(2, 2); // it will scale as double size

}completion:^(BOOL finished) {
// do whatever at animation completion
}
}];

To reset view back to normal size,

con.transform = CGAffineTransformIdentity;

Reference: https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGAffineTransform/index.html#//apple_ref/c/func/CGAffineTransformMakeScale

UIView animation doesn't animate on the first try

It is typically bad practice to change frames when using auto layout. From what it seems, instead of setting the frame to be wherever the animation stops, you could just set the objects to not reset back to its original position when it completes using this:

anim.fillMode = kCAFillModeForwards
anim.removedOnCompletion = false

but be sure to place those lines BEFORE you add the animation

buttons[index].layer.addAnimation(anim, forKey: "animate position along path"+String(index))

To avoid setting the frames again when you want to move your buttons back, you should be doing basically the same animation but instead of

clockwise: false

set it to true

clockwise: true

then from there you can use your end angles as the new start angles for the movement back, and set your end angles for the movement back to wherever you need the buttons to be

Changing cornerRadius using Core Animation

You're setting the corner radius on the layer property of the animation object; this animation object doesn't have a layer property.

You need to set the corner radius on the layer of the thing you're animating, in this case OpenNoteVisible. You also need to ensure the toValue of the animation object matches the value you're setting on the layer, otherwise you'll get odd animations.

Your code should now be:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.fromValue = [NSNumber numberWithFloat:10.0f];
animation.toValue = [NSNumber numberWithFloat:140.0f];
animation.duration = 1.0;
[OpenNoteVisible.layer setCornerRadius:140.0];
[OpenNoteVisible.layer addAnimation:animation forKey:@"cornerRadius"];

Animate UIView transform happens instantly instead of over duration value

You need to call your animation block from another lifecycle method. You can't trigger this from viewDidLoad as the view has only loaded, there is nothing on screen yet.

Try using viewDidAppear

UIView Animation Removes layer.cornerRadius After Completion

I suspect you're subclassing UILabel here since it looks like you have padding in there, is that correct?

There could be something going awry with any custom drawing/calculations you're doing in there, so it would probably be helpful to post that code for inspection as well.

A few questions:

  • Do you have masksToBounds set to YES?
  • If you're not using a custom UILabel subclass, are you wrapping the label in a view?
  • How is the animation being triggered? Is it by a button? A callback from a NSURLRequest? If it's triggered by an async callback are you jumping back on the main queue to perform the animation?
  • If the animation is triggered automatically within the lifecycle, which lifecycle method is it triggered in?

I wasn't able to reproduce the issue in a test project with a vanilla UILabel. I then tried it with a UILabel subclass which includes additional padding and still wasn't able to reproduce it there.

I've included example code snippets below:

#import "ViewController.h"
#import "R4NInsetLabel.h"

@interface ViewController ()
@property BOOL showingToast;
@property (strong, nullable) IBOutlet R4NInsetLabel *toastLabel;
@property (strong, nullable) IBOutlet NSLayoutConstraint *toastLabelTopConstraint;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor whiteColor]};
self.showingToast = NO;
// start with the label pushed off the top of the screen
self.toastLabelTopConstraint.constant = -40.0f;
self.toastLabel.layer.cornerRadius = 6.0f;
self.toastLabel.layer.masksToBounds = YES;
}

- (IBAction)toggleToast:(id)sender {
[UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:0.4 options:UIViewAnimationOptionCurveEaseInOut animations:^{
if (self.showingToast == NO) {
self.toastLabelTopConstraint.constant = 16;
self.showingToast = YES;
} else {
self.toastLabelTopConstraint.constant = -40;
self.showingToast = NO;
}
[self.view layoutIfNeeded];
} completion:nil];
}

@end
#import "R4NInsetLabel.h"

IB_DESIGNABLE
@interface R4NInsetLabel()
@property IBInspectable CGFloat contentPadding;
@property (nonatomic) UIEdgeInsets contentInsets;
- (CGSize)_addInsetsToSize:(CGSize)size;
@end

@implementation R4NInsetLabel

- (UIEdgeInsets)contentInsets {
return UIEdgeInsetsMake(self.contentPadding, self.contentPadding, self.contentPadding, self.contentPadding);
}

- (CGSize)_addInsetsToSize:(CGSize)size {
CGFloat width = size.width + self.contentInsets.left + self.contentInsets.right;
CGFloat height = size.height + self.contentInsets.top + self.contentInsets.bottom;
return CGSizeMake(width, height);
}

- (void)drawTextInRect:(CGRect)rect {
CGRect insetRect = UIEdgeInsetsInsetRect(rect, self.contentInsets);
[super drawTextInRect:insetRect];
}

- (CGSize)intrinsicContentSize {
CGSize baseSize = [super intrinsicContentSize];
return [self _addInsetsToSize:baseSize];
}

- (CGSize)sizeThatFits:(CGSize)size {
CGSize baseSize = [super sizeThatFits:size];
return [self _addInsetsToSize:baseSize];
}

@end

And here's what it looks like:

Toast_Animation_With_Corner_Radius



Related Topics



Leave a reply



Submit