如何在Swift中使用AutoLayout绘制四分之一圆(象限圆)
在Swift中用AutoLayout实现四分之一象限圆
核心思路
要实现标准的四分之一圆,关键是确保承载图形的视图为正方形(通过AutoLayout约束宽高相等),再用UIBezierPath绘制对应象限的圆弧并闭合路径,最后通过自定义View绘制或给普通View添加CAShapeLayer蒙版来呈现效果。
实现方案(两种常用方式)
方式一:自定义View直接绘制
创建自定义UIView子类,在绘制方法中生成对应象限的路径,同时通过AutoLayout约束宽高一致。
import UIKit class QuarterCircleView: UIView { // 定义四象限枚举 enum Quadrant { case topLeft, topRight, bottomLeft, bottomRight } var quadrant: Quadrant = .topRight { didSet { setNeedsDisplay() } } override func draw(_ rect: CGRect) { super.draw(rect) guard let context = UIGraphicsGetCurrentContext() else { return } // 强制转为正方形,确保圆弧为标准四分之一圆 let squareRect = CGRect(x: rect.origin.x, y: rect.origin.y, width: min(rect.width, rect.height), height: min(rect.width, rect.height)) // 根据象限设置圆弧的起始/结束角度、圆心 var startAngle: CGFloat = 0 var endAngle: CGFloat = 0 var centerPoint: CGPoint = .zero switch quadrant { case .topRight: startAngle = -CGFloat.pi / 2 endAngle = 0 centerPoint = CGPoint(x: squareRect.minX, y: squareRect.minY) case .topLeft: startAngle = CGFloat.pi endAngle = CGFloat.pi / 2 centerPoint = CGPoint(x: squareRect.maxX, y: squareRect.minY) case .bottomLeft: startAngle = CGFloat.pi / 2 endAngle = 0 centerPoint = CGPoint(x: squareRect.maxX, y: squareRect.maxY) case .bottomRight: startAngle = 0 endAngle = -CGFloat.pi / 2 centerPoint = CGPoint(x: squareRect.minX, y: squareRect.maxY) } // 创建贝塞尔路径并闭合 let path = UIBezierPath(arcCenter: centerPoint, radius: squareRect.width / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true) path.addLine(to: centerPoint) path.close() // 设置填充与边框样式 UIColor.blue.setFill() path.fill() UIColor.black.setStroke() path.lineWidth = 2 path.stroke() } // 设置默认 intrinsic 大小,可被AutoLayout覆盖 override var intrinsicContentSize: CGSize { return CGSize(width: 100, height: 100) } }
在ViewController中添加并约束:
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let quarterCircle = QuarterCircleView() quarterCircle.quadrant = .topRight quarterCircle.translatesAutoresizingMaskIntoConstraints = false view.addSubview(quarterCircle) // 约束:距离右上角各20pt,宽高150pt(强制宽高相等) NSLayoutConstraint.activate([ quarterCircle.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), quarterCircle.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), quarterCircle.widthAnchor.constraint(equalToConstant: 150), quarterCircle.heightAnchor.constraint(equalTo: quarterCircle.widthAnchor) ]) } }
方式二:用CAShapeLayer做蒙版
如果不想自定义View,可给普通UIView添加蒙版层实现效果,同样需要约束宽高相等:
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let circleView = UIView() circleView.backgroundColor = .red circleView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(circleView) // 约束:居中显示,宽高200pt且相等 NSLayoutConstraint.activate([ circleView.centerXAnchor.constraint(equalTo: view.centerXAnchor), circleView.centerYAnchor.constraint(equalTo: view.centerYAnchor), circleView.widthAnchor.constraint(equalToConstant: 200), circleView.heightAnchor.constraint(equalTo: circleView.widthAnchor) ]) // 布局完成后更新蒙版路径(AutoLayout会在viewDidLoad后确定bounds) DispatchQueue.main.async { [weak self] in self?.updateQuarterCircleMask(for: circleView, quadrant: .bottomLeft) } } private func updateQuarterCircleMask(for view: UIView, quadrant: QuarterCircleView.Quadrant) { let radius = view.bounds.width / 2 var centerPoint: CGPoint = .zero var startAngle: CGFloat = 0 var endAngle: CGFloat = 0 switch quadrant { case .bottomLeft: centerPoint = CGPoint(x: view.bounds.maxX, y: view.bounds.maxY) startAngle = .pi/2 endAngle = .pi // 可添加其他象限的配置逻辑 default: break } let path = UIBezierPath() path.move(to: centerPoint) path.addArc(withCenter: centerPoint, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true) path.close() let maskLayer = CAShapeLayer() maskLayer.path = path.cgPath view.layer.mask = maskLayer } }
关键注意事项
- 强制宽高相等:只有视图为正方形时,圆弧半径才能与边长匹配,得到标准四分之一圆,可通过
heightAnchor.constraint(equalTo: widthAnchor)约束。 - 象限对应参数匹配:不同象限的圆心位置、起始/结束角度不同,需严格对应(比如右上象限圆心在视图左上角,角度范围是-π/2到0)。
- 布局完成后更新路径:AutoLayout会在
viewDidLoad之后确定视图的实际尺寸,因此需要在viewDidLayoutSubviews或异步回调中更新路径。
内容的提问来源于stack exchange,提问作者K. Janjuha
相关产品推荐
相关产品推荐

