使用 UIBezierPath 的平行四边形视图

Parallelogram view using UIBezierPath

我正在尝试使用 UIBezierPath 创建自定义平行四边形视图,但没有得到完美的视图。

以下是我的自定义视图代码

class CustomView: UIView {
    override func draw(_ rect: CGRect) {
        let offset  = 60.0;
        let path = UIBezierPath()

           path.move(to: CGPoint(x: self.frame.origin.x + CGFloat(offset), y: self.frame.origin.y))
           path.addLine(to: CGPoint(x: self.frame.width + self.frame.origin.x  , y: self.frame.origin.y))
           path.addLine(to: CGPoint(x: self.frame.origin.x + self.frame.width - CGFloat(offset) , y: self.frame.origin.y + self.frame.height))
           path.addLine(to: CGPoint(x: self.frame.origin.x, y: self.frame.origin.y + self.frame.height))


        // Close the path. This will create the last line automatically.
         path.close()
        UIColor.red.setFill()
        path.fill()

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        self.layer.mask = shapeLayer;

    }
}

我使用

创建视图
let customView = CustomView()
customView.frame = CGRect(origin: CGPoint(x: 10, y: 20), size: CGSize(width: 250, height: 250))
self.view.addSubview(customView)

但是我得到的是这样的视图,它不是一个完美的平行四边形。

正如@Losiowaty 所暗示的那样,数学是错误的,因为右上角超出了框架的范围。 (不确定为什么 clipToBounds=false 不起作用;但这只是一个调试建议——不是解决方案)。试试这个:

class CustomView: UIView {
    override func draw(_ rect: CGRect) {
        let offset: CGFloat = 60.0;

        let path = UIBezierPath()

        let width = self.bounds.width - offset

        let upperLeftPoint = CGPoint(x: self.bounds.origin.x + offset, y: self.bounds.origin.y)
        let upperRightPoint = CGPoint(x: self.bounds.origin.x + width, y: self.bounds.origin.y)

        let lowerRightPoint = CGPoint(x: width - offset, y: self.bounds.size.height)
        let lowerLeftPoint = CGPoint(x: self.bounds.origin.x, y: self.bounds.size.height)

        path.move(to: upperLeftPoint)
        path.addLine(to: upperRightPoint)
        path.addLine(to: lowerRightPoint)
        path.addLine(to: lowerLeftPoint)
        path.addLine(to: upperLeftPoint)

        // Close the path. This will create the last line automatically.
        path.close()
        UIColor.red.setFill()
        path.fill()

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        self.layer.mask = shapeLayer;
    }
}

问题是在 draw(_:) 中使用了 frame。问题是 frame 是“视图的大小和位置 在其父视图的坐标系中 ”(强调)。你如何在这个视图中渲染这个形状通常与这个视图在它的父视图中的位置无关。而如果这个view恰好不在它的superview的0, 0,就可以体验裁剪

但也不要使用 rect 参数。 “第一次绘制视图时,这个矩形通常是视图的整个可见边界。然而,在后续的绘图操作中,矩形可能只指定了视图的一部分。”如果 OS 决定它只需要更新你的 CustomView.

的一部分,你就有可能从根本上改变形状

改用bounds,它在视图自己的坐标系中。并且,使用 minXminYmaxXmaxY 稍微简化了代码。

@IBDesignable
class CustomView: UIView {

    @IBInspectable var offset:    CGFloat = 60        { didSet { setNeedsDisplay() } }
    @IBInspectable var fillColor: UIColor = .red      { didSet { setNeedsDisplay() } }

    override func draw(_ rect: CGRect) {
        let path = UIBezierPath()

        path.move(to: CGPoint(x: bounds.minX + offset, y: bounds.minY))
        path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
        path.addLine(to: CGPoint(x: bounds.maxX - offset, y: bounds.maxY))
        path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))

        // Close the path. This will create the last line automatically.
        path.close()
        fillColor.setFill()
        path.fill()
    }

}

顺便说一句,我没有设置掩码。如果您正在制作动画,每次调用 draw 时不断重置遮罩似乎效率不高。我个人只是将视图的背景颜色设置为 .clear。但这与眼前的问题无关。