ChatGPT解决这个技术问题 Extra ChatGPT

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

有没有办法只为 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;
编辑后,需要修复三件事:(1) 圆角路径应基于 view.bounds,而不是 frame,(2) 图层应为 CAShapeLayer,而不是 CALayer; (3) 设置图层的path,而不是shadowPath
question & 的可能重复项answer
使用 Bezier 曲线算法,在 CGPath 上创建曲线。我很确定它是 CoreGraphics 的一部分。如果没有,en.wikipedia.org/wiki/Bézier_curve 有一些很棒的定义和动画。
在此处查看我的答案:stackoverflow.com/a/50396485/6246128

l
lewis

我不确定为什么您的解决方案不起作用,但以下代码对我有用。创建一个贝塞尔蒙版并将其应用于您的视图。在下面的代码中,我将 _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

斯威夫特 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 扩展 here


在文本字段中尝试。左成功右失败
@RainerLiao 我有同样的问题,我在 viewDidLoad 方法上做了所有这些,将它移到 viewDidAppear 并且它有效。
我如何将其设置为适用于任何设备。这仅适用于故事板的 simulator 大小。 ex: if the simulator size is 6s it works fine for 6s, but not of others. 我该如何克服这个问题。
Swift 3 版本中的小语法错误:path.CGPath 应该是 path.cgPath
不适用于底部。即bottomLeft & bottomRight
A
Almas Adilbek

最后……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

这绝对是最好的。在动态调整表格视图单元格的边缘时,我们总是遇到其他解决方案的问题。
旧版本会做什么? self.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner,.layerMaxXMinYCorner]
旋转设备时其他答案不起作用,此代码适用于旋转并允许您绕过某些角落,这应该是公认的答案。
这应该是最佳答案。这是对OP问题的最直接和最相关的答案。
让我们把它放在首位
S
Stéphane de Luca

请注意,如果您附加了布局约束,则必须在 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)
    }
}

答案最近已修复.. @kelin
这绝对是拐角处。
您必须在视图出现后执行此操作
如果有人遇到与我相同的问题 - 如果您在 ViewController 中编写,请不要忘记 override func viewDidLayoutSubviews() { roundCorners() }
UIBezierPath 与固定尺寸视图一起使用,但是当视图尺寸因自动布局约束而改变时会产生问题
H
Hashem Aboonajmi

这是@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)

笔记

如果您使用 Auto Layout,则需要子类化您的 UIView 并在视图的 layoutSubviews 中调用 roundCorners 以获得最佳效果。

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

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

很棒的扩展。但是:.TopRight.BottomRight 似乎对我不起作用。
Swift 2 上:view.roundCorners([.TopLeft , .BottomLeft], radius: 10)
它不适用于右侧,因为您的视图边界尚未通过自动布局很好地定义......我目前面临同样的问题。
@dosdos 子类该视图和 layoutSubviews 围绕自我的角落。
如果你使用自动布局,并且没有想要圆整的视图的引用,你可以调用.layoutIfNeeded(),然后这个方法。
C
Community

此处的 Swift 代码示例:https://stackoverflow.com/a/35621736/308315

不是直接的。你不得不:

创建一个 CAShapeLayer 根据 view.bounds 将其路径设置为 CGPathRef,但只有两个圆角(可能使用 +[UIBezierPath bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:]) 将 view.layer.mask 设置为 CAShapeLayer


请注意,如果您在多个视图上执行此操作,则会损害性能...
@jjxtra那么在没有太多性能损失的情况下最好的方法是什么?我想在 UITableViewCell 中显示它。
@xi.lin 我记得设置 layer.shouldRasterize == YES 将速度提高了 5 倍左右。但是文档说它只有在你使用不透明的单元格时才有效。试一试。
J
Johnny Rockex

这是一个像这样实现的简短方法:

- (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 面具没有颜色,如果你想有一个边框,你可以添加第二个 CAShapeLayer
T
Tung Fam

在 Swift 4.1 和 Xcode 9.4.1

在 iOS 11 中,这一行就足够了:

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
}

如果您在情节提要中使用 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有什么解决方案吗?
@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
不适用于 ios 9。角落部分没有边框。
@jay 我没有 iOS 9 操作系统手机,但我签入了 iOS 9 模拟器。它正在工作...您检查的方式和位置。你能建议我检查 iOS 9
我签入了模拟器..这里是 docs.google.com/document/d/…
N
Neil Japhtha

有一种超级简单的方法可以做到这一点。我在 here 上找到了它。

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

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


我喜欢这个答案,简短而简洁。
p
pierre23

这将是最简单的答案:

yourView.layer.cornerRadius = 8
yourView.layer.masksToBounds = true
yourView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]

标记它是 iOS 11 或更高版本的解决方案
r
reza_khalafi

iOS 11 , Swift 4 你可以试试这个代码:

if #available(iOS 11.0, *) {
   element.clipsToBounds = true
   element.layer.cornerRadius = CORNER_RADIUS
   element.layer.maskedCorners = [.layerMaxXMaxYCorner]
} else {
   // Fallback on earlier versions
}

您可以在表格视图单元格中使用它。


这是今天编码的正确答案。再见遮罩层。
这一定是最好的答案!
H
Harshil Kotecha

Swift 4 Swift 5 简单的方法在 1 行

用法:

//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;
    }

只记得在 viewDidLayoutSubviews 完成之前不会计算视图的角,因此您应该在其中调用 roundCorners func
首先添加圆角半径,然后添加阴影
@JirsonTavera 感谢您的提示,这让我发疯了,我几乎花了一个小时才在这里找到您的评论!
此解决方案导致应用到图层的阴影不出现
佚名

试试这个代码,

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:( UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(5.0, 5.0)];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.view.bounds;
maskLayer.path  = maskPath.CGPath;

view.layer.mask = maskLayer;

B
Benjamin RD

Emma:.TopRight.BottomRight 对您不起作用,可能是因为对 view.roundCorners 的调用是在计算最终 view bounds 之前完成的。请注意,Bezier Path 派生自调用时的视图边界。例如,如果自动布局会使视图变窄,则右侧的圆角可能在视图之外。尝试在 viewDidLayoutSubviews 中调用它,其中视图的边界是最终的。


谢谢你。对我来说,解决这个问题的方法是将调用从 viewDidLoad 和 viewWillAppear 移到 viewDidAppear 中。我可以这样做,因为我的控件最初设置为隐藏。
你说的对。它会产生自动布局的问题,你做得很好。
B
Bane M

我在 swift 中舍入 UIView 和 UITextFiels 的特定角的解决方案是使用

.layer.cornerRadius

layer.maskedCorners

实际的 UIView 或 UITextFields。

例子:

fileprivate func inputTextFieldStyle() {
        inputTextField.layer.masksToBounds = true
        inputTextField.layer.borderWidth = 1
        inputTextField.layer.cornerRadius = 25
        inputTextField.layer.maskedCorners = [.layerMaxXMaxYCorner,.layerMaxXMinYCorner]
        inputTextField.layer.borderColor = UIColor.white.cgColor
    }

并通过使用

.layerMaxXMaxYCorner

.layerMaxXMinYCorner

,我可以指定 UITextField 的右上角和右下角要四舍五入。

你可以在这里看到结果:

https://i.stack.imgur.com/821lJ.png


完美的边框和特定角落组合🎉🎉🎉
完美的解决方案
B
Benjamin RD

斯威夫特 4

extension UIView {

    func roundTop(radius:CGFloat = 5){
        self.clipsToBounds = true
        self.layer.cornerRadius = radius
        if #available(iOS 11.0, *) {
            self.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
        } else {
            // Fallback on earlier versions
        }
    }

    func roundBottom(radius:CGFloat = 5){
        self.clipsToBounds = true
        self.layer.cornerRadius = radius
        if #available(iOS 11.0, *) {
            self.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
        } else {
            // Fallback on earlier versions
        }
    }
}

在我的情况下,您节省了我的一天,我不得不为特定单元格制作四舍五入的表格视图
根据我的要求完美回答。我使用了所有答案,对我没有任何帮助,但是您的答案帮助了我并解决了我的问题。
V
Vipul Kumar

使用此扩展程序,它将涵盖所有内容。

extension UIView {

   func roundTopCorners(radius: CGFloat = 10) {
    
       self.clipsToBounds = true
       self.layer.cornerRadius = radius
       if #available(iOS 11.0, *) {
           self.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
       } else {
           self.roundCorners(corners: [.topLeft, .topRight], radius: radius)
       }
   }

   func roundBottomCorners(radius: CGFloat = 10) {
    
       self.clipsToBounds = true
       self.layer.cornerRadius = radius
       if #available(iOS 11.0, *) {
           self.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
       } else {
           self.roundCorners(corners: [.bottomLeft, .bottomRight], radius: radius)
       }
   }

   private 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
    }
}

然后像这样使用它:-

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
            
    self.yourView.roundTopCorners()
}

注意:- 我建议您不要将此代码放在 viewDidLayoutSubviews() 中,因为每当视图更新时,您都会在其中调用。所以使用 viewDidAppear(),它会像一个魅力一样工作。


谢谢它有效
@SourabhSharma 很高兴它有帮助:)
F
FunkyKat

简单的扩展

extension UIView {
    func roundCorners(corners: UIRectCorner, radius: CGFloat) {
        if #available(iOS 11, *) {
            self.clipsToBounds = true
            self.layer.cornerRadius = radius
            var masked = CACornerMask()
            if corners.contains(.topLeft) { masked.insert(.layerMinXMinYCorner) }
            if corners.contains(.topRight) { masked.insert(.layerMaxXMinYCorner) }
            if corners.contains(.bottomLeft) { masked.insert(.layerMinXMaxYCorner) }
            if corners.contains(.bottomRight) { masked.insert(.layerMaxXMaxYCorner) }
            self.layer.maskedCorners = masked
        }
        else {
            let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
            let mask = CAShapeLayer()
            mask.path = path.cgPath
            layer.mask = mask
        }
    }
}

用法:

view.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 12)

D
Dmytro Onyshchuk

这是 Swift 5 的最佳方式:

import UIKit

extension UIView {

func roundCorners(radius: CGFloat = 10, corners: UIRectCorner = .allCorners) {
        self.clipsToBounds = true
        self.layer.cornerRadius = radius
        if #available(iOS 11.0, *) {
            var arr: CACornerMask = []
            
            let allCorners: [UIRectCorner] = [.topLeft, .topRight, .bottomLeft, .bottomRight, .allCorners]
            
            for corn in allCorners {
                if(corners.contains(corn)){
                    switch corn {
                    case .topLeft:
                        arr.insert(.layerMinXMinYCorner)
                    case .topRight:
                        arr.insert(.layerMaxXMinYCorner)
                    case .bottomLeft:
                        arr.insert(.layerMinXMaxYCorner)
                    case .bottomRight:
                        arr.insert(.layerMaxXMaxYCorner)
                    case .allCorners:
                        arr.insert(.layerMinXMinYCorner)
                        arr.insert(.layerMaxXMinYCorner)
                        arr.insert(.layerMinXMaxYCorner)
                        arr.insert(.layerMaxXMaxYCorner)
                    default: break
                    }
                }
            }
            self.layer.maskedCorners = arr
        } else {
            self.roundCornersBezierPath(corners: corners, radius: radius)
        }
    }
    
    private func roundCornersBezierPath(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
    }
    
}

N
Nate Symer

以编程方式执行此操作的一种方法是在具有圆角的 UIView 的顶部创建一个 UIView。或者您可以将顶部隐藏在某些东西下面。


这是一个非常丑陋的解决方案:P
s
silwar
    // Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds 
                           byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight) 
                           cornerRadii:CGSizeMake(7.0, 7.0)];

// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = cell.stripBlackImnageView.bounds;
maskLayer.path = maskPath.CGPath; 
// Set the newly created shapelayer as the mask for the image view's layer
view.layer.mask = maskLayer;

M
Matt Hudson

最简单的方法是制作带有圆角层的蒙版。

CALayer *maskLayer = [CALayer layer];
maskLayer.frame = CGRectMake(0,0,maskWidth ,maskHeight);
maskLayer.contents = (__bridge id)[[UIImage imageNamed:@"maskImageWithRoundedCorners.png"] CGImage];

aUIView.layer.mask = maskLayer;

不要忘记:

#import <QuartzCore/QuartzCore.h>

a
apouche

已经给出的所有答案都非常好且有效(尤其是尤努斯使用 mask 属性的想法)。

但是我需要一些更复杂的东西,因为我的层经常会改变大小,这意味着我每次都需要调用该屏蔽逻辑,这有点烦人。

我使用了 swift extensions 和计算属性来构建一个真正的 cornerRadii 属性,该属性负责在布局图层时自动更新蒙版。

这是使用 Peter Steinberg 出色的 Aspects 库进行调配来实现的。

完整代码在这里:

extension CALayer {
  // This will hold the keys for the runtime property associations
  private struct AssociationKey {
    static var CornerRect:Int8 = 1    // for the UIRectCorner argument
    static var CornerRadius:Int8 = 2  // for the radius argument
  }

  // new computed property on CALayer
  // You send the corners you want to round (ex. [.TopLeft, .BottomLeft])
  // and the radius at which you want the corners to be round
  var cornerRadii:(corners: UIRectCorner, radius:CGFloat) {
    get {
      let number = objc_getAssociatedObject(self, &AssociationKey.CornerRect)  as? NSNumber ?? 0
      let radius = objc_getAssociatedObject(self, &AssociationKey.CornerRadius)  as? NSNumber ?? 0
      return (corners: UIRectCorner(rawValue: number.unsignedLongValue), radius: CGFloat(radius.floatValue))
    }
    set (v) {
      let radius = v.radius
      let closure:((Void)->Void) = {
        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: v.corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.CGPath
        self.mask = mask
      }
      let block: @convention(block) Void -> Void = closure
      let objectBlock = unsafeBitCast(block, AnyObject.self)
      objc_setAssociatedObject(self, &AssociationKey.CornerRect, NSNumber(unsignedLong: v.corners.rawValue), .OBJC_ASSOCIATION_RETAIN)
      objc_setAssociatedObject(self, &AssociationKey.CornerRadius, NSNumber(float: Float(v.radius)), .OBJC_ASSOCIATION_RETAIN)
      do { try aspect_hookSelector("layoutSublayers", withOptions: .PositionAfter, usingBlock: objectBlock) }
      catch _ { }
    }
  }
}

我写了一个简单的 blog post 来解释这一点。


a
apinho

重用 Yunus Nedim Mehel 解决方案的可爱扩展

斯威夫特 2.3

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

用法

let view = UIView()
view.roundCornersWithLayerMask(10,[.TopLeft,.TopRight])

不错的清洁解决方案
B
Bruno Bieri

如果您正在寻找仅限界面构建器的解决方案,则可以找到适用于 iOS 11 及更高版本的解决方案。在此处查看我的答案:https://stackoverflow.com/a/58626264


j
jfmg

这是在 C# 中使用 Xamarin 为按钮的每个角设置角半径的方法:

var maskPath = UIBezierPath.FromRoundedRect(MyButton.Bounds, UIRectCorner.BottomLeft | UIRectCorner.BottomRight,
    new CGSize(10.0, 10.0));
var maskLayer = new CAShapeLayer
{
    Frame = MyButton.Bounds,
    Path = maskPath.CGPath
};
MyButton.Layer.Mask = maskLayer;

A
Abdelrahman Mohamed

对于SwiftUI

我找到了这些解决方案,您可以从这里查看 https://stackoverflow.com/a/56763282/3716103

我强烈推荐第一个

选项 1:使用 Path + GeometryReader

(有关 GeometryReader 的更多信息:https://swiftui-lab.com/geometryreader-to-the-rescue/

struct ContentView : View {
    var body: some View {

        Text("Hello World!")
            .foregroundColor(.white)
            .font(.largeTitle)
            .padding(20)
            .background(RoundedCorners(color: .blue, tl: 0, tr: 30, bl: 30, br: 0))
    }
}

https://i.stack.imgur.com/J1Sgp.png

RoundedCorners

struct RoundedCorners: View {

    var color: Color = .white

    var tl: CGFloat = 0.0
    var tr: CGFloat = 0.0
    var bl: CGFloat = 0.0
    var br: CGFloat = 0.0

    var body: some View {
        GeometryReader { geometry in
            Path { path in

                let w = geometry.size.width
                let h = geometry.size.height

                // Make sure we do not exceed the size of the rectangle
                let tr = min(min(self.tr, h/2), w/2)
                let tl = min(min(self.tl, h/2), w/2)
                let bl = min(min(self.bl, h/2), w/2)
                let br = min(min(self.br, h/2), w/2)

                path.move(to: CGPoint(x: w / 2.0, y: 0))
                path.addLine(to: CGPoint(x: w - tr, y: 0))
                path.addArc(center: CGPoint(x: w - tr, y: tr), radius: tr, startAngle: Angle(degrees: -90), endAngle: Angle(degrees: 0), clockwise: false)
                path.addLine(to: CGPoint(x: w, y: h - be))
                path.addArc(center: CGPoint(x: w - br, y: h - br), radius: br, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 90), clockwise: false)
                path.addLine(to: CGPoint(x: bl, y: h))
                path.addArc(center: CGPoint(x: bl, y: h - bl), radius: bl, startAngle: Angle(degrees: 90), endAngle: Angle(degrees: 180), clockwise: false)
                path.addLine(to: CGPoint(x: 0, y: tl))
                path.addArc(center: CGPoint(x: tl, y: tl), radius: tl, startAngle: Angle(degrees: 180), endAngle: Angle(degrees: 270), clockwise: false)
            }
            .fill(self.color)
        }
    }
}

RoundedCorners_Previews

struct RoundedCorners_Previews: PreviewProvider {
    static var previews: some View {
        RoundedCorners(color: .pink, tl: 40, tr: 40, bl: 40, br: 40)
    }
}

https://i.stack.imgur.com/EHEUY.png


A
Akshay Khadke

使用这个扩展来设置圆角和圆角的圆形边框

像这样使用:

override func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()

         myView.roundCornersWithBorder(corners: [.topLeft, .topRight], radius: 8.0)
    
        myView.roundCorners(corners: [.topLeft, .topRight], radius: 8.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
    }
    
    func roundCornersWithBorder(corners: UIRectCorner, radius: CGFloat) {
        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: radius, height: radius)).cgPath
        
        layer.mask = maskLayer
        
        // Add border
        let borderLayer = CAShapeLayer()
        borderLayer.path = maskLayer.path // Reuse the Bezier path
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = UIColor(red:3/255, green:33/255, blue:70/255, alpha: 0.15).cgColor
        borderLayer.lineWidth = 2
        borderLayer.frame = bounds
        layer.addSublayer(borderLayer)
    }
    
}

S
Sanjay Mishra

更改代码后@apinho 在 swift 4.3 中工作正常

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

使用此功能为您查看

YourViewName. roundCornersWithLayerMask(cornerRadii: 20,corners: [.topLeft,.topRight])

m
mehdi

Stephane 答案的另一个版本。

import UIKit

    class RoundCornerView: UIView {
    var corners : UIRectCorner = [.topLeft,.topRight,.bottomLeft,.bottomRight]
        var roundCornerRadius : CGFloat = 0.0
        override func layoutSubviews() {
            super.layoutSubviews()
            if corners.rawValue > 0 && roundCornerRadius > 0.0 {
                self.roundCorners(corners: corners, radius: roundCornerRadius)
            }
        }
        private 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
        }

    }

S
Sreeraj VR

在 Swift 4.2 中,通过 @IBDesignable 创建它,如下所示:

@IBDesignable

class DesignableViewCustomCorner: UIView {

    @IBInspectable var cornerRadious: CGFloat = 0 {
        didSet {
            let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: cornerRadious, height: cornerRadious))
            let mask = CAShapeLayer()
            mask.path = path.cgPath
            self.layer.mask = mask
        }
    }

}