目前设置 UIView 角半径的“正确”方法是啥?
Posted
技术标签:
【中文标题】目前设置 UIView 角半径的“正确”方法是啥?【英文标题】:What's currently the "correct" way to set a UIView's corner radius?目前设置 UIView 角半径的“正确”方法是什么? 【发布时间】:2017-06-18 05:02:06 【问题描述】:可以通过以下方式设置UIView
的圆角半径:
设置layer
的cornerRadius
属性:
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 Builder
的Identity 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-left 和 top-right 角设置圆角半径,您可以使用这些属性:
【讨论】:
不太明白这个问题是如何回答的 @vahotm 添加说明【参考方案2】:你的三个选择:
使用CALayer
现有属性:对于简单的边角遮罩,这是一种简单(并且可能是最有效)的解决方案。它也是动画的。在 iOS 11 及更高版本中,您可以选择要遮罩的角。
重新自定义CAShapeLayer
掩码:如果角掩码不是简单的圆角而是一些任意路径,这是一个很好的方法。如果frame
发生变化,您必须小心确保更新此掩码(例如更新视图layoutSubviews
或控制器viewDidLayoutSubviews
中的路径)。
诚然,如果您想在视图的frame
更改时制作一个非常优美的动画,那需要更多的工作。但是,正如我在上面指出的,简单地响应 layoutSubviews
或 viewDidLayoutSubviews
中的帧变化非常简单,如果您不太担心动画中间的圆角,就可以处理它。
重新自定义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 角半径的“正确”方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章