如何为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个回答

4
最简单的方法是使用带有圆角图层的遮罩。
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>

4
所有已经给出的答案都非常好和有效(特别是Yunus使用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 _ { }
    }
  }
}

我写了一篇简单的博客文章来解释这个。


3

这是一个很棒的扩展,可以重复使用Yunus Nedim Mehel的解决方案。

适用于Swift 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])

不错的干净解决方案 - Aggressor

3

3
使用此扩展程序可以设置圆角和带有圆角的边框。
用法如下:
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)
    }
    
}

2
这是使用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;

2

仅将视图左上角和右上角设置圆角

lazy var footerBackgroundView: UIView = {

    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor = .blue

    //Make Corners Radius TopLeft & Top Right
    view.clipsToBounds = true
    view.layer.cornerRadius = 8
    view.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner] 
    
    return view
}()

2

对于SwiftUI

我找到了以下解决方案,你可以在这里查看:https://dev59.com/2FMI5IYBdhLWcg3wN4vi#56763282

我强烈推荐第一个

选项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))
    }
}

使用背景为文本视图提供圆角

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)
        }
    }
}
是一个代码段的名称,可能与圆角相关。
struct RoundedCorners_Previews: PreviewProvider {
    static var previews: some View {
        RoundedCorners(color: .pink, tl: 40, tr: 40, bl: 40, br: 40)
    }
}

I give the corner radius to the top of view only


1

在修改了代码位 @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])

0

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
        }

    }

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