提问



有没有办法只为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;

最佳参考


请注意以下事实:如果附加了布局约束,则必须按如下方式刷新:


 override public func layoutSubviews() {
        super.layoutSubviews()
        roundCorners(corners: [.bottomLeft, .bottomRight], radius: 3.0)
    }


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

其它参考1


我不确定为什么你的解决方案不起作用,但以下代码对我有用。创建一个贝塞尔蒙版并将其应用于您的视图。在下面的代码中,我将半径为3像素的_backgroundView的底角四舍五入。 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版本有一些改进:


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


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


注意



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


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

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

其它参考3


这里的Swift代码示例:https://stackoverflow.com/a/35621736/308315





不是直接的。你不得不:



  1. 创建CAShapeLayer

  2. 根据view.boundspath设置为CGPathRef,但只有两个圆角(可能使用+[UIBezierPath bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:])

  3. view.layer.mask设置为CAShapeLayer


其它参考4


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


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

其它参考5


最后...... iOS11中有CACornerMask!
CACornerMask可以很容易地完成:[48]


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

其它参考6


试试这个代码,


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;

其它参考7


Emma:.TopRight和.BottomRight可能不适合你,因为在计算最终视图边界之前完成了对view.roundCorners的调用。请注意,Bezier路径源自调用时的视图边界。例如,如果自动布局会缩小视图范围,则右侧的圆角可能位于视图外部。
尝试在viewDidLayoutSubviews中调用它,其中视图的绑定是最终的。

其它参考8


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

其它参考9


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

其它参考10


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
}


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

其它参考11


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


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


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


这是通过彼得斯坦伯格伟大的Aspects库进行调整来实现的。[49] [50]


完整代码在这里:


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 _ { }
    }
  }
}


我写了一篇简短的博客文章来解释这一点。[51]

其它参考12


对于iOS 11以上,此代码就足够了


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


但对于较低版本


let rectShape = CAShapeLayer()
    rectShape.bounds = detailsSubView.frame
    rectShape.position = detailsSubView.center
    rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds,    byRoundingCorners: [.bottomLeft , .bottomRight], 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 = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
    }else{
        let rectShape = CAShapeLayer()
        rectShape.bounds = detailsSubView.frame
        rectShape.position = detailsSubView.center
        rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds,    byRoundingCorners: [.bottomLeft , .bottomRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
        detailsSubView.layer.mask = rectShape
    }
}

其它参考13


最简单的方法是制作一个带圆角层的面具。


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>

其它参考14


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

其它参考15


重用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])

其它参考16


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