UIBezierPath частично обводит - PullRequest
1 голос
/ 20 мая 2019

У меня есть этот код для рисования прямоугольника, который скруглен только на одной стороне.

this one

  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)
}

Я хочу обвести его красным цветом на всем, кроме правого края (без правого края). Как мне это сделать?

1 Ответ

1 голос
/ 20 мая 2019

Вам придется создать свой собственный путь.

Пара наблюдений:

  • Не используйте параметр rect. rect - это то, что просят нарисовать в данный момент времени, что может быть не полностью. Используйте bounds, чтобы выяснить, каким должен быть общий путь.

  • Я мог бы вставить путь так, чтобы штрих оставался в границах вида.

  • Вы можете сделать это @IBDesignable, если хотите, чтобы вы также могли видеть его в IB.

  • Вам действительно не нужно 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()
    }
}

Что дает:

enter image description here


Теоретически, может быть более эффективно использовать 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
    }
}
...