iOS 12, Xcode 10: UIView setNeedsDisplay(_:) seems to be broken

After updating to Xcode 10.0 I realized that the draw(_ rect: CGRect) routine of my custom UIView (class derived from UIView) in my application was called with the wrong rect. Indeed it is always called with rect being the full frame of the underlying UIView, instead of the rect being specified by setNeedsDisplay(_ rect: CGRect).


Here is a code snippet that can be run as a playground, which at least in my setup shows the erroneous behavior described above in a minimalistic setting:


import Foundation
import UIKit
import PlaygroundSupport

class CustomView: UIView {
    override func draw(_ rect: CGRect) {
        print("rect = \(rect)")
    }
}

let customView = CustomView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: 200.0, height: 200.0)))
PlaygroundPage.current.liveView = customView
print("test")
customView.setNeedsDisplay(CGRect(origin: CGPoint.zero, size: CGSize(width: 100.0, height: 100.0)))


The output I get is

rect = (0.0, 0.0, 200.0, 200.0)
test
rect = (0.0, 0.0, 200.0, 200.0)


The first printed output for rect is the standard full redraw of the view, but the second one after printing "test" shows the problem. This second output is from redrawing due to the customView.setNeedsDisplay call and should be the smaller specified rectangle (0.0, 0.0, 100.0, 100.0).


So my obvious questions are:

  • Can you reproduce this behavior?
  • Am I missing something obvious?
  • Is this a bug?

Replies

Tested an app with

    @IBAction func testRedraw(_ sender: UIButton) {
        print("Test redraw")
        testView.setNeedsDisplay(CGRect(x: 20, y: 20, width: 100, height: 100))
    }


Tested in an app with XCode10, target IOS12, I observe the same behavior.

drawRect (0.0, 0.0, 240.0, 128.0)

Test redraw

drawRect (0.0, 0.0, 240.0, 128.0) // INCORRECT


Tested also with Swit set to Swift 3 and IOS 11. Same behavior.


Edit with a project created in XCode 9 : NOT the same behavior

drawRect (0.0, 0.0, 240.0, 128.0) // Initial draw

Test redraw

drawRect (20.0, 20.0, 100.0, 100.0) // CORRECT


So, that's a difference in XCode10, looks **** a bug.


Did you file a bug ? I have a small template project that can be used fopr bug report if needed.

And the bug does affect drawing in curious way.


I tested with the following draw


class TestView: UIView {

    var penColor = UIColor.clear    // blue // Will be changed by chartAction

    override func draw(_ rect: CGRect) {
        print("drawRect", rect)
        let aPath = UIBezierPath(ovalIn: rect)
        penColor.setFill()
        aPath.fill()
    }

}


And call the redraw with IBAction in the viewController:

    @IBAction func testRedraw(_ sender: UIButton) {
        print("Test redraw")
        testView.penColor = .red
        testView.setNeedsDisplay(CGRect(x: 20, y: 20, width: 100, height: 100))
    }


In Xcode 9.4, calling redraw draws a correct oval in red.

If I set

var penColor = UIColor.blue

then the red circle is drawn inside a yellow square over the blue initial oval.


In XCode 10

draws a square of the correct rect, clipped inside the global oval (should be better if I could post images).


So, it is just as if:

- the rect passed by setNeedsDisplay is not taken into account (see the result of print("drawRect", rect))

- so the redraw uses the global rect of the view

- But the rect from setNeedsDisplay is used for clipping (is it done at the call to setNeedsDisplay ?)


Filed a bug report 45016217

Thanks for filing the bug. I was waiting for replies to this thread first. I appreciate your feedback.

I will post information if I receive feedback. It is effectively an annoying bug.


I think you can now close the thread.

Does not hurt to stay open I guess. It is such a major bug, that it hopefully will get Apples attention very soon and maybe someone has a workaround or something that we are missing right now.

Thanks again.

The bug has been marked as. DUPLICATE OF 44513644


So, unfortunately I will not get more information.

Thanks for the feedback. Good thing someone noticed the bug before.

Tested with XCode 10.1 beta2, not corrected yet.

I also have this problem, running on the iOS10 device is no problem. But on iOS12, the return of the setNeedsDisplay(_ rect: CGRect) in draw(_ rect: CGRect) has been wrong. How to solve it?

Me too. This question is too annoying.

I have this too and its caused my games of mine to crash in ios 12. I hope it gets fixed soon or at least a workarand. thanks

Same problem here, drawRect always contain the full rectangle.

Tested with XCode 10.1 beta 3: Still not fixed!

It is getting worrisome. Still not fixed in the final release of XCode 10.1. I think I should file a bug report, although this has been reported before.

Did file another bug report, hope this gets the problem fixed.