具有透明内部矩形的 UIBezierPath 阴影 Objective c
UIBezierPath Shadow with transparent internal rect Objective c
我在使用 UIbezierPath 添加阴影时遇到问题。我的密码是
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);
我正在尝试使用此代码添加红色阴影。问题是我正在设置视图的透明背景色。因此,添加的红色阴影层在背景上变得可见,而不是父背景颜色。喜欢下图
但我希望它是这样的
如有解决方法请指导
您可以通过
- 添加一个
CAShapeLayer
作为子层
- 给它一个rounded-rect路径
- 给路径一个白色填充颜色
- 然后使用“从中心切出矩形的蒙版”
这是一个快速示例视图子类和演示它的控制器:
自定义UIView
子类
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
}
}
示例视图控制器
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),
])
}
}
外观:
编辑 - 我应该已经注意到 Objective-C 实施的需要(这些天请求越来越少)。
所以,这里和上面一样,但是在Obj-C(默认headers):
自定义UIView
子类
#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
示例视图控制器
#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
谢谢@DonMag!我从他的 class ShadowPathView 那里得到帮助,按如下方式编辑我的 Objective C 函数
- (void)setOnView:(UIView *)view {
CGFloat radius = 10;
CAShapeLayer *maskLayer = [CAShapeLayer layer];
CAShapeLayer *shadowLayer = [CAShapeLayer layer];
[view.layer addSublayer:shadowLayer];
shadowLayer.shadowColor = [UIColor redColor].CGColor;
shadowLayer.shadowOpacity = 1;
shadowLayer.shadowOffset = CGSizeMake(0, 0);
shadowLayer.mask = maskLayer;
shadowLayer.frame = view.bounds;
shadowLayer.path = [UIBezierPath bezierPathWithRoundedRect:view.bounds cornerRadius:radius].CGPath;
UIBezierPath *bez = [UIBezierPath bezierPathWithRect:CGRectInset(view.bounds, -radius, -radius)];
UIBezierPath *holePath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(view.bounds, 0, 0) cornerRadius:radius];
[bez appendPath:holePath];
bez.usesEvenOddFillRule = YES;
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.path = bez.CGPath;
CGFloat shadowWidth = 5;
CGRect shadowRect = CGRectInset(view.bounds, -shadowWidth, -shadowWidth);
shadowLayer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:shadowRect cornerRadius:radius].CGPath;
}
我在使用 UIbezierPath 添加阴影时遇到问题。我的密码是
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);
我正在尝试使用此代码添加红色阴影。问题是我正在设置视图的透明背景色。因此,添加的红色阴影层在背景上变得可见,而不是父背景颜色。喜欢下图
但我希望它是这样的
如有解决方法请指导
您可以通过
- 添加一个
CAShapeLayer
作为子层 - 给它一个rounded-rect路径
- 给路径一个白色填充颜色
- 然后使用“从中心切出矩形的蒙版”
这是一个快速示例视图子类和演示它的控制器:
自定义UIView
子类
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
}
}
示例视图控制器
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),
])
}
}
外观:
编辑 - 我应该已经注意到 Objective-C 实施的需要(这些天请求越来越少)。
所以,这里和上面一样,但是在Obj-C(默认headers):
自定义UIView
子类
#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
示例视图控制器
#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
谢谢@DonMag!我从他的 class ShadowPathView 那里得到帮助,按如下方式编辑我的 Objective C 函数
- (void)setOnView:(UIView *)view {
CGFloat radius = 10;
CAShapeLayer *maskLayer = [CAShapeLayer layer];
CAShapeLayer *shadowLayer = [CAShapeLayer layer];
[view.layer addSublayer:shadowLayer];
shadowLayer.shadowColor = [UIColor redColor].CGColor;
shadowLayer.shadowOpacity = 1;
shadowLayer.shadowOffset = CGSizeMake(0, 0);
shadowLayer.mask = maskLayer;
shadowLayer.frame = view.bounds;
shadowLayer.path = [UIBezierPath bezierPathWithRoundedRect:view.bounds cornerRadius:radius].CGPath;
UIBezierPath *bez = [UIBezierPath bezierPathWithRect:CGRectInset(view.bounds, -radius, -radius)];
UIBezierPath *holePath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(view.bounds, 0, 0) cornerRadius:radius];
[bez appendPath:holePath];
bez.usesEvenOddFillRule = YES;
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.path = bez.CGPath;
CGFloat shadowWidth = 5;
CGRect shadowRect = CGRectInset(view.bounds, -shadowWidth, -shadowWidth);
shadowLayer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:shadowRect cornerRadius:radius].CGPath;
}