如何为UIView的左上角和右上角设置cornerRadius?

548

有没有一种方法可以仅为UIView的左上角和右上角设置cornerRadius

我尝试了以下方法,但最终导致无法看到视图。

UIView *view = [[UIView alloc] initWithFrame:frame];

CALayer *layer = [CALayer layer];
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight) cornerRadii:CGSizeMake(3.0, 3.0)];
layer.shadowPath = shadowPath.CGPath;
view.layer.mask = layer;

9
在您的编辑后,有三个需要修正的地方:(1)圆角路径应基于 view.bounds,而不是 frame;(2)图层应该是一个 CAShapeLayer,而不是 CALayer;(3)设置图层的 path,而不是 shadowPath - Kurt Revis
1
可能是这个问题的重复:question & answer - Stuart
使用Bezier曲线算法,在CGPath上创建曲线。我相信它是CoreGraphics的一部分。如果不是,http://en.wikipedia.org/wiki/Bézier_curve有一些很好的定义和动画。 - Nate Symer
https://iosdevcenters.blogspot.com/2018/02/how-to-set-roundcorner-radius-on-view.html - Bhadresh
请查看我的答案:https://dev59.com/I10b5IYBdhLWcg3wGN0s#50396485 - wcarhart
显示剩余2条评论
31个回答

564

我不确定为什么您的解决方案没有起作用,但是以下代码对我有效。创建一个贝塞尔掩码并将其应用于您的视图。在下面的代码中,我将_backgroundView的底部圆角半径设置为3像素。self 是一个自定义的 UITableViewCell

UIBezierPath *maskPath = [UIBezierPath
    bezierPathWithRoundedRect:self.backgroundImageView.bounds
    byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight)
    cornerRadii:CGSizeMake(20, 20)
];

CAShapeLayer *maskLayer = [CAShapeLayer layer];

maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;

self.backgroundImageView.layer.mask = maskLayer;

一些改进的 Swift 2.0 版本:

let path = UIBezierPath(roundedRect:viewToRound.bounds, byRoundingCorners:[.TopRight, .BottomLeft], cornerRadii: CGSizeMake(20, 20))
let maskLayer = CAShapeLayer()

maskLayer.path = path.CGPath
viewToRound.layer.mask = maskLayer

Swift 3.0 版本:

let path = UIBezierPath(roundedRect:viewToRound.bounds,
                        byRoundingCorners:[.topRight, .bottomLeft],
                        cornerRadii: CGSize(width: 20, height:  20))

let maskLayer = CAShapeLayer()

maskLayer.path = path.cgPath
viewToRound.layer.mask = maskLayer

这里有一个关于Swift扩展的链接


2
在文本框中尝试。成功在左边,失败在右边。 - Rainer Liao
13
@RainerLiao 我也遇到了同样的问题,我把所有代码都放在了 viewDidLoad 方法里,但当我将其移动到 viewDidAppear 时,问题得以解决。 - Lucho
1
Swift 3 版本中有一个小语法错误:path.CGPath 应该是 path.cgPath - Rob
1
不适用于底部。即bottomLeft和bottomRight。 - user1673099
不要忘记添加 viewToRound.layoutIfNeeded()。谢谢。 - Nazmul Hasan
显示剩余7条评论

445

最后… 在iOS11中有CACornerMask! 使用CACornerMask非常容易实现:

let view = UIView()
view.clipsToBounds = true
view.layer.cornerRadius = 10
view.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner] // Top right corner, Top left corner respectively

6
这肯定是最好的方案。在动态调整表格视图单元格边缘时,我们总是遇到其他解决方案的问题。 - cornr
针对旧版本,应该怎么做呢? self.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner,.layerMaxXMinYCorner] - SHEBIN
1
其他答案在旋转设备时无法工作,而这段代码可以实现旋转并允许您圆角某些角落,应该接受这个答案。 - Cloud9999Strife
4
这应该是最佳答案。它是对原帖问题最直接和相关的回答。 - Richard Poutier
2
让我们把它置于首位。 - user5306470
显示剩余3条评论

371

请注意,如果您的UIView子类附有布局约束,则必须按如下方式刷新:

override func layoutSubviews() {
    super.layoutSubviews()
    roundCorners(corners: [.topLeft, .topRight], radius: 3.0)
}

如果您不这样做,它就不会出现。


而且要使用扩展来实现圆角:

extension UIView {
   func roundCorners(corners: UIRectCorner, radius: CGFloat) {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }
}


额外的视图控制器案例: 无论您是不想子类化视图还是不能子类化视图,您仍然可以使视图变圆。通过覆盖viewWillLayoutSubviews()函数来完成它,如下:

class MyVC: UIViewController {
    /// The view to round the top-left and top-right hand corners
    let theView: UIView = {
        let v = UIView(frame: CGRect(x: 10, y: 10, width: 200, height: 200))
        v.backgroundColor = .red
        return v
    }()
    
    override func loadView() {
        super.loadView()
        view.addSubview(theView)
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        // Call the roundCorners() func right there.
        theView.roundCorners(corners: [.topLeft, .topRight], radius: 30)
    }
}

视图出现后,您必须执行此操作。 - Jonathan.
2
如果有人遇到和我一样的虚拟问题 - 如果你在ViewController中编写,请不要忘记override func viewDidLayoutSubviews() { roundCorners() } - iago849
1
UIBezierPath在固定大小的视图中工作正常,但是当视图大小通过自动布局约束更改时会出现问题。 - Hardik Darji
它为我节省了时间(快速地将固定视图进行四舍五入),也花费了我时间(它意外地破坏了布局约束)。在某些情况下,这种方法会在对圆角视图进行动画处理时出现故障(例如使用基于弹簧阻尼的动画效果)。 - Samuël
非常尊敬,节省了我的时间,我缺少viewWillLayoutSubviews方法,谢谢。 - singh.jitendra
显示剩余4条评论

286

这里是@JohnnyRockex答案的Swift版本。

extension UIView {

    func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
         let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
         let mask = CAShapeLayer()
         mask.path = path.cgPath
         self.layer.mask = mask
    }

}

view.roundCorners([.topLeft, .bottomRight], radius: 10)

提示

如果您正在使用自动布局,则需要子类化您的UIView并在视图的layoutSubviews方法中调用roundCorners以获得最佳效果。

class View: UIView {
    override func layoutSubviews() {
        super.layoutSubviews()

        self.roundCorners([.topLeft, .bottomLeft], radius: 10)
    }
}

15
很棒的扩展程序。然而:".TopRight"和".BottomRight"在我的电脑上好像无法使用。 - Onichan
4
在Swift 2中,view.roundCorners([.TopLeft , .BottomLeft], radius: 10)表示将视图view的左上和左下角设置为圆角,圆角半径为10。 - fahrulazmi
2
它在右边不起作用,因为您的视图边界还没有使用自动布局定义好...我目前也面临着同样的问题。 - dosdos
3
子类化该视图,在layoutSubviews方法中对自身进行圆角处理。 - Arbitur
16
如果您使用自动布局,并且没有对要圆角的视图进行引用,您可以调用.layoutIfNeeded()方法,然后再调用此方法。 - zgjie
显示剩余13条评论

102

24
请注意,如果您在超过几个视图上执行此操作,性能将受到影响... - jjxtra
@jjxtra 那么在不影响性能的情况下,最好的方法是什么?我想在UITableViewCell中显示它。 - xi.lin
据我记得,设置 layer.shouldRasterize == YES 可以提高大约5倍的速度。但是文档说,它只适用于非透明单元格。试一试吧。 - codrut

68

这里有一个简短的实现方法:

- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *openInMaps = [UIButton new];
    [openInMaps setFrame:CGRectMake(15, 135, 114, 70)];
    openInMaps = (UIButton *)[self roundCornersOnView:openInMaps onTopLeft:NO topRight:NO bottomLeft:YES bottomRight:NO radius:5.0];
}

- (UIView *)roundCornersOnView:(UIView *)view onTopLeft:(BOOL)tl topRight:(BOOL)tr bottomLeft:(BOOL)bl bottomRight:(BOOL)br radius:(float)radius {

    if (tl || tr || bl || br) {
        UIRectCorner corner = 0;
        if (tl) {corner = corner | UIRectCornerTopLeft;}
        if (tr) {corner = corner | UIRectCornerTopRight;}
        if (bl) {corner = corner | UIRectCornerBottomLeft;}
        if (br) {corner = corner | UIRectCornerBottomRight;}

        UIView *roundedView = view;
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:roundedView.bounds byRoundingCorners:corner cornerRadii:CGSizeMake(radius, radius)];
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.frame = roundedView.bounds;
        maskLayer.path = maskPath.CGPath;
        roundedView.layer.mask = maskLayer;
        return roundedView;
    }
    return view;
}

设置颜色怎么样?maskLayer.borderWidth = 10; maskLayer.borderColor = [UIColor redColor].CGColor; 对我来说不起作用,先生。 - kiran
1
@kiran 掩码本身没有颜色,如果您想要边框,可以添加第二个CAShapeLayer。 - Johnny Rockex

57

它也可以在Swift 5.x上工作,按照我的完整答案操作。

在Swift 4.1和Xcode 9.4.1中:

在中,这一行就足够了:

detailsSubView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]//Set your view here

查看完整代码:

//In viewDidLoad
if #available(iOS 11.0, *) {
        detailsSubView.clipsToBounds = false
        detailsSubView.layer.cornerRadius = 10
        detailsSubView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
      //For lower versions
}

但对于较低版本

let rectShape = CAShapeLayer()
    rectShape.bounds = detailsSubView.frame
    rectShape.position = detailsSubView.center
    rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds,    byRoundingCorners: [.topLeft , .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
    detailsSubView.layer.mask = rectShape

完整的代码如下。

if #available(iOS 11.0, *) {
    detailsSubView.clipsToBounds = false
    detailsSubView.layer.cornerRadius = 10
    detailsSubView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
    let rectShape = CAShapeLayer()
    rectShape.bounds = detailsSubView.frame
    rectShape.position = detailsSubView.center
    rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds,    byRoundingCorners: [.topLeft , .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
    detailsSubView.layer.mask = rectShape
}

如果您正在使用Storyboard中的AutoResizing,请在 viewDidLayoutSubviews() 中编写以下代码。
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if #available(iOS 11.0, *) {
        detailsSubView.clipsToBounds = false
        detailsSubView.layer.cornerRadius = 10
        detailsSubView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
    } else {
        let rectShape = CAShapeLayer()
        rectShape.bounds = detailsSubView.frame
        rectShape.position = detailsSubView.center
        rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds,    byRoundingCorners: [.topLeft , .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
        detailsSubView.layer.mask = rectShape
    }
}

有没有iOS 9的解决方案? - Jaydeep Patel
@jay 让rectShape = CAShapeLayer() rectShape.bounds = detailsSubView.frame rectShape.position = detailsSubView.center rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds, byRoundingCorners: [.topLeft , .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath detailsSubView.layer.mask = rectShape 这段代码适用于iOS 9。 - Naresh
无法在iOS 9上工作。角落部分没有边框。 - Jaydeep Patel
@jay 我没有iOS 9操作系统的手机,但我在iOS 9模拟器中进行了检查。它可以工作...你是如何检查的?你能建议我如何在iOS 9上检查吗? - Naresh
我在模拟器中进行了检查...这是输出结果 https://docs.google.com/document/d/1wXu1Ooetx1QTsX7cwCmQDs0nPvsfA1MrYsSINUi64Hk/edit?usp=sharing - Jaydeep Patel
detailsSubView.clipsToBounds = false 应该改为 true,对吗? - byJeevan

40

有一种非常简单的方法来完成它。我在这里找到了它。

view.clipsToBounds = true
view.layer.cornerRadius = 24
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]

它使用视图的CALayer上的库存cornerRadius属性。您只需要定义角落。layerMinXMinYCorner是左上角,layerMaxXMinYCorner是右上角。


我喜欢这个答案,简短而且精练。 - Manish Singh

36
这是最简单的答案:
yourView.layer.cornerRadius = 8
yourView.layer.masksToBounds = true
yourView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]

1
标签,它是适用于iOS 11或更高版本的解决方案 - Ankur Lahiry

30

用一行代码轻松实现Swift 4和Swift 5

用法:

//MARK:- Corner Radius of only two side of UIViews
self.roundCorners(view: yourview, corners: [.bottomLeft, .topRight], radius: 12.0)

功能:

//MARK:- Corner Radius of only two side of UIViews
func roundCorners(view :UIView, corners: UIRectCorner, radius: CGFloat){
        let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        view.layer.mask = mask
}

在Objective-C中

用法:

[self.verticalSeparatorView roundCorners:UIRectCornerTopLeft | UIRectCornerTopRight radius:10.0];

该函数用于一个类别中(仅应用于一个角):

-(void)roundCorners: (UIRectCorner) corners radius:(CGFloat)radius {
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
        CAShapeLayer *mask = [[CAShapeLayer alloc] init];
        mask.path = path.CGPath;
        self.layer.mask = mask;
    }

3
请记住,直到 viewDidLayoutSubviews 完成后,您的视图角落才会被计算,因此您应该在其中调用 roundCorners 函数。 - Jirson Tavera
先添加圆角,然后再添加阴影。 - Shakeel Ahmed
1
@JirsonTavera 谢谢你的提示,老兄。这个问题让我疯了,差点花了一个小时才找到你在这里的评论! - Arash Afsharpour
这个解决方案导致应用于图层的阴影不会出现。 - beowulf

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