Parallelogram view using UIBezierPath

会有一股神秘感。 提交于 2019-12-22 19:35:12

问题


I am trying to create custom parallelogram view using UIBezierPath but not getting a perfect one.

Following is my custom view code

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;

    }
}

And i create the view using

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

But i got the view like this and it is not a perfect parallelogram.


回答1:


The problem is the use of frame within draw(_:). The issue is that frame is “the size and position of the view in its superview’s coordinate system” (emphasis added). How you render this shape within this view generally has nothing to do with where this view is located in its superview. And if this view doesn’t happen to be at 0, 0 of its superview, you can experience cropping.

But don’t use rect parameter, either. “The first time your view is drawn, this rectangle is typically the entire visible bounds of your view. However, during subsequent drawing operations, the rectangle may specify only part of your view.” You risk having shape radically changed if the OS decides it only needs to update a part of your CustomView.

Use bounds, instead, which is in the view’s own coordinate system. And, using minX, minY, maxX, and maxY simplifies the code a bit.

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

}

As an aside, I’m not setting the mask. If you’re animating, it seems that it might be inefficient to constantly reset the mask every time draw is called. I’d personally just set the background color of the view to .clear. But that’s not relevant to the immediate question.




回答2:


As @Losiowaty suggests, the math is off in that the upper right corner is extending beyond the bounds of the frame. (Not sure why clipToBounds=false doesn't work; but that's just a debug suggestion - not a solution). Try this:

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


来源:https://stackoverflow.com/questions/54496532/parallelogram-view-using-uibezierpath

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!