In your career as a Cocoa or Cocoa Touch developer, every now and then you’ll encounter an issue with something Apple has written. Whether it’s a full-blown bug, something that doesn’t work quite how you’d expect it to, or a minor inconvenience, it happens. When it does, naturally the first thing you do is file a bug report (right?). After that, though, you need to do something about it. This usually occurs right when a project is due, so often we can’t wait for Apple’s engineering teams to fix the problems (or tell you that you’re wrong). This post is an example of using KVO to get around the problem without worrying about it anymore.
The Problem: In iOS, if you create a
UITableViewCell and return it to the table view in its data source’s
-tableView:cellForRowAtIndexPath: method, but then return later (say, after doing some background processing) to add an image to the cell’s
imageView, you don’t see anything! Why? Well, it looks like either the image view isn’t added to the cell’s view hierarchy if you don’t immediately add an image or there’s some other bug in the
UITableViewCell implementation. I don’t think it’s a bug, I think it’s just a side effect of an optimization; if there’s no image, why add it to the cell?
So how do we fix it? Well, a simple call to
-setNeedsLayout gets the cell to fix itself quite nicely. But we shouldn’t have to do that from our table view data source—that has a bit of code smell to it. Lines like that quickly get overused, with programmers calmly stating, “I don’t know why, but we always do that.” No, a better solution is to get the cell to handle this problem on its own.
We’ll create a subclass of UITableViewCell and use KVO. When we create the cell, we’ll register for KVO notifications with the on the image view whenever its
image property is modified—but we’ll send the option to include the old value in the change dictionary. When we receive the notification, we’ll look at that dictionary, and if the old value was
nil, then we’ll send
-setNeedsLayout message. This avoids having to do it in other classes, and only does it when necessary. We simply set it and forget it.