目前设置 UIView 角半径的“正确”方法是啥?

Posted

技术标签:

【中文标题】目前设置 UIView 角半径的“正确”方法是啥?【英文标题】:What's currently the "correct" way to set a UIView's corner radius?目前设置 UIView 角半径的“正确”方法是什么? 【发布时间】:2017-06-18 05:02:06 【问题描述】:

可以通过以下方式设置UIView的圆角半径:

    设置layercornerRadius属性:

    view.layer.cornerRadius = 5;
    view.layer.masksToBounds = true;
    

    应用蒙版:

    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
    
    

    覆盖draw(_:):

    func draw(_ rect: CGRect) 
        // Size of rounded rectangle
        let rectWidth = rect.width
        let rectHeight = rect.height
    
        // Find center of actual frame to set rectangle in middle
        let xf: CGFloat = (self.frame.width  - rectWidth)  / 2
        let yf: CGFloat = (self.frame.height - rectHeight) / 2
    
        let ctx = UIGraphicsGetCurrentContext()!
        ctx.saveGState()
    
        let rect = CGRect(x: xf, y: yf, width: rectWidth, height: rectHeight)
        let clipPath = UIBezierPath(roundedRect: rect, cornerRadius: rectCornerRadius).cgPath
    
        ctx.addPath(clipPath)
        ctx.setFillColor(rectBgColor.cgColor)
    
        ctx.closePath()
        ctx.fillPath()
        ctx.restoreGState()
    
    

以下哪一项通常被认为是在UIView 上实现圆角的“正确”方式,符合以下标准:

配置(某些角可能是圆角,而另一些则不是) 动画(你能动画cornerRadius变化吗) 灵活性(它是否会破坏您已应用的第三方库或掩码) 可读性(解决方案的简洁性/可重用性) 速度(是否会对性能产生负面影响)

【问题讨论】:

抱歉不一致,我只是抓住了我可以在网上找到的每个选项的最短示例。随意使用 Swift3 进行编辑。 除了所有答案之外,我们还可以使用 ios 11 中的“CACornerMask”轻松设置角半径以查看选定角。 顺便说一句,如果你使用draw(_:),永远不要使用frame(在superview的坐标系中)。使用bounds(在视图自己的坐标系中)。 【参考方案1】:

请注意,我不知道目前设置 UIView 圆角半径的“正确”方法是什么。

我更喜欢做的是尽可能多地使用 Interface Builder,而不需要额外的代码,这种方法显示并且根据我的经验是可靠的。


iOS 11 以上版本

您可以通过设置以下属性在Interface BuilderIdentity inspector 中使用用户定义的运行时属性:

layer.cornerRadius
layer.maskedCorners
layer.masksToBounds

根据documentation of the CACornerMask,您可以看到maskedCorners 属性实际上是NSUInteger 数据类型,您可以设置以下值:

kCALayerMinXMinYCorner = 1U << 0
kCALayerMaxXMinYCorner = 1U << 1
kCALayerMinXMaxYCorner = 1U << 2
kCALayerMaxXMaxYCorner = 1U << 3

由于您可以将这些掩码bitwise OR 一起使用,因此您只需“计算”您实际需要的按位 OR 的结果整数。

因此,为maskedCorners 属性设置以下数字(整数)值以获得圆角:

 0 = no corner is being rounded
 1 = top left corner rounded only
 2 = top right corner rounded only
 3 = top left and top right corners rounded only
 4 = bottom left corner rounded only
 5 = top left and bottom left corners rounded only
 6 = top right and bottom left corners rounded only
 7 = top left, top right and bottom left corners rounded only
 8 = bottom right corner rounded only
 9 = top left and bottom right corners rounded only
10 = top right and bottom right corners rounded only
11 = top left, top right and bottom right corners rounded only
12 = bottom left and bottom right corners rounded only
13 = top left, bottom left and bottom right corners rounded only
14 = top right, bottom left and bottom right corners rounded only
15 = all corners rounded

示例:如果您想为 UIView 的 top-lefttop-right 角设置圆角半径,您可以使用这些属性:

【讨论】:

不太明白这个问题是如何回答的 @vahotm 添加说明【参考方案2】:

你的三个选择:

    使用CALayer 现有属性:对于简单的边角遮罩,这是一种简单(并且可能是最有效)的解决方案。它也是动画的。在 iOS 11 及更高版本中,您可以选择要遮罩的角。

    重新自定义CAShapeLayer 掩码:如果角掩码不是简单的圆角而是一些任意路径,这是一个很好的方法。如果frame 发生变化,您必须小心确保更新此掩码(例如更新视图layoutSubviews 或控制器viewDidLayoutSubviews 中的路径)。

    诚然,如果您想在视图的frame 更改时制作一个非常优美的动画,那需要更多的工作。但是,正如我在上面指出的,简单地响应 layoutSubviewsviewDidLayoutSubviews 中的帧变化非常简单,如果您不太担心动画中间的圆角,就可以处理它。

    重新自定义draw(_:):这比它值得做的工作更多,您可能不喜欢 Apple 团队可能在幕后所做的优化(例如,如果随后的 draw 调用只绘制了完整的一部分怎么办? bounds;不管怎样,你的代码都在重绘整个东西。

我建议选项 1 用于简单的情况,如果您需要比选项 1 提供的更多控制,我建议选项 2。但没有“最佳”方法:这取决于您需要什么以及您愿意完成多少工作。

【讨论】:

我相信 3 不会围绕子视图。它只是画一个椭圆。 嗯,不是椭圆,而是圆角矩形。但你是对的,它只是填充那条路径,而不是裁剪到那条路径。它甚至没有做对。但它可以很容易地修复以裁剪此特定视图的draw(_:) 呈现的任何其他内容。 (顺便说一句,如果您选择“剪辑到边界”,第一种方法只会裁剪子视图。) @Rob 你如何处理动画/视差效果(比如在 tvOS 中)?我设法使其完全工作的唯一方法是重绘图像,剪掉角落。一旦完成,使用 clipsToBounds = false 否则缩放的图像会被剪裁,我必须使用“masksFocusEffectToContents = true”。效果很好,但我怀疑 Apple 在电影等应用中是如何做到的。 我用的是2(或者ios 12上的1)但是只有左边是正确舍入的,右边不是,背景没有正确更新,大概是因为更新了bounds /frame 要更宽(然后是掩蔽):imgur.com/a/BmP6cu7 圆角半径的代码当前位于layoutSubviews 内。似乎其他人也有来自 cmets ***.com/a/11255791/321629 的相同问题 修复了它,因为我必须调用 self.setNeedsLayout()self.layoutIfNeeded() 才能更新子视图边界(我也在使用自动布局),然后它会获得正确的更新绑定数据。 【参考方案3】:

我使用 iOS 11 或更低版本进行了几次测试,我发现的最佳实践是绕过特定或所有角落,您可以使用下一个代码。

// Full size
CGSize vSize = [UIScreen mainScreen].bounds.size;

// Object
UIView *viewTest = [[UIView alloc] initWithFrame: CGRectMake(0, 0, vSize.width, vSize.height)];
[viewTest setAutoresizingMask: UIViewAutoresizingFlexibleHeight];
[viewTest setBackgroundColor: [UIColor grayColor]];

// maskedCorners is only available in iOS 11
if (@available(iOS 11.0, *)) 
    [viewTest setClipsToBounds: YES];
    [viewTest.layer setCornerRadius: 10];

    // Only if you want to round the left and right top corners
    [viewTest.layer setMaskedCorners: kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner];

else 
    // The old way used in lower version
    CAShapeLayer *shapeLayerObj = [CAShapeLayer layer];
    [shapeLayerObj setPath: [UIBezierPath bezierPathWithRoundedRect: viewTest.bounds byRoundingCorners: UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii: (CGSize)10.0, 10.].CGPath];

    [viewTest.layer setMask: shapeLayerObj];

此代码修复了 autoResizingMask 在使用旧方法圆角时不起作用的问题。 修复了带有圆角的 UIScrollView 和 setContentSize 主要为对象高度的错误。

swift版是这样的

let view = UIView()
view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]

【讨论】:

是的,如果你想要一个快速版本,请检查这个答案***.com/questions/40498892/… 但如果你想要 Objective-C 版本,我可以做到,干杯:) 我试过了,但只有圆角半径没有阴影显示。 @Ladd.c【参考方案4】:

我认为这是最全面的总结:http://texturegroup.org/docs/corner-rounding.html

我的启发是,如果视图不需要高性能(例如,它不在表格视图单元格内),最简单的选择是使用 CALayer 的cornerRadius。如果您需要更高级的圆角半径或高性能,那么最好探索其他选项。

【讨论】:

【参考方案5】:

我选择第一个,这是一种更简洁的方式,您可以在 IDE 中完成,无需代码。打开属性检查器,然后单击身份检查器并在“用户定义的运行时属性”下添加这两个属性:

【讨论】:

除此之外不允许仅对某些角进行舍入的第一个标准。 同意@rmaddy,不确定我们是否可以添加用户定义的属性来屏蔽 minXminY 角 @Naishta 似乎可以看到我的回答:***.com/a/58626264

以上是关于目前设置 UIView 角半径的“正确”方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

仅将角半径设置为 UIView/UIButton 的特定侧

在 UIView 上绘制的遮罩层的角半径

如何在tableview Custom Cell上为UIView的左上角和右上角设置角半径?

uiview 上的角半径没有按预期工作

Swift 四个角设置不同半径大小的圆角

如何在 iOS Swift 中将不同的角半径应用于 UIView 的不同角?