I recently had to improve the performance of a few views that
utilized CALayer-based shadows on rounded-rect UIView objects. On this
particular iPad application, when the device was rotated, the views
rotated quite a lot slower than we would have hoped. It wasn’t a
show-stopper, but the jerky rotation animation made it look cheap and
unpolished. The easiest way to have our cake, and eat it too, was to set
a custom CGPath to the layer’s shadowPath property. This told UIKit to
set the inside of the path to opaque, reducing the amount of work the
rendering engine needed to perform.
The resulting image, as you can see above, has a shadow as you’d expect. But since we’ve declared the shape the path will have, the iPad can drastically improve its rendering performance.
Through that process however, I decided to see what sort of effects I could pull off by passing in a path other than the default rectangular bounds of the layer. Since you can create any sort of path you want, I considered the different effects I could get away with by making non-rectangular paths and using them as shadows.
By carefully drawing a trapezoidal shape below and slightly beneath the view, you can give the illusion of depth.
Just like the trapezoid, there are other effects you can achieve by playing with simple shapes for the use of creating shadows.
By using a control point on a bezier curve, you can make the bottom
side of the shadow curve inward, making it appear like the view is
printed on paper that has been curled inward.
If you want to play with the source use to create these examples, make sure you download the ShadowTest.zip
// Add background tile
UIImage *bgImage = [UIImage imageNamed:@"embedded_bg.png"];
self.view.backgroundColor = [UIColor colorWithPatternImage:bgImage];
// Add the reference view
UIImage *image = [UIImage imageNamed:@"dccp.jpeg"];
UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
[self.view addSubview:imgView];
imgView.center = self.view.center;
imgView.layer.shadowColor = [UIColor blackColor].CGColor;
imgView.layer.shadowOpacity = 0.7f;
imgView.layer.shadowOffset = CGSizeMake(10.0f, 10.0f);
imgView.layer.shadowRadius = 5.0f;
imgView.layer.masksToBounds = NO;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:imgView.bounds];
imgView.layer.shadowPath = path.CGPath;
[imgView release];
The resulting image, as you can see above, has a shadow as you’d expect. But since we’ve declared the shape the path will have, the iPad can drastically improve its rendering performance.
Through that process however, I decided to see what sort of effects I could pull off by passing in a path other than the default rectangular bounds of the layer. Since you can create any sort of path you want, I considered the different effects I could get away with by making non-rectangular paths and using them as shadows.
Trapezoidal CGPath
CGSize size = imgView.bounds.size;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(size.width * 0.33f, size.height * 0.66f)];
[path addLineToPoint:CGPointMake(size.width * 0.66f, size.height * 0.66f)];
[path addLineToPoint:CGPointMake(size.width * 1.15f, size.height * 1.15f)];
[path addLineToPoint:CGPointMake(size.width * -0.15f, size.height * 1.15f)];
Elliptical CGPath
CGRect ovalRect = CGRectMake(0.0f, size.height + 5, size.width - 10, 15);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:ovalRect];
Paper-curl effect
CGSize size = imgView.bounds.size;
CGFloat curlFactor = 15.0f;
CGFloat shadowDepth = 5.0f;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0.0f, 0.0f)];
[path addLineToPoint:CGPointMake(size.width, 0.0f)];
[path addLineToPoint:CGPointMake(size.width, size.height + shadowDepth)];
[path addCurveToPoint:CGPointMake(0.0f, size.height + shadowDepth)
controlPoint1:CGPointMake(size.width - curlFactor, size.height + shadowDepth - curlFactor)
controlPoint2:CGPointMake(curlFactor, size.height + shadowDepth - curlFactor)];
More possibilities than can be covered
There are plenty of other possibilities, more than can be covered here. Creating CGPathRef objects, either using UIBezierCurve or by using Quartz2D drawing methods, can easily step through composing shadows. Use a CGAffineTransform object to manipulate your path to stretch, scale, or rotate it as needed. Once you realize what your possibilities are, you can add an extra degree of polish to your application with very little effort.If you want to play with the source use to create these examples, make sure you download the ShadowTest.zip
No comments:
Post a Comment