Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
157 views
in Technique[技术] by (71.8m points)

ios - Correctly setting corner radius to Rects drawn with Gore Graphics

I want to implement a bar constisting of colored rectangles to visualise a distribution, similar to the storage graphic on the iPhone.

My current approach is this, which is working correctly:

override open  func draw(_ rect: CGRect) {
    
    let context = UIGraphicsGetCurrentContext()
    drawRects(context!)

}

func drawRects(_ context:CGContext) {
    if values.count == 0 { return }
    
    var currentX: Float = 0
    let scale = (Float(self.frame.width)) / Float(totalSum)
    
    for i in 0..<values.count {
        let color = colors[i].cgColor
        let value = values[i]
        
        let rect = CGRect(x: CGFloat(currentX), y: 0, width: CGFloat(Float(value) * scale), height: frame.size.height)

        context.addRect(rect)
        context.setFillColor(color)
        context.fill(rect)
        
        currentX += ((Float(value) * scale) + (value == 0 ? 0 : barSpacing))
    }
}

The result is this: enter image description here

Now I want to round the edges of the individual rectangles with UIBezierPath using this code at the end of the for-loop in the drawRects method:

let path = UIBezierPath(roundedRect: rect, cornerRadius: rect.height / 2) 
UIColor.white.setStroke()
path.stroke()

This leads to this:

enter image description here

Somehow this doesn't just round the edges, but cuts off a bit of the height and width, which leads to the small leftovers of the rectangle in the corners. As you can see in the two images, the height of the rectangles changes when applying the path.

Setting the UIBezierPath as a path to the current Core Graphics context instead of setting it as a stroke leads to the same problem.

What is it that I'm doing wrong?

question from:https://stackoverflow.com/questions/65885097/correctly-setting-corner-radius-to-rects-drawn-with-gore-graphics

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Disclaimer: Did this without testing.

I do believe the way you are filling in your context is incorrect. So, you are filling via context.fill(rect), which does exactly as you are stating. Fill the rect, everything except the path. Well, the path doesn't interfere with the corner, thus your result.

To counter this, we utilize the method context.fillPath() which provides the default rule, CGPathFillRule = .winding. It should only fill the interior of the `rect.

https://developer.apple.com/documentation/coregraphics/cgpathfillrule

CGPathFillRule: When filling a path, regions that a fill rule defines as interior to the path are painted. When clipping with a path, regions interior to the path remain visible after clipping.

override open  func draw(_ rect: CGRect) {
    
    let context = UIGraphicsGetCurrentContext()
    drawRects(context!)

}

func drawRects(_ context:CGContext) {
    if values.count == 0 { return }
    
    var currentX: Float = 0
    let scale = (Float(self.frame.width)) / Float(totalSum)
    
    for i in 0..<values.count {
        let color = colors[i].cgColor
        let value = values[i]
        
        let rect = CGRect(x: CGFloat(currentX), y: 0, width: CGFloat(Float(value) * scale), height: frame.size.height)

        let path = UIBezierPath(roundedRect: rect, cornerRadius: rect.height / 2) 
        context.addPath(path.cgPath)

        context.addRect(rect)
        context.setFillColor(color)
        context.fillPath()
        context.closePath()

        currentX += ((Float(value) * scale) + (value == 0 ? 0 : barSpacing))
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...