After a long time of writing, my second book has been published! Developing for Apple Watch is now available from the Pragmatic Bookshelf in both paper and eBook formats! This is a “Pragmatic exPress” book, meaning it’s a shorter look at a specific technology. It’s also available on Amazon if you’d prefer it that way.
The book introduces WatchKit, Apple’s technology for making Apple Watch apps. With 100% of its code in Swift, you’ll be ready to go with the latest Apple technology. Get it now and get a head start on making watch apps before WWDC!
Today was MobiDevDay in Detroit, and it was a heck of a good conference (and I’m not just saying that because Detroit Labs helped put it on). The slides from my presentation are on Speaker Deck, and you can see AmazeKit on GitHub or view its documentation. Enjoy!
So I wouldn’t recommend using this in a shipping application, but I wanted to see if it worked. I created a simple app that loads a nib from a website, then tries to initialize a view controller’s view using it. You can view the whole project on GitHub, but here’s the relevant code:
Would I recommend using this in a shipping app? Absolutely not, given Jury’s recommendations. But it is an interesting idea for enterprise, in-house, or jailbreak apps, and I can see the possibility for some very cool stuff to come out of it.
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 self a -setNeedsLayout message. This avoids having to do it in other classes, and only does it when necessary. We simply set it and forget it.
Due to popular demand, I’ve updated my GCD example from previous talks to include a few things to make the example not only do something on a background queue, but also snappy. It should scroll much better now. A quick rundown of what changed:
Images are now resized. Since the example uses wallpaper-sized images, there’s no sense in not resizing them to go on a 44-pixel-tall table view cell. I’m using the popular image-resizing routines from Trevor’s Bike Shed to do the resizing with a nice interpolation quailty.
Those resized images are now cached. I use an NSCache to store the images. If the app receives a memory warning, it’ll jettison all of the cached images, but if you’re just scrolling up and down this is a quick and dirty way to cache the images. I had never really used NSCache before, so this was a good excuse to try it.
I’m at CocoaConf in that state down to the South today, so this post has been brought to you by late-night hotel room caffeine. I made some other changes to the project to deal with a weird table view cell bug that I’ve submitted to Apple; a post on that is coming up next!