How to animate layer shadowOpacity?

18,863

Solution 1

This will work properly:

#import <QuartzCore/CAAnimation.h>

CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
anim.fromValue = [NSNumber numberWithFloat:1.0];
anim.toValue = [NSNumber numberWithFloat:0.0];
anim.duration = 1.0;
[vv.layer addAnimation:anim forKey:@"shadowOpacity"];
vv.layer.shadowOpacity = 0.0;

For Swift 3.0:

 let animation = CABasicAnimation(keyPath: "shadowOpacity")
 animation.fromValue = layer.shadowOpacity
 animation.toValue = 0.0
 animation.duration = 1.0
 view.layer.add(animation, forKey: animation.keyPath)
 view.layer.shadowOpacity = 0.0

Solution 2

I've put above code in a little extension of UIView:

extension UIView {

func animateLayer<Value>(_ keyPath: WritableKeyPath<CALayer, Value>, to value:Value, duration: CFTimeInterval) {

    let keyString = NSExpression(forKeyPath: keyPath).keyPath
    let animation = CABasicAnimation(keyPath: keyString)
    animation.fromValue = self.layer[keyPath: keyPath]
    animation.toValue = value
    animation.duration = duration
    self.layer.add(animation, forKey: animation.keyPath)
    var thelayer = layer
    thelayer[keyPath: keyPath] = value
}
}

Usage like:

animateLayer(\.shadowOffset, to: CGSize(width: 3, height: 3), duration:1)
animateLayer(\.shadowOpacity, to: 0.4, duration: 1)

It's not thoroughly tested. but worked for me. (Also posted here)

Solution 3

The below code work for me

1)Add QuartzCore frame work 2)Import QuartzCore frame work

Add the following Code in the required place

UIImageView * glowimageview = [[[UIImageView alloc]init]autorelease];
    [glowimageview setFrame:CGRectMake(500,400,200,200)];
    [glowimageview setImage:[UIImage imageNamed:@"144.png"]];
    [sender addSubview:glowimageview];

    glowimageview.layer.shadowColor = [UIColor redColor].CGColor;
    glowimageview.layer.shadowRadius = 10.0f;
    glowimageview.layer.shadowOpacity = 1.0f;
    glowimageview.layer.shadowOffset = CGSizeZero;

    CABasicAnimation *shadowAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
    shadowAnimation.duration=1.0;
    shadowAnimation.repeatCount=HUGE_VALF;
    shadowAnimation.autoreverses=YES;
    shadowAnimation.fromValue = [NSNumber numberWithFloat:1.0];
    shadowAnimation.toValue = [NSNumber numberWithFloat:0.0];
    [glowimageview.layer addAnimation:shadowAnimation forKey:@"shadowOpacity"];

It will works. Change the format of the code as per your requirement

Solution 4

Here's a material design like take on some of the above with animations It's also available here as framework through Carthage https://github.com/sevenapps/SVNMaterialButton

public init(frame: CGRect, color: UIColor) {
    super.init(frame: frame)
    self.backgroundColor = color
    self.layer.masksToBounds = false
    self.layer.borderWidth = 1.0
    self.layer.shadowColor = UIColor.black.cgColor
    self.layer.shadowOpacity = 0.8
    self.layer.shadowRadius = 8
    self.layer.shadowOffset = CGSize(width: 8.0, height: 8.0)
}

required public init?(coder aDecoder: NSCoder) {
    fatalError("This class is not set up to be instaciated with coder use init(frame) instead")
}

public override func layoutSubviews() {
    super.layoutSubviews()
    self.layer.cornerRadius = self.frame.height / 4
}

public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    self.animate(to: 0.5, and: CGSize(width: 5.0, height: 5.0), with: 0.5)
    super.touchesBegan(touches, with: event)
}

public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    self.animate(to: 0.8, and: CGSize(width: 8.0, height: 8.0), with: 0.5)
    super.touchesBegan(touches, with: event)
}

private func animate(to opacity: Double, and offset: CGSize, with duration: Double){
    CATransaction.begin()
    let opacityAnimation = CABasicAnimation(keyPath: "shadowOpacity")
    opacityAnimation.toValue = opacity
    opacityAnimation.duration = duration
    opacityAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
    opacityAnimation.fillMode = kCAFillModeBoth
    opacityAnimation.isRemovedOnCompletion = false

    let offsetAnimation = CABasicAnimation(keyPath: "shadowOffset")
    offsetAnimation.toValue = offset
    offsetAnimation.duration = duration
    offsetAnimation.timingFunction = opacityAnimation.timingFunction
    offsetAnimation.fillMode = opacityAnimation.fillMode
    offsetAnimation.isRemovedOnCompletion = false

    self.layer.add(offsetAnimation, forKey: offsetAnimation.keyPath!)
    self.layer.add(opacityAnimation, forKey: opacityAnimation.keyPath!)
    CATransaction.commit()
}
Share:
18,863

Related videos on Youtube

sudo rm -rf
Author by

sudo rm -rf

Hi, I'm Jonathan. I'm an iOS &amp; Mac developer with a specialty in Core Animation. I created this Stack Overflow account back when I knew absolutely nothing about programming. Thus this profile serves as a documentation of my journey to where I am today. Glad I was able to help people out along the way. Keep up with me elsewhere: GitHub: https://github.com/jwilling Twitter. https://twitter.com/willing Blog: http://jwilling.com

Updated on December 09, 2020

Comments

  • sudo rm -rf
    sudo rm -rf over 3 years

    I have a view on which I've set the layerOpacity to 1.

        theView.layer.shadowOpacity = 1.0;
    

    This looks fine when the view is farther down the screen. When I move this view up to be flush with another view that has a shadow, they don't look good. Is there a way I can animate the shadowOpacity on my layer to be 0? I tried using an animation block but it seems as if this property is not animatable.

    alt text

    EDIT: Request for code that doesn't work:

    [UIView animateWithDuration:1.0 animations:^{
        splitView2.layer.shadowOpacity = 0;}
                     completion:NULL];
    
    • Costique
      Costique over 13 years
      From Apple's docs: "@property float shadowOpacity Specifies the opacity of the receiver’s shadow. Animatable." Can you post some code where that doesn't work?
    • Fattie
      Fattie over 9 years
      doesn't work in UIView animateWithDuration
    • sudo rm -rf
      sudo rm -rf over 9 years
      @JoeBlow: Correct, that's why I wrote that the code I posted doesn't work. You'll have to use Core Animation directly, since UIView doesn't provide an implicit animation for it.
    • Fattie
      Fattie over 9 years
      hi Sudo! To be clear, notice @Costique 's comment: I was replying to that comment. Cheers! Thanks for asking this handy question, which resulted in the good answer below.
  • Admin
    Admin over 13 years
    if you not set the vv.layer.shadowOpacity = 0.0; at the last line, the animation will be done but after that it will jump back to the old layer
  • Snowman
    Snowman over 9 years
    If you want to animate the shadow on rather than off, these two pieces are important: anim.removedOnCompletion = NO; anim.fillMode = kCAFillModeForwards;
  • BigSauce
    BigSauce about 9 years
    @moby Thanks for your comment. That is an essential point to getting this answer to work.
  • fatuhoku
    fatuhoku over 8 years
    Can the code snippet be updated with where anim.removeOnCompletion should go?
  • Scott Ahten
    Scott Ahten almost 7 years
    Important note: animations implemented via this method might not run at a reduced speed when "Debug > Slow Animations" is enabled in the iOS simulator. This one sent me off in the weeds for quite some time. Specifically, I thought I had set my animation up incorrectly because my custom view transition was running at reduced speed, but my CABasic animation ran at full speed.