10

I've got a problem with adding shadow to my UIView which is created in iOS 6 application with Autolayout.

Let's assume I have a method that adds a shadow on the bottom of UIView (this is actually a Category of UIView, so it's reusable):

- (void) addShadowOnBottom {
    self.layer.shadowOffset = CGSizeMake(0, 2);
    self.layer.shadowOpacity = 0.7;
    self.layer.shadowColor = [[UIColor blackColor] CGColor];
    self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
}

When I call this method in viewDidLoad of some UIViewController, shadow is not added, probably due to all constraints, that have to be calculated.

When I call this method in viewWillAppear the same situation.

When I call this method in viewDidAppear it works, but when new view shows up there is a short moment when there is no shadow and it appears after a while.

If I resign from setting the shadowPath and remove line self.layer.shadowPath everything works, but view transitions are not smooth.

So my question is what is the right way to add a shadow to view in iOS 6 with Autolayout turned on ?

Kamil
  • 248
  • 2
  • 8
  • I just calculate the CGRect myself: CGPathRef path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 320, 480)].CGPath; [cview.layer setShadowPath:path]; Works for me.. – Hans Dec 10 '13 at 22:44

3 Answers3

15

Another thing you can add to the layer when working with AutoLayout and you need a shadow on a UIView where the frame is not yet known is this :

self.layer.rasterizationScale = [[UIScreen mainScreen] scale]; // to define retina or not
self.layer.shouldRasterize = YES;

Then remove the shadowPath property because the auto layout constraints are not yet processed, so it's irrelevant. Also at the time of execution you will not know the bounds or the frame of the view.

This improves performance a lot!

Yannick
  • 1,076
  • 1
  • 11
  • 23
  • 2
    This should be marked as the correct answer. This just took my tableview scroll FPS from 45 to 57. Thanks – Esko918 Oct 03 '14 at 18:21
  • 1
    if don't use shadowpath , it will slow your animation. you can see my question here http://stackoverflow.com/questions/27172911/ios-assistivetouch-slow-animation-when-using-custom-keyboard – TomSawyer Dec 02 '14 at 09:12
  • How do you remove the shadowPath? nvm I removed the line, It thought you had to call a method to remove it – Benjamin Jimenez Aug 25 '15 at 19:19
2

i removed self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath; from your code and it is working for me in viewDidLoad, please confirm.

Increasing shdowOffset will make you see the shadow more clear.

Vaibhav Saran
  • 12,848
  • 3
  • 65
  • 75
  • 2
    If I remove `shadowPath` then animation is not smooth on my iPhone 4. In simulator it works fine, but on real device it is slow. How does it work for you? – Kamil Mar 21 '13 at 16:09
  • On my device this is the main issue :) – Kamil Mar 22 '13 at 10:58
  • @Kamil that's right, if remove shadowPath, animation will slow , i tested on iphone 6 and same trouble – TomSawyer Dec 02 '14 at 09:15
1

Having the exact same issue...

Although I am unable to get the CALayer shadow on a view to animate nicely, at least the shadow does re-align properly after animation.

My solution (which works fine in my application) is the set the shadowOpacity to 0, then reset it to the desired value AFTER the animation has completed. From a user's perspective, you cannot even tell the shadow is gone because the animations are typically too fast to perceive the difference.

Here is an example of some code in my application, in which I am changing the constant value of a constraint, which is 'trailing edge to superview' NSLayoutContraint:

- (void) expandRightEdge
{
    [self.mainNavRightEdge setConstant:newEdgeConstant];
    [self updateCenterContainerShadow];
    [UIView animateWithDuration:ANIMATION_DURATION delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            [[NSNotificationCenter defaultCenter] postNotificationName:@"PanelLayoutChanged" object:nil];
            [self.view layoutIfNeeded];
                    } completion:^(BOOL finished) {
                    nil;
                    }];
}

- (void) updateCenterContainerShadow
{
    self.centerContainer.layer.masksToBounds = NO;
    self.centerContainer.layer.shadowOpacity = 0.8f;
    self.centerContainer.layer.shadowRadius = 5.0f;
    self.centerContainer.layer.shadowOffset = CGSizeMake(0, 0);
    self.centerContainer.layer.shadowColor = [UIColor blackColor].CGColor;
    CGPathRef shadowPath = [UIBezierPath bezierPathWithRect:self.centerContainer.layer.bounds].CGPath;
    [self.centerContainer.layer setShadowPath:shadowPath];

    // Schedule a time to fade the shadow back in until we can figure out the CALayer + Auto-Layout issue
    [self performSelector:@selector(fadeInShadow) withObject:nil afterDelay:ANIMATION_DURATION+.05];
}

- (void) fadeInShadow
{
    [self.centerContainer.layer setShadowOpacity:0.8f];
}

Two things:

  • I could have put the fadeInShadow in the completion block, but due to the way some of my other code is factored, this works better for me.
  • I realize I am not performing a fade in with "fadeInShadow", but given how quickly it renderes after the completion of the animation, I found it is not necessary.

Hope that helps!

mattv123
  • 979
  • 1
  • 9
  • 13
  • You can use CABasicAnimation to animate the shadow too: http://stackoverflow.com/questions/11515647/objective-c-cabasicanimation-applying-changes-after-animation – Nailer Oct 16 '13 at 08:57