有没有办法为一个UIView的左上角和右上角设置角半径?
我尝试了下面的方法,但最终它再也看不到视图了。
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;
简单的扩展
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)
以下是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
}
}
所有已经给出的答案都是非常好的和有效的(特别是尤努斯使用蒙版属性的想法)。
然而,我需要一些更复杂的东西,因为我的层可以经常改变大小,这意味着我需要每次都调用掩蔽逻辑,这有点烦人。
我使用了swift扩展和计算属性来构建一个真正的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 _ { }
}
}
}
我写了一篇简单的博客来解释这一点。