Search code examples
iosswiftxcode10ios12

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


After updating to Xcode 10 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" yields the problem. The output there is from redrawing due to calling customView.setNeedsDisplay before 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?

Solution

  • I tested this in Xcode 9, 10 & 10.1.

    The behaviour has definitely changed between iOS 11 and iOS 12 / 12.1

    There's no indication in the documentation or header file that this was intentional.

    Looks like a bug to me.