How to create the new UIViewController in a Universal Application - iphone

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]];
//...
}

Related

Selecting different storyboards based on device type

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];
}

How can I make my iPhone app into a universal app (iPhone & iPad compatible)?

I have been looking around at tutorials on how to make an iPhone app universal, so that it work on iPad. However when I have tried to implement this I've become a bit stuck since Xcode has changed a fair amount and the tutorials seem out of date. So far I have done the following:
I updated the devices to be set to Universal:
I have also updated my app delegate class like so:
// Set up tab 1
TabOne_ViewController *tabOne = [TabOne_ViewController alloc];
UIViewController *tabOneViewController;
// Set up tab 2
TabTwo_ViewController *tabTwo = [TabTwo_ViewController alloc];
UIViewController *tabTwoViewController;
// Determine which UI to load for each tab
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
tabOneViewController = [tabOne initWithNibName:#"TabOne_ViewController" bundle:nil];
tabTwoViewController = [tabTwo initWithNibName:#"TabTwo_ViewController" bundle:nil];
}
else
{
tabOneViewController = [tabOne initWithNibName:#"TabOne_ViewController(iPad)" bundle:nil];
tabTwoViewController = [tabTwo initWithNibName:#"TabTwo_ViewController(iPad)" bundle:nil];
}
I have also created two extra .xib files:
TabOne_ViewController(iPad).xib
TabTwo_ViewController(iPad).xib
Finally I tried to set the class on TabOne_ViewController(iPad).xib to be TabOne_ViewController.h & TabOne_ViewController.m. And also TabTwo_ViewController(iPad).xib to be TabTwo_ViewController.h & TabTwo_ViewController.m. However I wasn't able to do so.
Have I gone wrong somewhere? Are there extra steps I have missed out?
EDIT:
When I was referring to not being able to select the class I mean in IB:
Without being able to link the class to the .xib file I can't link up all of the IBOutlets & IBActions.
In the xib you tried to change the class of the View. But you have to change the class of File's Owner
you go to target click on project,give in summery:
Device: "Universal"
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
tabOneViewController = [tabOne initWithNibName:#"TabOne_ViewController" bundle:nil];
tabTwoViewController = [tabTwo initWithNibName:#"TabTwo_ViewController" bundle:nil];
}
else
{
tabOneViewController = [tabOne initWithNibName:#"TabOne_ViewController(iPad)" bundle:nil];
tabTwoViewController = [tabTwo initWithNibName:#"TabTwo_ViewController(iPad)" bundle:nil];
}
Instead of this code you try this code as follows:
UIDevice* thisDevice = [UIDevice currentDevice];
if(thisDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)
{
// iPad
tabOneViewController = [tabOne initWithNibName:#"TabOne_ViewController(iPad)" bundle:nil];
tabTwoViewController = [tabTwo initWithNibName:#"TabTwo_ViewController(iPad)" bundle:nil];
}
else
{
// iPhone
tabOneViewController = [tabOne initWithNibName:#"TabOne_ViewController" bundle:nil];
tabTwoViewController = [tabTwo initWithNibName:#"TabTwo_ViewController" bundle:nil];
}

Questions about Universal application in Xcode 4

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.

IPhone App to IPad App How to make transition from ViewController.xib to ViewController-iPad.xib ?

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];

When should I initialize a view controller using initWithNibName?

When should I use init: and when should I use initWithNibName:bundle: when creating a view controller?
-initWithNibName:bundle: is the designated initializer for UIViewController. Something should eventually call it. That said, and despite Apple's examples (which favor brevity over maintainability in many cases), it should never be called from outside the view controller itself.
You will often see code like this:
MYViewController *vc = [[MYViewController alloc] initWithNibName:#"Myview" bundle:nil];
I say this is incorrect. It puts implementation details (the name of the NIB and the fact that a NIB is even used) into the caller. That breaks encapsulation. The correct way to do this is:
MYViewController *vc = [[MYViewController alloc] init];
Then, in MYViewController:
- (instancetype)init
{
self = [super initWithNibName:#"Myview" bundle:nil];
if (self != nil)
{
// Further initialization if needed
}
return self;
}
- (instancetype)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
NSAssert(NO, #"Initialize with -init");
return nil;
}
This moves the key implementation details back into the object, and prevents callers from accidentally breaking encapsulation. Now if you change the name of the NIB, or move to programmatic construction, you fix it in one place (in the view controller) rather than in every place the view controller is used.
Use initWithNibName: bundle: if you are... initializing with a nib file! That is, a file that you made using Interface Builder.
If you aren't using IB to layout your views, you can just use init.
You can just call init, as long as the xib has the same name as the view controller class. The encapsulation is not necessary. This saves typing, but may not serve clarity.
NUDMainViewController *mainVC = [[NUDMainViewController alloc] init];
using init when there is no nib/xib file, e.g. UI are created by coding
using initWithNibName , if we have an nib/xib or same controller share by more than 1 nib/xib
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController_iPhone" bundle:nil];
} else {
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController_iPad" bundle:nil];
}
that's what I think..