UIView animateWithDuration无法动画化cornerRadius变化

68

我正在尝试动画化改变UIView实例layercornerRadius,但cornerRadius的变化立即发生。

以下是代码:

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 100, 100)];
view.layer.masksToBounds = YES;
view.layer.cornerRadius = 10.0;
[UIView animateWithDuration:1.0 animations:^{
    
    [view.layer.cornerRadius = 0.0;
    
}];

感谢所有将给我任何提示的人。

编辑:

我使用了 Core AnimationCABasicAnimation 实现了该属性的动画效果。

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

1
尝试使用以下代码,使你的动画停留在新数值: animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; - Mohammad Abdurraafay
@MohammadAbdurraafay 不要这样做。这是制作动画“粘滞”的错误方式。 - David Rönnqvist
@DavidRönnqvist 为什么不应该使用 animation.removedOnCompletion = NO;?你在这里或者你的回答中都没有给出任何替代建议。 - Leon Storey
@DavidRönnqvist 链接未附上。 - Leon Storey
@user1763532 让我们再试一次:这里是我的答案,解释了removeOnCompletion = NO存在的问题,并提供了一种替代方案。 - David Rönnqvist
显示剩余3条评论
8个回答

90

简而言之:animateWithDuration:animations:中,圆角半径不可动画化。


关于视图动画的文档说明

正如iOS“视图编程指南”中关于动画的章节所述:

UIKit和Core Animation都支持动画,但每种技术提供的支持级别不同。在UIKit中,使用UIView对象执行动画。

完整的属性列表可以使用旧版或新版的任何一种来进行动画处理。

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

或者更新的

[UIView animateWithDuration:animations:];

您正在使用的属性包括:
- frame - bounds - center - transform(CGAffineTransform,而不是CATransform3D) - alpha - backgroundColor - contentStretch
如您所见,cornerRadius不在列表中。
一些混淆:
UIView动画实际上只用于动画视图属性。令人困惑的是,您也可以在UIView动画块内对图层上的相同属性进行动画处理,例如frame、bounds、position、opacity和backgroundColor。因此,人们看到animateWithDuration内的图层动画,并认为他们可以在其中动画任何视图属性。
同一部分还说:
“在需要执行更复杂的动画或不受UIView类支持的动画的地方,可以使用核心动画和视图的底层图层来创建动画。因为视图和图层对象密切链接在一起,所以对视图的图层进行的更改会影响视图本身。”
往下几行,您可以阅读Core Animation可动画属性的列表,其中包括以下内容:
  • 图层的边框(包括图层的圆角)

因此,要动画显示 cornerRadius,您需要使用核心动画,正如您已经在更新的问题(和答案)中提到的那样。我只是试图解释为什么这样做。


一些额外的说明

当人们阅读文档并看到animateWithDuration被推荐用于动画时,很容易认为它试图替换CABasicAnimation、CAAnimationGroup、CAKeyframeAnimation等,但实际上并不是这样。它是在替换你之前看到的beginAnimations:context:commitAnimations


好的回答。感谢指出所有的文档资料。 - SpacyRicochet
完美的答案。帮了很多忙。非常感谢。 - Jassi
2
这在iOS 11中有所改变,请参考我的答案。 - edwardmp

25

我使用这个扩展来动画改变圆角半径:

extension UIView
{
    func animateCornerRadius(from: CGFloat, to: CGFloat, duration: CFTimeInterval)
    {
        CATransaction.begin()
        let animation = CABasicAnimation(keyPath: "cornerRadius")
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
        animation.fromValue = from
        animation.toValue = to
        animation.duration = duration
        CATransaction.setCompletionBlock { [weak self] in
            self?.layer.cornerRadius = to
        }
        layer.add(animation, forKey: "cornerRadius")
        CATransaction.commit()
    }
}

3
工作得非常好。您实际上不需要"from"字段,因为它应该始终是“layer.cornerRadius”。 - Wyetro
谢谢,它在我的UIView.animate块中很好地工作,即将UIView的移动与其cornerRadius的更改相结合。 - xaphod
1
从iOS 11开始,它可以进行动画处理。https://useyourloaf.com/blog/masked-and-animated-corners/ - aehlke

15

圆角半径的变化是可以通过动画来呈现的,但实现此功能唯一的方法是使用CABasicAnimation。希望这能帮助到某个需要的人。


14

iOS 10开始,实际上您可以动画化cornerRadius:

UIViewPropertyAnimator(duration: 3.0, curve: .easeIn) {
    square.layer.cornerRadius = 20
}.startAnimation()

2
它可在iOS 10+上使用。 - infiniteLoop

13

您可以按照这里的方法实现UIView动画:http://maniacdev.com/2013/02/ios-uiview-category-allowing-you-to-set-up-customizable-animation-properties

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
animation.duration = DURATION;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.toValue = @(NEW_CORNER_RADIUS);
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
[view.layer addAnimation:animation forKey:@"setCornerRadius:"];

2
自从iOS 11以来,cornerRadius成为了可动画化的属性。 - akaDuality

2

1
嗯...如果你使用旧的动画方法,它会起作用...但是如果你尝试使用苹果建议的方法,它就不会动画。 - Francesco Puglisi
1
@singingAtom 的 animateWithDuration: 和 CABasicAnimations 其实不是同一回事,因此不存在新旧方法的问题。animateWithDuration: 不是 Core Animation 的一部分,只能对视图属性进行动画操作(cornerRadius 是层属性)。真正的旧方法是 [UIView beginAnimations:context:] 和 [UIView commitAnimation],它们与新方法完全相同。Apple 建议使用 animateWithDuration: 对视图属性进行动画操作,并继续说您应该使用 Core Animation 进行更高级的动画操作。 - David Rönnqvist

1

您可以通过在视图类中实现-[actionForLayer:forKey]来使此属性在+[UIView animateWithDuration:]中可动画化。请参见this question,了解示例。


0

CornerRadius属性是可动画化的

以下是可用的工作代码

//图层初始化

let imageLayer = CALayer() 
imageLayer.frame = CGRect(x: 0, y: 0.0, width: tenPercent, height: tenPercent)
imageLayer.contents = imageNew.cgImage
imageLayer.masksToBounds = true

// 动画初始化

let animationCornerRadius = CABasicAnimation(keyPath: "cornerRadius")
animationCornerRadius.beginTime =  0.01
animationCornerRadius.duration = CFTimeInterval(5)
animationCornerRadius.fromValue = 1
animationCornerRadius.toValue = tenPercent / 2
animationCornerRadius.fillMode = .forwards
animationCornerRadius.isRemovedOnCompletion = false
imageLayer.add(animationCornerRadius , forKey: "cornerRadius")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接