Search code examples
iosuitableviewcore-graphicsuibezierpathdrawrect

UIBezierPath not cleared on redraw


I have custom UIView in which I draw a UIBezierPath. This view is used in a UITableViewCell. So when I scroll, the custom view's bezier path gets redrawn. The problem is that the new path draws over old path drawings (the context isn't cleared properly). I'm calling setNeedsDisplay() on the table cell fetch and I've also set clearsContextBeforeDrawing = true for the view. The only thing that clears up the old drawing is calling context.clear(rect) but this isn't at all ideal since I lose the background (it becomes black).

Any ideas on how to fix this?

class CustomView: UIView {

var percentage: CGFloat = 0 {
    didSet {
        setNeedsDisplay()
    }
}

override init(frame: CGRect) {
    super.init(frame: frame)
    clearsContextBeforeDrawing = true
    contentMode = .redraw
    clipsToBounds = false
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func draw(_ rect: CGRect) {
    super.draw(rect)

    // NOTE: don't really like doing this
    let context = UIGraphicsGetCurrentContext()!
    context.clear(rect)

    let center = CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/2)

    let path = UIBezierPath(arcCenter: center,
                            radius: self.bounds.size.width/2-10,
                            startAngle: 0.5 * .pi,
                            endAngle: (-2.0 * self.percentage + 0.5) * .pi,
                            clockwise: false)

    path.lineWidth = 4
    UIColor.red.setStroke()
    path.stroke()
}
}

This is where my cell and custom uiview gets set.

override func tableView(_ tableView: UITableView, cellForItemAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        let num = list[indexPath.item]
        let percentage = CGFloat(num) / 10
        cell.customView.percentage = percentage // Custom view should get redrawn after this call
        return cell
    }

Solution

  • I think the problem is merely that your drawing is huge and strays outside the cell because your views do not clip to bounds. That's incoherent. Each cell needs to draw just its own content.

    In other words, the overlapping you are seeing has nothing to do with the custom view drawing; it has to do with the cell drawing. You are infecting the neighboring cells with your drawing.

    By the way, you say:

    but this isn't at all ideal since I lose the background (it becomes black)."

    You can fix that by saying self.backgroundColor = .clear in your init.