【问题标题】:Animate CALayer border change动画 CALayer 边框变化
【发布时间】:2014-03-22 14:06:17
【问题描述】:

我以这种方式将 border 宽度和颜色设置为 UIView 子类:

- (void) setViewBorder
{
    self.layer.borderColor = [UIColor greenColor].CGColor;
    self.layer.borderWidth = 3.0f;
}

我的问题是:如何制作动画?谢谢。

【问题讨论】:

  • 您要制作什么动画?边框外观?还是轮换?
  • @ValentinShamardin 边框外观。和换帧动画一样的效果。

标签: ios objective-c calayer


【解决方案1】:

如果你只想淡入边框,你也可以这样做:

UIView.transition(with: self, duration: 0.3, options: .transitionCrossDissolve, animations: {

        self.layer.borderColor = UIColor.green.cgColor
        self.layer.borderWidth = 3

}, completion: nil)

【讨论】:

  • 在 UIViewPropertAnimator 中也不适用于我。
  • @JaapWeijland 你需要使用UIView.transition 而不是UIViewPropertyAnimator
  • 如果我没记错的话,相同的代码应该可以在两种方法中使用
  • @JaapWeijland 已经有一段时间了,但实际上我认为我选择了UIView.transition,因为它不适用于UIView.animate/UIViewPropertyAnimator。也许试一试;)
  • 这在包含 UIPickerViews 的视图上使用时会产生一些跳跃/丑陋
【解决方案2】:

这是@David 答案的快速解决方案:

let colorAnimation = CABasicAnimation(keyPath: "borderColor")
colorAnimation.fromValue = UIColor.red.cgColor
colorAnimation.toValue = UIColor.blue.cgColor
view.layer.borderColor = UIColor.blue.cgColor

let widthAnimation = CABasicAnimation(keyPath: "borderWidth")
widthAnimation.fromValue = 2
widthAnimation.toValue = 4
widthAnimation.duration = 4
view.layer.borderWidth = 4

let bothAnimations = CAAnimationGroup()
bothAnimations.duration = 0.5
bothAnimations.animations = [colorAnimation, widthAnimation]
bothAnimations.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

view.layer.add(bothAnimations, forKey: "color and width")

【讨论】:

  • 你的意思是 self.view.layer.borderColor 而不是 'backgroundColor'?
  • 您应该使用#keyPath(CALayer.borderColor) 代替"borderColor"。与宽度相同...
  • @DeanKelly 为什么 CALayer.borderColor 优于“borderColor”?
  • @Eric 不是 100% 我记得推理。我认为更多的是使用#keyPath 部分,它是编译安全的,因为如果属性名称发生变化,它将无法编译。
【解决方案3】:

borderColorborderWidth 都是动画属性,但由于您是在 UIView 动画块之外的视图子类中执行此操作,因此会禁用隐式动画(当您更改值时自动发生的动画)。

如果您想为这些属性设置动画,则可以使用CABasicAnimation 进行显式动画。由于您在同一层上为两个属性设置动画,因此您可以将它们都添加到动画组中,并且只配置一次持续时间、时间等。请注意,显式动画是纯视觉的,添加它们时模型值(实际属性)不会改变。这就是为什么您既要配置动画又要设置模型值的原因。

CABasicAnimation *color = [CABasicAnimation animationWithKeyPath:@"borderColor"];
// animate from red to blue border ...
color.fromValue = (id)[UIColor redColor].CGColor;
color.toValue   = (id)[UIColor blueColor].CGColor;
// ... and change the model value
self.layer.borderColor = [UIColor blueColor].CGColor;

CABasicAnimation *width = [CABasicAnimation animationWithKeyPath:@"borderWidth"];
// animate from 2pt to 4pt wide border ...
width.fromValue = @2;
width.toValue   = @4;
// ... and change the model value
self.layer.borderWidth = 4;

CAAnimationGroup *both = [CAAnimationGroup animation];
// animate both as a group with the duration of 0.5 seconds
both.duration   = 0.5;
both.animations = @[color, width];
// optionally add other configuration (that applies to both animations)
both.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

[self.layer addAnimation:both forKey:@"color and width"];

如果您查看“设置插值值”部分下的the documentation for CABasicAnimation,您会发现不需要像我一样同时指定 toValue 和 fromValue,因此代码可以稍微短一些。但是,为了清晰和可读性(尤其是当您开始使用 Core Animation 时),更加明确可以帮助您(和您的同事)理解代码。

【讨论】:

  • “因为您是在 UIView 动画块之外的视图子类中执行此操作,所以隐式动画(那些在您更改值时自动发生的动画)被禁用。”这在任何地方都有记录吗?我发现自己经常禁用图层操作,对此我一无所知。
  • @jrturton 视图是图层的代表,当您在动画块之外时,为actionForLayer:forKey: 返回NSNull。整个过程在 CALayer 上对actionForKey: 的讨论中有所记录。我会看看能否找到更好的委托参考资料。
  • @jrturton 在 UIView 上 layer 属性的文档中,您可以读到视图是委托,您不应该更改委托。
  • @jrturton 至于视图在actionForLayer:forKey: 中的实际作用,我能找到的最好的my answer here 又引用this answer。但是,您可以进行自己的实验,并在动画块内部和外部向图层委托(或直接视图)询问actionForLayer:forKey:,然后查看您得到的响应。不过,我不记得在某处看到过它的正式记录。
  • @SwiftArchitect 是的,不知何故,很长一段时间都没有被注意到
【解决方案4】:

您可以创建关键帧动画并将其添加到您的视图中。

//Create animation
        CAKeyframeAnimation *colorsAnimation = [CAKeyframeAnimation animationWithKeyPath:@"borderColor"];
        colorsAnimation.values = [NSArray arrayWithObjects: (id)[UIColor greenColor].CGColor,
                                  (id)[UIColor yellowColor].CGColor, nil];
        colorsAnimation.keyTimes = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.25],[NSNumber numberWithFloat:1.0], nil];
        colorsAnimation.calculationMode = kCAAnimationLinear;
        colorsAnimation.removedOnCompletion = NO;
        colorsAnimation.fillMode = kCAFillModeForwards;
        colorsAnimation.duration = 3.0f;

    //Create animation
    CAKeyframeAnimation *sizeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"borderWidth"];
    sizeAnimation.values = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],
                             [NSNumber numberWithFloat:5.0], nil];
    sizeAnimation.keyTimes = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],[NSNumber numberWithFloat:3.0], nil];
    sizeAnimation.calculationMode = kCAAnimationLinear;
    sizeAnimation.removedOnCompletion = NO;
    sizeAnimation.fillMode = kCAFillModeForwards;
    sizeAnimation.duration = 3.0f;

    //Add animation
    [yoursubview.layer   addAnimation:colorsAnimation forKey:nil];
    [yoursubview.layer   addAnimation:sizeAnimation forKey:nil];

【讨论】:

  • 如果您要在答案中使用 removedOnCompletion = NO,那么您应该向 OP 解释后果。
  • 另外,为什么你使用只有两个值的关键帧动画。看代码一点都不明显,没必要复杂。
猜你喜欢
  • 2018-11-19
  • 2012-08-19
  • 2021-10-19
  • 1970-01-01
  • 2011-02-24
  • 2017-05-25
  • 1970-01-01
  • 2010-12-06
  • 2019-07-30
相关资源
最近更新 更多