48

You can use a device modifier (i.e., ~ipad) to provide a device-specific key in Info.plist, and to specify a device-specific launch image (Default.png for iPhone, and Default~ipad.png for iPad, for example). Those two things are specifically mentioned in Apple Docs, but they don't say that this will work for any other kinds of files.

I've discovered (quite by accident) that this works for loading .xib files via initWithNibName:bundle:. So for example, I can have MyView.xib and MyView~ipad.xib, and this code:

MyViewController *viewController = [[MyViewController alloc] 
                                     initWithNibName:@"MyView" bundle:nil];

... will totally load MyView~ipad.xib on an iPad, and MyView.xib on other devices.

So, 1) Is this documented somewhere? I sure couldn't find it any any Apple docs. It's sure handier than checking UI_USER_INTERFACE_IDIOM() and hardcoding two different nib names everywhere, but I kinda don't trust it if it isn't documented.

And, 2) Does anyone know what version of iOS this started working in? I've only tried it in 4.2, and it works there. Device modifiers in general (even for the documented things listed above) are 4.0 minimum.

zpasternack
  • 17,838
  • 2
  • 63
  • 81
  • 1
    Good spot. Pretty sure this is 4.0 onwards, certainly for universal apps; although perhaps this worked on 3.2 on iPad? Looking back at bundle programming guide, it does always use images as examples for resource loading, but reading between the lines again, it might be that all resources loaded though the app bundle drop-out through the same mechanism? – petert Mar 04 '11 at 08:54

4 Answers4

49

I had this same problem. The answer didn't make sense at first, but the good news is that it's easy to do! :)

Just name your iPad xibs without any modifier and your iPhone xibs with ~iphone modifier and it'll select them correctly.

So, with MyViewController, you'll have MyViewController.xib for the iPad and MyViewController~iphone.xib for the iPhone. Then you can just init your view controller with simple alloc/init.

[[MyViewController alloc] init] and it'll grab the right xib.

So, when I create a new view controller in XCode, I always choose the box to format it for ipad, because the xib it will create will be named MyViewController.xib and you want that one to be the iPad sized xib. Then I create a second xib, formatted for iPhone and name it with the ~iphone modifier.

The documentation is a little contradictory at times, but this page talks about how resources with an identifier will default to iPad.

ImageSoundResources

Check the section about using high res images. I know we're talking xibs and not images, but it does work. My last 6 apps have all used this idiom.

David Snabel-Caunt
  • 57,804
  • 13
  • 114
  • 132
Kenny Wyland
  • 20,844
  • 26
  • 117
  • 229
  • 1
    That's all good, but my point is, nowhere in any official documentation does it mention that resource files in general use the device-modifier idiom. I was hoping to see official documentation regarding the behavior, and/or some official documentation on minimum iOS versions. – zpasternack Mar 06 '11 at 04:40
  • I feel you, I was never able to find anything in the documentation that explicitly said this for xibs, but when I found the above linked documentation about Images I decided to give it a try hoping that it would also apply to xibs... and viola, it did... and dev has been a breeze ever since. I've been using it since 4.0. Sorry, best I can give you! – Kenny Wyland Mar 06 '11 at 17:02
24

Actually, it is explicitly defined in the docs, but as a footnote.

CocoaNibs

In the note at the bottom of "Loading NIB files using NSBundle":

Note: If you are developing a Universal application for iOS, you can use the device-specific naming conventions to load the correct nib file for the underlying device automatically. For more information about how to name your nib files, see “iOS Supports Device-Specific Resources.”

Which links to Cocoa Conceptual LoadingResources

However, yes, this is a 4.0+ only feature.

NANNAV
  • 4,875
  • 4
  • 32
  • 50
Caleb Shay
  • 2,531
  • 1
  • 19
  • 13
3

I hate to be that guy and answer my own question, but I think the answer is:

1) Nope, not explicitly documented in any Apple documentation, and
2) 4.0 and higher (this based on my own testing)

All you really save is a couple lines of code checking for UI_USER_INTERACE_IDIOM(). Still, I'll take it. Less code is less code.

zpasternack
  • 17,838
  • 2
  • 63
  • 81
2

The appropriate technique to use in iOS 3.2 and later is the UI_USER_INTERFACE_IDIOM() function. I typically use a ternary operator to init the UIViewController with the appropriate XIB.

UIViewController* controller = [[UIViewController alloc] 
    initWithNibName:UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? 
    @"YourViewController-iPad" : @"YourViewController" andBundle:nil];
Wayne Hartman
  • 18,369
  • 7
  • 84
  • 116
  • 2
    iOS has built in naming scheme so you don't need to include this ternary conditional stuff. You can use a simple alloc/init if you name the xibs correctly. – Kenny Wyland Mar 04 '11 at 19:08
  • 1
    Right, using ternary operators is a good way to go, but if just naming xibs properly does the same job, I'd rather do that. I guess I'm saying, doesn't really answer my question. – zpasternack Mar 06 '11 at 03:57