I have a problem by adding shadow using UIbezierPath. My Code is
CGRect f = view.bounds;
view.layer.shadowColor = [UIColor redColor].CGColor;
view.layer.shadowOpacity = 1;
view.layer.shadowRadius = 10;
CGFloat shadowWidth = 5;
CGRect shadowRect = CGRectMake(-shadowWidth, -shadowWidth, f.size.width+(shadowWidth*2), f.size.height+(shadowWidth*2));
CGFloat shadowRadius = radius;
view.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:shadowRect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(shadowRadius, shadowRadius)].CGPath;
view.layer.shadowOffset = CGSizeMake(0, 0);
I am trying to add red shadow using this code. The problem is I am setting transparent background color of my view. Due to this the added red shadow layer becomes visible on background, rather the parent background color. like following image
But I want it to be like this
If there is any solution for the problem please guide.
You can do this by
CAShapeLayer
as a sublayerHere's a quick example view subclass and a controller demonstrating it:
Custom UIView
subclass
class ShadowPathView: UIView {
let radius: CGFloat = 10
let shadowLayer = CAShapeLayer()
let maskLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// these properties don't change
backgroundColor = .clear
layer.addSublayer(shadowLayer)
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = UIColor.red.cgColor
shadowLayer.shadowOpacity = 1.0
shadowLayer.shadowOffset = .zero
// set the layer mask
shadowLayer.mask = maskLayer
}
override func layoutSubviews() {
super.layoutSubviews()
shadowLayer.frame = bounds
shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath
// create a rect bezier path, large enough to exceed the shadow bounds
let bez = UIBezierPath(rect: bounds.insetBy(dx: -radius * 2.0, dy: -radius * 2.0))
// create a path for the "hole" in the layer
let holePath = UIBezierPath(rect: bounds.insetBy(dx: radius, dy: radius))
// this "cuts a hole" in the path
bez.append(holePath)
bez.usesEvenOddFillRule = true
maskLayer.fillRule = .evenOdd
// set the path of the mask layer
maskLayer.path = bez.cgPath
let w: CGFloat = 5
// make the shadow rect larger than bounds
let shadowRect = bounds.insetBy(dx: -w, dy: -w)
// set the shadow path
// make the corner radius larger to make the curves look correct
shadowLayer.shadowPath = UIBezierPath(roundedRect: shadowRect, cornerRadius: radius + w).cgPath
}
}
Example view controller
class ShadowPathVC: UIViewController {
// two of our custom ShadowPathView
let v1 = ShadowPathView()
let v2 = ShadowPathView()
// a label to put UNDER the second view
let underLabel = UILabel()
// a label to add as a SUVBVIEW of the second view
let subLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 0.8, green: 0.92, blue: 0.97, alpha: 1.0)
[underLabel, subLabel].forEach { v in
v.textAlignment = .center
v.backgroundColor = .green
}
[v1, v2, underLabel, subLabel].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
}
[v1, underLabel, v2].forEach { v in
view.addSubview(v)
}
v2.addSubview(subLabel)
underLabel.text = "This label is Under the shadow view"
subLabel.text = "This label is a subview of the shadow view"
subLabel.numberOfLines = 0
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
v1.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
v1.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
v1.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
v1.heightAnchor.constraint(equalToConstant: 120.0),
v2.topAnchor.constraint(equalTo: v1.bottomAnchor, constant: 80.0),
v2.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
v2.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
v2.heightAnchor.constraint(equalToConstant: 160.0),
underLabel.leadingAnchor.constraint(equalTo: v2.leadingAnchor, constant: -20.0),
underLabel.topAnchor.constraint(equalTo: v2.topAnchor, constant: -20.0),
underLabel.heightAnchor.constraint(equalToConstant: 80.0),
subLabel.bottomAnchor.constraint(equalTo: v2.bottomAnchor, constant: -12.0),
subLabel.trailingAnchor.constraint(equalTo: v2.trailingAnchor, constant: -40.0),
subLabel.widthAnchor.constraint(equalToConstant: 120.0),
])
}
}
How it looks:
Edit - I should have caught the need for Objective-C implementation (fewer and fewer requests for that theses days).
So, here's the same as above, but in Obj-C (default headers):
Custom UIView
subclass
#import "ShadowPathView.h"
@interface ShadowPathView ()
{
CAShapeLayer *shadowLayer;
CAShapeLayer *maskLayer;
CGFloat radius;
}
@end
@implementation ShadowPathView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (void) commonInit {
radius = 10;
shadowLayer = [CAShapeLayer new];
maskLayer = [CAShapeLayer new];
self.backgroundColor = [UIColor clearColor];
[self.layer addSublayer:shadowLayer];
shadowLayer.fillColor = [UIColor whiteColor].CGColor;
shadowLayer.shadowColor = [UIColor redColor].CGColor;
shadowLayer.shadowOpacity = 1.0;
shadowLayer.shadowOffset = CGSizeZero;
// set the layer mask
shadowLayer.mask = maskLayer;
}
- (void)layoutSubviews {
[super layoutSubviews];
shadowLayer.frame = self.bounds;
shadowLayer.path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:radius].CGPath;
// create a rect bezier path, large enough to exceed the shadow bounds
UIBezierPath *bez = [UIBezierPath bezierPathWithRect:CGRectInset(self.bounds, -radius, -radius)];
// create a path for the "hole" in the layer
UIBezierPath *holePath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, 0, 0) cornerRadius:radius];
// this "cuts a hole" in the path
[bez appendPath:holePath];
bez.usesEvenOddFillRule = YES;
maskLayer.fillRule = kCAFillRuleEvenOdd;
// set the path of the mask layer
maskLayer.path = bez.CGPath;
CGFloat shadowWidth = 5;
// make the shadow rect larger than bounds
CGRect shadowRect = CGRectInset(self.bounds, -shadowWidth, -shadowWidth);
// set the shadow path
// make the corner radius larger to make the curves look correct
shadowLayer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:shadowRect cornerRadius:radius + shadowWidth].CGPath;
}
@end
Example view controller
#import "ViewController.h"
#import "ShadowPathView.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithRed:0.8 green:0.92 blue:0.97 alpha:1.0];
// two of our custom ShadowPathView
ShadowPathView *v1 = [ShadowPathView new];
ShadowPathView *v2 = [ShadowPathView new];
// a label to put UNDER the second view
UILabel *underLabel = [UILabel new];
// a label to add as a SUVBVIEW of the second view
UILabel *subLabel = [UILabel new];
for (UILabel *v in @[underLabel, subLabel]) {
v.textAlignment = NSTextAlignmentCenter;
v.backgroundColor = [UIColor greenColor];
}
for (UIView *v in @[v1, v2, underLabel, subLabel]) {
v.translatesAutoresizingMaskIntoConstraints = NO;
}
for (UIView *v in @[v1, underLabel, v2]) {
[self.view addSubview:v];
}
[v2 addSubview:subLabel];
underLabel.text = @"This label is Under the shadow view";
subLabel.text = @"This label is a subview of the shadow view";
subLabel.numberOfLines = 0;
UILayoutGuide *g = self.view.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
[v1.topAnchor constraintEqualToAnchor:g.topAnchor constant:40.0],
[v1.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:40.0],
[v1.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:-40.0],
[v1.heightAnchor constraintEqualToConstant:120.0],
[v2.topAnchor constraintEqualToAnchor:v1.bottomAnchor constant:80.0],
[v2.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:40.0],
[v2.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:-40.0],
[v2.heightAnchor constraintEqualToConstant:160.0],
[underLabel.leadingAnchor constraintEqualToAnchor:v2.leadingAnchor constant:-20.0],
[underLabel.topAnchor constraintEqualToAnchor:v2.topAnchor constant:-20.0],
[underLabel.heightAnchor constraintEqualToConstant:80.0],
[subLabel.bottomAnchor constraintEqualToAnchor:v2.bottomAnchor constant:-12.0],
[subLabel.trailingAnchor constraintEqualToAnchor:v2.trailingAnchor constant:-40.0],
[subLabel.widthAnchor constraintEqualToConstant:120.0],
]];
}
@end