无法添加圆角半径和阴影

Posted

技术标签:

【中文标题】无法添加圆角半径和阴影【英文标题】:Can't add a corner radius and a shadow 【发布时间】:2010-07-23 08:12:35 【问题描述】:

我正在尝试在图像上绘制阴影和圆角半径。我可以单独添加它们,但我无法同时添加两种效果。我正在添加阴影:

[layer setShadowOffset:CGSizeMake(0, 3)];
[layer setShadowOpacity:0.4];
[layer setShadowRadius:3.0f];
[layer setShouldRasterize:YES];

这里,layer 是 UIView 子类的 CALayer。所以每当我设置时这都有效

[layer setMasksToBounds:NO];

现在要添加圆角半径,我这样做:

[layer setCornerRadius:7.0f];

但我需要将 MasksToBounds 设置为 YES 才能正常工作:

[layer setMasksToBounds:YES];

我是否可以同时添加这两种效果?

感谢您的宝贵时间,

丹尼斯

【问题讨论】:

【参考方案1】:

是的,是的,有……

如果你想要一个圆角半径和一个阴影,你不要打开-masksToBounds,而是设置圆角半径并用圆角矩形设置阴影的贝塞尔路径。保持两者的半径相同:

[layer setShadowOffset:CGSizeMake(0, 3)];
[layer setShadowOpacity:0.4];
[layer setShadowRadius:3.0f];
[layer setShouldRasterize:YES];

[layer setCornerRadius:12.0f];
[layer setShadowPath:
                   [[UIBezierPath bezierPathWithRoundedRect:[self bounds]
                                               cornerRadius:12.0f] CGPath]];

在设置阴影路径后,您可能希望在不设置 -shouldRasterize 参数的情况下检查性能。设置阴影路径后,绘图性能往往会非常好。

更新

我已经有一段时间没有研究这个问题了,但您似乎不再需要设置 shadowPath 来使其正常工作。现在只需设置cornerRadiusshadowOpacity 即可。我认为自 ios5 以来就是这种情况(据我所知)。提供此更新可能是不必要的,因为将这些参数设置为“正常工作”,但我会为后代提供它。回顾一下,这就是您现在所需要的:

[layer setShadowOpacity:0.4];
[layer setCornerRadius:12.0f];

如果您仍然需要更好的性能,您可以继续设置shouldRasterize 参数:

[layer setShouldRasterize:YES];

说到性能,值得注意的是,如果您注意到动画缓慢,您毕竟会想要使用设置阴影路径的技术。这次更新真的只是为了指出不再需要设置路径来达到同时显示圆角半径和阴影的效果。但是,如果性能是您的首要任务,请使用路径。

更新 2

由于人们似乎在某些情况下无法使其正常工作,因此我将在此处从我创建的示例项目中发布更完整的代码 sn-p:

- (void)viewDidLoad

  [super viewDidLoad];

  CALayer *layer = [CALayer layer];
  [layer setBounds:CGRectMake(0.0f, 0.0f, 100.0f, 200.0f)];
  [layer setPosition:[[self view] center]];
  [layer setBackgroundColor:[[UIColor lightGrayColor] CGColor]];
  [layer setShadowOpacity:0.55f];
  [layer setCornerRadius:8.0f];
  [layer setBorderWidth:1.0f];

  [[[self view] layer] addSublayer:layer];

  [[[self testView] layer] setShadowOpacity:0.55f];
  [[[self testView] layer] setShadowRadius:15.0f];
  [[[self testView] layer] setCornerRadius:8.0f];
  [[[self testView] layer] setBorderWidth:1.0f];

testView 是我在 Interface Builder 中添加的 UIView,并设置了一个出口。这是为了确保它在您显式添加的两个图层以及子视图中的图层上都可以正常工作。

我已经在 iOS5 到 iOS6.1 的模拟器上对此进行了测试。它在每个人中都为我提供了这个结果:

【讨论】:

有什么理由不将 shouldRasterize 设置为 YES 吗? 这取决于您更新图层的频率。如果您打算设置一次并且不再更新它,如果您正在为图层设置动画,则将其栅格化将有助于提高性能。但是,如果图层的内容经常更新,则每次内容更改时都会将该内容呈现为图像,这实际上可能会损害您的滚动性能(假设您正在滚动/动画)。 UIBezierPath 和 bezierPathWithRoundedRect 之间需要一个空格。 谢谢阿里。我在答案中修复了它。 @PaulBatum 我使用的另一个技巧是使用多个图层。在您的图像层上使用masksToBounds:YES,然后在提供阴影的图像层后面创建一个层。【参考方案2】:

因为我使用带有背景图像的UIButton,所以这些答案都不适合我。我的按钮上要么没有阴影,要么没有圆边。

在我的场景中,最简单的方法是在按钮后面添加另一个视图并像这样添加阴影:

button.clipsToBounds=YES;
button.layer.cornerRadius = 25;

UIView *shadowView = [[UIView alloc]initWithFrame:button.frame];

shadowView.backgroundColor = [UIColor whiteColor];//needs this to cast shadow
shadowView.layer.cornerRadius = 25;
shadowView.clipsToBounds = YES;
shadowView.layer.masksToBounds = NO;
shadowView.layer.shadowOffset = CGSizeMake(0, 2);
shadowView.layer.shadowRadius = 1;
shadowView.layer.shadowOpacity = 0.2;


[[button superview]addSubview:shadowView];
[[button superview]bringSubviewToFront:button];

【讨论】:

【参考方案3】:

以下 Swift 3 代码展示了如何使用 CAShapeLayerCALayer 在图像上绘制阴影和圆角半径。

import UIKit

class ViewController: UIViewController 

    override func viewDidLoad() 
        super.viewDidLoad()

        // constants
        let radius: CGFloat = 20, offset = 8
        let rect = CGRect(x: 0, y: 0, width: 200, height: 200)

        // roundedView
        let roundedView = UIView()
        view.addSubview(roundedView)

        // shadow layer
        let shadowLayer = CALayer()
        shadowLayer.shadowColor = UIColor.darkGray.cgColor
        shadowLayer.shadowPath = UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath
        shadowLayer.shadowOffset = CGSize(width: offset, height: offset)
        shadowLayer.shadowOpacity = 0.8
        shadowLayer.shadowRadius = 2
        roundedView.layer.addSublayer(shadowLayer)

        // mask layer
        let maskLayer = CAShapeLayer()
        maskLayer.path = UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath

        // image layer
        let imageLayer = CALayer()
        imageLayer.mask = maskLayer
        imageLayer.frame = rect
        imageLayer.contentsGravity = kCAGravityResizeAspectFill
        imageLayer.contents = UIImage(named: "image")?.cgImage
        roundedView.layer.addSublayer(imageLayer)

        // auto layout
        roundedView.translatesAutoresizingMaskIntoConstraints = false
        roundedView.widthAnchor.constraint(equalToConstant: rect.width).isActive = true
        roundedView.heightAnchor.constraint(equalToConstant: rect.height).isActive = true
        roundedView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        roundedView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    



此代码生成以下显示:


之前的代码可以重构为如下的swift文件:

CustomView.swift

import UIKit

class CustomView: UIView 

    var imageLayer: CALayer!
    var image: UIImage? 
        didSet  refreshImage() 
    

    override var intrinsicContentSize:


        CGSize 
        return CGSize(width: 200, height: 200)
    

    func refreshImage() 
        if let imageLayer = imageLayer, let image = image 
            imageLayer.contents = image.cgImage
        
    

    override func layoutSubviews() 
        super.layoutSubviews()

        if imageLayer == nil 
            let radius: CGFloat = 20, offset: CGFloat = 8

            let shadowLayer = CALayer()
            shadowLayer.shadowColor = UIColor.darkGray.cgColor
            shadowLayer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath
            shadowLayer.shadowOffset = CGSize(width: offset, height: offset)
            shadowLayer.shadowOpacity = 0.8
            shadowLayer.shadowRadius = 2
            layer.addSublayer(shadowLayer)

            let maskLayer = CAShapeLayer()
            maskLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath

            imageLayer = CALayer()
            imageLayer.mask = maskLayer
            imageLayer.frame = bounds
            imageLayer.backgroundColor = UIColor.red.cgColor
            imageLayer.contentsGravity = kCAGravityResizeAspectFill
            layer.addSublayer(imageLayer)
        


        refreshImage()
    


ViewController.swift

import UIKit

class ViewController: UIViewController 

    override func viewDidLoad() 
        super.viewDidLoad()

        let roundedView = CustomView()
        roundedView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(roundedView)

        // auto layout
        let horizontalConstraint = roundedView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        let verticalConstraint = roundedView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])

        roundedView.image = UIImage(named: "image")
    



您可以在 Github repo 上找到更多将带有圆角和阴影的图像组合在一起的方法。

【讨论】:

【参考方案4】:
view.layer.cornerRadius=4;
[view.layer setMasksToBounds:YES];
[view.layer setShadowColor:SHADOW_COLOR];
[view.layer setShadowOpacity:4 ];
[view.layer setShadowRadius:4];
[view.layer setShadowOffset:0];    
[view.layer setShadowPath: [[UIBezierPath bezierPathWithRoundedRect:[view bounds] cornerRadius:CORNER_RADIUS] CGPath]];
view.layer.borderColor=[[UIColor lightGrayColor] colorWithAlphaComponent:.5].CGColor;
view.layer.borderWidth=.4;

【讨论】:

以上是关于无法添加圆角半径和阴影的主要内容,如果未能解决你的问题,请参考以下文章

图层和阴影上的圆角半径

UILabel,带有圆角半径和阴影

增加 UITextField 的圆角半径会移除它的阴影

如果设置角半径,则不应用阴影

具有角半径和阴影的 SWIFT UITableViewCell

UIVIew 角半径和阴影?