Well, it finally happened. Apple is releasing an iPhone with a different screen size and aspect ratio. “Oh no!” everyone screamed. iOS is headed down the path of Android! Fragmentation is here!! OK, there’s probably (almost ) no one actually saying that. Yes, there’s a new device, but the fragmentation situation for Apple – even including the various combination of screen size, processor type, iOS version, etc. – doesn’t even come CLOSE to that of Android. The fact is, Apple know what they’re doing. Agree or disagree with their reasoning for the iPhone 5’s new screen, it’s clear that they don’t take this type of change lightly. I mean, just listen to how Sir Jonathan Ive opens this video. How can you not agree with everything this man ever says?!
The first big “disruption” to the iOS ecosystem, was of course, the iPad. But guess what, Apple took careful measures to make sure that every single existing app would automatically run. I’m sure there were examples of apps that didn’t take to the simulated iPhone environment perfectly, but for the most part, everything just worked. And that was no small feat of engineering, I’m sure! Yes, we were all pushed to build native apps for the iPad, but they weren’t going to immediately alienate thousands of developers and millions of app users just because they decided to make a cool new device. The next disruption was iPhone 4 and Retina Display. Once again, great new technology for those who incorporate it, but in the meantime, existing apps for the most part ran just fine. And now, last week, the 4″ screen of the iPhone 5. But guess what, all existing apps will run just fine, only letter-boxed. But what about those who want to take advantage of this new technology, to create an app that makes full use of that new tall screen?
One Way to Adapt
Now, I’ve got to be careful here. The iPhone 5 and its specs themselves are public knowledge, but iOS 6 is still under NDA for a little while more. However, what I’ve got to share here is not necessarily specific to the iPhone 3.5″/4″ display divide. It’s just as applicable to a Universal iPhone/iPad app. Or, to an app that needs to deal with all three screen sizes. (Retina/non-retina is a related issue, but not quite the same.) Let’s say we’ve got a custom UIView subclass which is going to display some content. Now, it’s very likely that you’ll be using separate .xib files for the different interfaces, especially in the case of an iPhone/iPad universal app. In that case, of course, you’ll simply configure each interface file for the correct dimensions and position for the UIView. But what about an iPhone 5 app that shares a .xib? Or an app that does more programmatic creation of views? First strategy: try to get the frameworks to do the work for you. Make use of the springs and struts for autoresizing, and let iOS take care of making sure everything lays out nice and neat, no matter what the view size. (Better yet, use the new Auto Layout features of iOS 6.0. That name’s not under NDA, right? It was on the Keynote slides, I swear!) If the view subclass knows how to draw its contents at various sizes, and the superview can resize it appropriately, then your work is done. But, what if these options just don’t cut it, and it doesn’t look quite right? I’ve got this view, and I want it here on an iPad but here on the iPhone. Or, I need it exactly this tall on the 3.5″ screen, and this tall on the 4″ one. This is the situation I found myself in sometimes when updating Click, so I thought I’d share what I’m doing. Again, there are other ways to accomplish the same thing, but I found this one helpful.
First, you have to find a way to decide what device your running on and how you’re going to store this: BOOL, custom enum for 3 or more device types, etc. For this example, we’ll assume we just want to know between a 3.5″ or a 4″ screen and that we’re doing a check one time immediately when the app launches and storing it in a BOOL called “isiPhoneFive” which we can access anywhere else later on in the program. We could start littering our code with conditionals like this:
if (isiPhoneFive) myView.center = CGPointMake(10.0, 60.0); else myView.center = CGPointMake(10.0, 34.0)
Not bad, but tedious and very repetitive. In the case of a simple BOOL choice, this can be cleaned up by using the ternary operator:
myView.center = CGPointMake(10.0, ((isiPhoneFive) ? 60.0 : 34.0));
It’s a little better by being more concise, but it’s still repetitive, and that ternary operator is strange and a bit awkward to type out accurately and quickly. Plus, if you have more than two options to choose from, it gets messy trying to nest them together. A better option, in my opinion is to use a preprocessor macro to do the work for you. By adding this #define somewhere in a header file that will be visible throughout the project, you can turn this repetitive decision-making code into a much smaller, reusable snippet.
#define DeviceSpecificSetting(iPhone, iPhoneFive) ((isiPhoneFive) ? (iPhone) : (iPhoneFive))
Now, we can do something like this:
myView.center = CGRectMake(10.0, DeviceSpecificSetting(60.0, 34.0)
We could make this a regular C function that would return the correct value depending on the device, but this would mean choosing a specific variable type ahead of time. Because the #define macro is actually just inserted by the preprocessor as a literal text substitution, we can use the same macro to choose between ints, floats, even strings or full expressions:
UIImageView *myImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed: DeviceSpecificSetting(@"iPhoneImage", @"iPhone5Image")]]; myView.frame = DeviceSpecificSetting(CGRectMake(0.0, 0.0, 100.0, 100.0), CGRectMake(0.0, 0.0, 100.0, 188.0));
Now, if you’re finding your code to be just absolutely riddled with these little hard-coded values for different devices, then perhaps there’s a different/better way you can approach things. But, for those times when this is the way you need to do it, this little macro makes for a nice quick, clear, auto-completable way to program in different settings for different devices/screens. With a little more work – and generous use of parentheses for safety in different contexts – this kind of thing can also be used to decide between three or more different values as well, e.g. iPad, iPhone, iPhone 5. For more in depth, technical goodness on macros, see this Friday Q&A. Have fun, and hope you’re all ready for the new iPhone!