I am trying to build an universal application with Xcode 4. However, it seems a little different from past versions.
My project utilizes the View Based Application template. My issue is that I added a UIViewController subclass which has one nib file for iPad. How do I create another nib file with the same class targeted instead for iPhone? Also, how do I ensure that the proper nib file is loaded for the proper platform?
EDITED :
here is my code :
- (IBAction)BookView {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
BookViewController *viewController = [[BookViewController alloc] initWithNibName:#"BookViewController" bundle:nil];
[self presentModalViewController:viewController animated:YES];
} else {
BookViewController *viewController = [[BookViewController alloc] initWithNibName:#"BookViewController_iPad" bundle:nil];
[self presentModalViewController:viewController animated:YES];
}
}
Step 1: Create for iPhone:
New file / ios / cocoa touch / UIViewController subclass
uncheck Targeted for iPad
check with XIB
This step will create .m .h and .xib files with same name, for example: CustomView
Step 2: Create new XIB for iPad:
New file / ios / user interface / view
device family iPad
for convenience choose the same name with suffix _iPad (for example CustomView_iPad)
in this xib go to File's Owner, in the inspector tabs choose identity inspector, custom class and choose the same name of class created in step 1.
Connect IBOutlets.
In your code use something like this:
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
CustomView *viewController = [[CustomView alloc] initWithNibName:#"CustomView" bundle:nil];
} else {
CustomView *viewController = [[CustomView alloc] initWithNibName:#"CustomView_iPad" bundle:nil];
}
Good luck!
Name your xib file for iPad BookViewController~ipad.xib and the iPhone one BookViewController~iphone.xib. Then load the nib file as usual:
BookViewController *viewController = [[BookViewController alloc] initWithNibName:#"BookViewController" bundle:nil];
[self presentModalViewController:viewController animated:YES];
When the app runs on an iPad device, the ~ipad xib will be automatically loaded. If the app runs on an iPhone, the ~iphone xib will be automatically loaded.
Note that ~ipad and ~iphone suffixes are case sensitive. If you name it ~iPad for instance, you'll get a runtime exception that the nib file is not found.
Related
So I've created an app for iPhone and I wanted to convert it to iPad, by following steps from this answer.
Duplicate your iPhone-Storyboard and rename it MainStoryboard_iPad.storyboard
Open this file any text editor.
Search for targetRuntime="iOS.CocoaTouch"and change it to targetRuntime="iOS.CocoaTouch.iPad"
Now save everything and reopen Xcode -> the iPad-Storyboard contains the same as the iPhone-file but everyting could be disarranged
Everything is done correctly but iPad simulator/device anyways uses iPhone storyboard. Any suggestions?
I've set iPad storyboard in summary->ipad deployment info->Main storyboard. And main.plist-> Main storyboard file base name (iPad) is set to iPad storyboard.
Please tell me what I am missing.
UPD. Interesting Thing, when I delete iPad storyboard name from ipad deployment info it still uses my iPhone storyboard on device.
You could always pick the proper storyboard in the appDelegate and present the appropriate root view controller programatically
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
UIViewController *rvc;
}
Implementation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"IPAD_Storyboard" bundle:nil];
rvc = [storyboard instantiateViewControllerWithIdentifier:#"identifierForController"];
}
else {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
rvc = [storyboard instantiateViewControllerWithIdentifier:#"identifierForController"];
}
[self.window addSubview:rvc.view];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Dont forget to add following things in project's info.plist file (Main Storyboard file base name/ Main Storyboard file base name (iPad))
Hope this helps.
I have a universal app in which I'm loading my main storyboard manually in application:didFinishLaunchingWithOptions.
I have 2 storyboards for iPhone and iPad which have the ~iPhone and ~iPad suffixes. I'm loading my storyboard using:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
self.initialViewController = [storyboard instantiateInitialViewController];
This prints Unknown class ViewController in Interface Builder file. to the console, so apparently it's not loading the correct storyboard. However, when I use [UIStoryboard storyboardWithName:#"MainStoryboard~iPhone" bundle:nil]; it works fine, but of course will work only for iPhone.
What am I missing? How can I use the name suffixes to automatically select the correct storyboard?
I am not aware of any automatic selection of storyboards based on their filename suffix. You can use userInterfaceIdiom to select for iPad vs iPhone:
if ([[UIDevice currentDevice] userInterfaceIdiom] ==UIUserInterfaceIdiomPad) {
UIStoryboard *storyboard =
[UIStoryboard storyboardWithName:#"MainStoryboard_iPad" bundle:nil];
} else {
[UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
}
But if you are doing this to start up with a specifc view controller, all you need to do is drag the 'start' arrow to your preferred view controller in the storyboard
Or - select the view controller in the storyboard, go to the attributes instpector and tick isInitialViewController
This is another thing you can set directly within your info.plist file. No need for any programming efforts. Look for the property named 'Main storyboard file base name' that will have 'Main' in it by default.
You can add another property named 'Main storyboard file base name (iPad)' that will then get used for iPad.
This is what the raw output in the plist looks like:
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIMainStoryboardFile~ipad</key>
<string>iPad</string>
Afaik it is also possible to simply add a second storyboard named Main~iPad.storyboard (if the UIMainStoryboardFile key is set as Main). And that will get picked up for iPads. Haven't tested this in a while though.
// In appdelegate class, while launching application select specified story board.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIStoryboard *storyboard1;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
storyboard1 = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:[NSBundle mainBundle]];
}
else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
storyboard1 = [UIStoryboard storyboardWithName:#"Main_iPad" bundle:[NSBundle mainBundle]];
}
UIViewController *vc = [storyboard instantiateInitialViewController];
// Set root view controller and make windows visible
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
return YES;
}
You can name your storyboard like this
Main.storyboard (for iPhone)
Main_iPad.storyboard (for iPad)
and select them like this
- (UIStoryboard *)deviceStoryboardWithName:(NSString *)name bundle:(NSBundle *)bundle {
if (IS_IPAD) {
NSString *storyboardIpadName = [NSString stringWithFormat:#"%#_iPad", name];
NSString *path = [[NSBundle mainBundle] pathForResource:storyboardIpadName ofType:#"storyboardc"];
if (path.length > 0) {
return [UIStoryboard storyboardWithName:storyboardIpadName bundle:bundle];
}
}
return [UIStoryboard storyboardWithName:name bundle:bundle];
}
I am developing an application. I am creating the Universal Application using the Single View Based application model. So, I need to create a new class. But, it gives only a single xib. I need two xibs for iPhone and iPad. Please tell me how to create the two xibs for a single class.
Create a new one with same name .. lets suppose your view controller name is "NewViewController" .. your xib will be NewViewController~ipad for the iPad and NewViewController~iPhone for the iphone .. so when you implement initWithNibName just write the basic name for you xib which is NewViewController and the iOS will take care about calling the match xib based on current used platform .. and don't forget to assign the custom class for the file owner in the new xib to be your new class like the image below.
For Create new xib check these images :
Malek_Jundi has a clear guide on how to create and load .nib file for iphone and ipad.
If you want to create different class for each case (iphone or ipad), you can use IF statement like this:
UIViewController *target;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
target = [[NewViewController alloc] initWithNibName:#"NewViewController" bundle:[NSBundle mainBundle]];
} else {
target = [[NewViewController_ipad alloc] initWithNibName:#"NewViewController" bundle:[NSBundle mainBundle]];
}
But I am too lazy to input "IF" statement repeatedly in my code to create specific class for iphone/ipad. I have an alternative way:
- (Class)idiomClassWithName:(NSString*)className
{
Class ret;
NSString *specificName = nil;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
specificName = [[NSString alloc] initWithFormat:#"%#_ipad", className];
} else {
specificName = [[NSString alloc] initWithFormat:#"%#_iphone", className];
}
ret = NSClassFromString(specificName);
if (!ret) {
ret = NSClassFromString(className);
}
return ret;
}
- (void)createSpecificNewController
{
Class class = [self idiomClassWithName:#"NewViewController"];
UIViewController *target = [[class alloc] initWithNibName:#"NewViewController" bundle:[NSBundle mainBundle]];
//...
}
I am trying to convert my iPhone only application to a Universal application. I switched the devices to Universal and let Xcode do it's thing making a MainWindow-iPad.xib for me, and now when I run the app in the iPhone simulator it works fine, but when I run it in the iPad simulator I get a white screen and the Application windows are expected to have a root view controller at the end of application launch error. I have read some other posts about this same problem but none of them are just limited to one device.
Here is my application:didFinishLaunchWithOptions: method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
/* some dropbox setup stuff */
// INIT VIEW AND CORE DATA
RootViewController *rootViewController = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:nil];
NSManagedObjectContext *context = [self managedObjectContext];
if (!context) {
// Handle the error.
}
rootViewController.managedObjectContext = context;
UINavigationController *aNavigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
self.navigationController = aNavigationController;
[_window addSubview:[_navigationController view]];
[_window makeKeyAndVisible];
[rootViewController release];
[aNavigationController release];
return YES;
}
EDIT: I just have one root view controller that is sized for iPhone called RootViewController. But it should still load in shouldn't it? Or if it shouldn't how do I create one for iPad?
Change the following line:
[_window addSubview:[_navigationController view]];
to:
_window.rootViewController = _navigationController;
or, if you need iOS 3 compatibility:
if ([_window respondsToSelector:#selector(setRootViewController:)]) {
_window.rootViewController = _navigationController;
} else {
[_window addSubview:_navigationController.view];
}
You need to create a RootViewController with the xib file for iPad, otherwise you will get this error. Below are the template code provided by Xcode for universal app. If you debug the app in iPad simulator and point the debugger to run create the view controller with iPhone xib file, you will see the exact error.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.viewController = [[SYKViewController alloc] initWithNibName:#"SYKViewController_iPhone" bundle:nil];
} else {
self.viewController = [[SYKViewController alloc] initWithNibName:#"SYKViewController_iPad" bundle:nil];
}
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
In iOS 4 and later, UIWindow has the settable property rootViewController. This is the UINavigationController that pushes the UIViewController displayed on application launch. In Xcode IB, selecting Initial Scene: Is initial view controller for the UINavigationController sets everything up with no code required.
From the generated MainWindow-iPad.xib, in Interface Builder, add a View Controller object in IB, as well as an Object underneath the View. For the object, set it's class to AppDelegate, for the View Controller, set the class to ViewController (i the Identity Inspector) and specify the nib name in the Attributes Inspector. You can look at the MainWindow.nib for the device you were converting from to see the differences.
Edit: I forgot to mention some important steps. You will also need to set the File's Owner class to "UIApplication" in IB, and set the referencing outlets appropriately for the App Delegate and View Controller. Again, in IB, it's easiest to look at the Connections Inspector for the MainWindow nib you had and emulate it. If you have another nib specific to the iPad, other than MainWindow-iPad.nib (i.e. ViewController-iPad.nib), be sure to select it's File's Owner and point it to the view and set it's class appropriately too.
I tried your suggestions but none of them worked for me, sorry. :/ I ended up just making the view manually in code without interface builder:
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// Setup view for iPad
} else
// Setup view for iPhone and iPod Touch
}
which I thought would be a lot harder than it actually was.
Note that if you use this method you can still hook up everything in interface builder and just change the frame of objects in these blocks if you are going to have the same objects on both iPhone and iPad.
I am using XCode 4.0.
I am Transiting from my iPhone App to IPad as a universal app?
I have some doubts for xib files?
Should I use the autoresize element for each UIElement?
Or
Trasit each xib file to the ipad xib file?
How? Could not get the option for that?
.
Use different .xib files for iPad and iPhone and initWithNibName:bundle:. Determine whether to use one or another using this block
NSString *nibName = nil;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
nibName = #"CustomViewControllerIpad";
}
else {
nibName = #"CustomViewControllerIphone";
}
CustomViewController *viewController = [[CustomViewController alloc] initWithNibName:nibName bundle[NSBundle mainBundle]];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];