【问题标题】: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];

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

感谢您的宝贵时间,

丹尼斯

【问题讨论】:

    标签: iphone objective-c core-animation core-graphics quartz-graphics


    【解决方案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;
        

        【讨论】:

          猜你喜欢
          • 2014-08-29
          • 2018-05-26
          • 1970-01-01
          • 1970-01-01
          • 2012-05-19
          • 1970-01-01
          • 1970-01-01
          • 2021-06-16
          • 1970-01-01
          相关资源
          最近更新 更多