如何使用CALayers创建带有圆角的UIView,只圆角化部分角落?

125

我的应用程序中有四个按钮,分别命名为:

  • 左上
  • 左下
  • 右上
  • 右下

在这些按钮的上方有一个图像视图(或UIView)。

现在,假设用户点击了“左上”按钮,则上方的图像/视图应该在该特定角落处变为圆角。

我在应用圆角到UIView上有些困难。

目前,我正在使用以下代码将圆角应用于每个视图:

    // imgVUserImg is a image view on IB.
    imgVUserImg.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"any Url Here"];
    CALayer *l = [imgVUserImg layer];
    [l setMasksToBounds:YES];
    [l setCornerRadius:5.0];  
    [l setBorderWidth:2.0];
    [l setBorderColor:[[UIColor darkGrayColor] CGColor]];

以上代码将圆角应用于提供的视图的每个角落。但我只想将圆角应用于所选的角落,例如 - 顶部/顶部+左侧/底部+右侧等。

这是否可行?怎么做?

14个回答

2
半个十年过去了,但我认为人们目前处理这个问题的方式并不完全正确。许多人使用UIBezierPath + CAShapeLayer方法时会遇到问题,特别是在故事板上设置时会干扰自动布局。没有答案涵盖此问题,因此我决定添加自己的解决方案。
有一种非常简单的方法可以绕过这个问题:在drawRect(rect: CGRect)函数中绘制圆角。
例如,如果我想要一个UIView的顶部圆角,我会子类化UIView,然后在适当的地方使用该子类。
import UIKit

class TopRoundedView: UIView {

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)

        var maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: UIRectCorner.TopLeft | UIRectCorner.TopRight, cornerRadii: CGSizeMake(5.0, 5.0))

        var maskLayer = CAShapeLayer()
        maskLayer.frame = self.bounds
        maskLayer.path = maskPath.CGPath

        self.layer.mask = maskLayer
    }
}

这是征服问题的最佳方法,而且不需要任何时间来适应。

1
这不是解决问题的正确方法。只有在视图中进行自定义绘制(例如使用核心图形)时,才应该覆盖 drawRect(_:) 方法,否则即使是空实现也会影响性能。每次创建新的遮罩层也是不必要的。你实际上只需要在视图边界发生变化时更新遮罩层的 path 属性,而这个操作应该在 layoutSubviews() 方法的重写中完成。 - Stuart

1
为了补充答案附加信息, 我用Swift创建了一个简单易用的UIView. 根据您的使用情况, 您可能需要进行修改(避免在每个布局上创建对象等), 但我想保持它尽可能简单。这个扩展可以让您更容易地将其应用于其他视图(例如UIImageView), 如果您不喜欢子类化的话。
extension UIView {

    func roundCorners(_ roundedCorners: UIRectCorner, toRadius radius: CGFloat) {
        roundCorners(roundedCorners, toRadii: CGSize(width: radius, height: radius))
    }

    func roundCorners(_ roundedCorners: UIRectCorner, toRadii cornerRadii: CGSize) {
        let maskBezierPath = UIBezierPath(
            roundedRect: bounds,
            byRoundingCorners: roundedCorners,
            cornerRadii: cornerRadii)
        let maskShapeLayer = CAShapeLayer()
        maskShapeLayer.frame = bounds
        maskShapeLayer.path = maskBezierPath.cgPath
        layer.mask = maskShapeLayer
    }
}

class RoundedCornerView: UIView {

    var roundedCorners: UIRectCorner = UIRectCorner.allCorners
    var roundedCornerRadii: CGSize = CGSize(width: 10.0, height: 10.0)

    override func layoutSubviews() {
        super.layoutSubviews()
        roundCorners(roundedCorners, toRadii: roundedCornerRadii)
    }
}

以下是如何将其应用于UIViewController

class MyViewController: UIViewController {

    private var _view: RoundedCornerView {
        return view as! RoundedCornerView
    }

    override func loadView() {
        view = RoundedCornerView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        _view.roundedCorners = [.topLeft, .topRight]
        _view.roundedCornerRadii = CGSize(width: 10.0, height: 10.0)
    }
}

0
总结一下斯图尔特的回答,你可以采用以下方法来实现圆角:
@implementation UIView (RoundCorners)

- (void)applyRoundCorners:(UIRectCorner)corners radius:(CGFloat)radius {
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];

    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.frame = self.bounds;
    maskLayer.path = maskPath.CGPath;

    self.layer.mask = maskLayer;
}

@end

所以要应用圆角,你只需要这样做:

[self.imageView applyRoundCorners:UIRectCornerTopRight|UIRectCornerTopLeft radius:10];

0
我建议定义一个图层的遮罩层。这个遮罩层本身应该是一个具有专用路径的CAShapeLayer对象。你可以使用下一个UIView扩展(Swift 4.2):
extension UIView {
    func round(corners: UIRectCorner, with radius: CGFloat) {
        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = UIBezierPath(
            roundedRect: bounds,
            byRoundingCorners: corners,
            cornerRadii: CGSize(width: radius, height: radius)
        ).cgPath
        layer.mask = maskLayer
   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接