Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.


Optimizing setNeedsDisplayInRect and drawRect

While working on my scrollview I added some debug code to the NSView drawRect method. And I could not help but noticing that drawRect gets called way more than necessary.

This is due to the way I use notifications to update the GUI if changes to the datamodel are made. I think that the way I work is pretty normal:
  • an update to the data model is made,
  • a notification is raised so that the GUI knows that an update is needed,
  • the GUI calculates which part of the view must be updated,
  • and uses setNeedsDisplayInRect to request the update
So far so good, but I had assumed that drawRect would not be called for the part of the GUI that is outside the visible area. Not true. As far as I can see the drawRect method is called for every  setNeedsDisplayInRect that is issued. Usually this is not important since the GUI won't be updated if the area is invisible anyway. Though there might be applications where this is an issue, this is not the case in my app. However we do waste precious (?) CPU cycles by going through all these motions. It would be way more efficient to prevent updates to non-visible portions of the GUI before the call to drawRect.

Luckily this is simple, just override setNeedsDisplayInRect to prevent updates to non-visible areas.

For my view I used the following:

    override func setNeedsDisplayInRect(invalidRect: NSRect) {
        let reqRect = visibleRect.intersect(invalidRect)
        if reqRect.size.width > 0 && reqRect.size.height > 0 {


Caveat: Since the GUI outside the visible view is not updated, any scrolling will display old data until the drawRect is called by the system. Which is after you release the mouse. If this is an issue, don't use this optimization.

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

No comments:

Post a Comment