UIBezierPath 部分描边

UIBezierPath partially stroke

我有这段代码可以绘制一个只有一侧是圆角矩形的矩形。

  override func draw(_ rect: CGRect) {
    // Drawing code

    guard let context = UIGraphicsGetCurrentContext() else { return }

    let lineWidth = CGFloat(4)


    let pathRect = CGRect(x: 0, y: 0, width: rect.width, height: rect.height)
    let path = UIBezierPath(roundedRect: pathRect.inset(by: UIEdgeInsets(top: lineWidth, left: lineWidth, bottom: lineWidth, right: 0)), byRoundingCorners: [.topLeft, .bottomLeft], cornerRadii: CGSize(width: 7, height: 7))

    context.setFillColor(UIColor.black.cgColor)
    path.fill()

    context.setLineWidth(lineWidth)
}

我想在除右边缘以外的所有边缘都用红色描边(右边缘没有描边)。我该怎么做?

您必须创建自己的路径。

一些观察:

  • 不要使用rect参数。 rect 是此时要求绘制的内容,可能不是整个视图。在确定总体路径应该是什么时使用 bounds

  • 我可能会插入路径,使笔画保持在视图的范围内。

  • 如果你还想在 IB 中看到它,你可以制作这个 @IBDesignable

  • 你真的不需要UIGraphicsGetCurrentContext()UIKit 方法 fill()stroke()setFill()setStroke() 方法自动使用当前上下文。

因此:

@IBDesignable
class OpenRightView: UIView {
    @IBInspectable var lineWidth: CGFloat = 4      { didSet { setNeedsDisplay() } }
    @IBInspectable var radius: CGFloat = 7         { didSet { setNeedsDisplay() } }
    @IBInspectable var fillColor: UIColor = .black { didSet { setNeedsDisplay() } }
    @IBInspectable var strokeColor: UIColor = .red { didSet { setNeedsDisplay() } }

    override func draw(_ rect: CGRect) {
        let pathRect = bounds.inset(by: .init(top: lineWidth / 2, left: lineWidth / 2, bottom: lineWidth / 2, right: 0))

        let path = UIBezierPath()
        path.lineWidth = lineWidth
        path.move(to: CGPoint(x: pathRect.maxX, y: pathRect.minY))
        path.addLine(to: CGPoint(x: pathRect.minX + radius, y: pathRect.minY))
        path.addQuadCurve(to: CGPoint(x: pathRect.minX, y: pathRect.minY + radius), controlPoint: pathRect.origin)
        path.addLine(to: CGPoint(x: pathRect.minX, y: pathRect.maxY - radius))
        path.addQuadCurve(to: CGPoint(x: pathRect.minX + radius, y: pathRect.maxY), controlPoint: CGPoint(x: pathRect.minX, y: pathRect.maxY))
        path.addLine(to: CGPoint(x: pathRect.maxX, y: pathRect.maxY))

        fillColor.setFill()
        path.fill()

        strokeColor.setStroke()
        path.stroke()
    }
}

产生:


理论上,使用 CAShapeLayer 并让 Apple 为我们处理 draw(_:) 可能更有效。例如,他们可能优化了渲染以处理部分视图更新等。

可能如下所示:

@IBDesignable
class OpenRightView: UIView {
    @IBInspectable var lineWidth: CGFloat = 4      { didSet { updatePath() } }
    @IBInspectable var radius: CGFloat = 7         { didSet { updatePath() } }
    @IBInspectable var fillColor: UIColor = .black { didSet { shapeLayer.fillColor = fillColor.cgColor } }
    @IBInspectable var strokeColor: UIColor = .red { didSet { shapeLayer.strokeColor = strokeColor.cgColor } }

    lazy var shapeLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.fillColor = fillColor.cgColor
        shapeLayer.strokeColor = strokeColor.cgColor
        shapeLayer.lineWidth = lineWidth
        return shapeLayer
    }()

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        configure()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        updatePath()
    }
}

private extension OpenRightView {
    func configure() {
        layer.addSublayer(shapeLayer)
    }

    func updatePath() {
        let pathRect = bounds.inset(by: .init(top: lineWidth / 2, left: lineWidth / 2, bottom: lineWidth / 2, right: 0))

        let path = UIBezierPath()
        path.move(to: CGPoint(x: pathRect.maxX, y: pathRect.minY))
        path.addLine(to: CGPoint(x: pathRect.minX + radius, y: pathRect.minY))
        path.addQuadCurve(to: CGPoint(x: pathRect.minX, y: pathRect.minY + radius), controlPoint: pathRect.origin)
        path.addLine(to: CGPoint(x: pathRect.minX, y: pathRect.maxY - radius))
        path.addQuadCurve(to: CGPoint(x: pathRect.minX + radius, y: pathRect.maxY), controlPoint: CGPoint(x: pathRect.minX, y: pathRect.maxY))
        path.addLine(to: CGPoint(x: pathRect.maxX, y: pathRect.maxY))
        shapeLayer.path = path.cgPath
        shapeLayer.lineWidth = lineWidth
    }
}