Dynamically load nib for iPhone/iPad within view controller - iphone

I have converted an iPhone application using the wizard like thing in XCode into a universal app.
It builds fine but obviously looks a bit rubbish in some areas :)
I need to load nibs according to which device is being used. I dont wish to create my view controllers using initWithNib as I already have code to create the controllers with some data (initWithMyLovelyData) which doesnt do anything to do with nib loading.
I know to find out the device you use UI_USER_INTERFACE_IDIOM() so I tried overriding the initWithNibName within the actual view controllers themselves, assuming they get called internally somehow. But it's not working as I guess I am unsure of the syntax.
I have tried
if(ipad..) self = [super initWithNibName:#"MyIpadNib" bundle:nibBundleOrNil];
And that doesnt work :/
EDIT - I know I have massively edited this, made my question a bit more specific after doing some more research - apologies!

Actually, Apple does all this automatically, just name your NIB files:
MyViewController~iphone.xib // iPhone
MyViewController~ipad.xib // iPad
and load your view controller with the smallest amount of code:
[[MyViewController alloc] initWithNibName:nil bundle:nil]; // Apple will take care of everything

EDIT: #Adam's answer below is the correct answer.
To determine which nib to load, do the following, and scrap your initWithMyLovelyData method and use a property to set the data. You should be able to easily move all your init code into the property setter method.
MyViewController *viewController;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
viewController = [[MyViewController alloc] initWithNibName:#"ipadNIB" bundle:nil];
} else {
viewController = [[MyViewController alloc] initWithNibName:#"iphoneNIB" bundle:nil];
}
viewController.myLovelyData = someData;

I just put these two methods in a class called IPadHelper, and use the addIPadSuffixWhenOnIPad method to conditionally pick between two nibs depending on platform
+ (BOOL)isIPad{
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_2){
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad){
return YES;
}
}
return NO;
}
+ (NSString *)addIPadSuffixWhenOnIPad:(NSString *)resourceName{
if([IPadHelper isIPad]){
return [resourceName stringByAppendingString:#"-iPad"];
}
else {
return resourceName;
}
}
see here http://cocoawithlove.com/2010/07/tips-tricks-for-conditional-ios3-ios32.html for more explanation of the first method...

You can have your cake and eat it too. Just add a method that wraps initWithNibName:bundle: and adds your myLovelyData parameter:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil myLovelyData:(id)data
{
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]))
{
// Custom initialization using myLovelyData
//
}
return self;
}

I think it will be better to create C file.
FileHelper.h
#import <Foundation/Foundation.h>
BOOL isIPad();
NSString *addIPadSuffixWhenOnIPad(NSString *resourceName);
FileHelper.m
#import "FileHelper.h"
BOOL isIPad() {
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_2) {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
return YES;
}
}
return NO;
}
NSString *addIPadSuffixWhenOnIPad(NSString *resourceName) {
if(isIPad()) {
return [resourceName stringByAppendingString:#"-iPad"];
}
else {
return resourceName;
}
}

Related

Gamecenter authentication in landscape only Cocos2d with CCLayer for iOS 6

I'm having what seems to be a fairly common problem, but my searches and implementations of solutions have not worked out.
I've built a Cocos2d game that is intended to be landscape only, but needs to access Gamecenter. Gamecenter is working, with portrait mode enabled, but it's also allowing the game to flip to portrait mode too.
I've attempted the following fixes:
Game center login lock in landscape only in i OS 6
GameCenter authentication in landscape-only app throws UIApplicationInvalidInterfaceOrientation
Error in iOS 6 after adding GameCenter to a landscape-only cocos2d app
Cocos 2d 2.0 shouldAutorotate not working?
I believe the problem is that I've built the game using CCLayers instead of UIViewControllers
Example:
MenuLayer.h
#interface MenuLayer : CCLayer <GKAchievementViewControllerDelegate, GKLeaderboardViewControllerDelegate, UINavigationControllerDelegate>{
..my header info..
}
MenuLayer.m
...
-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
-(BOOL)shouldAutorotate {
return [[UIDevice currentDevice] orientation] != UIInterfaceOrientationPortrait;
}
-(void)authenticateLocalPlayer
{
GKLocalPlayer * localPlayer= [GKLocalPlayer localPlayer];
if(localPlayer.authenticated == NO)
{
NSString *reqSysVer = #"6.0";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
{
[[GKLocalPlayer localPlayer] setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError *error) {
if (viewcontroller != nil) {
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] presentModalViewController:viewcontroller animated:YES];
}else if ([GKLocalPlayer localPlayer].authenticated)
{
//do some stuff
}
})];
}
else
{
[localPlayer authenticateWithCompletionHandler:^(NSError *error){
if(localPlayer.isAuthenticated)
{
//Peform Additionl Tasks for the authenticated player.
}
}];
}
}
}
...
Since I've built the game using CCLayers instead of UIViewControllers, what alternatives do I have? Am I correct in assuming that CCLayers don't call use supportedInterfaceOrientations or shouldAutorotate?
Or am I supposed be changing this code somehow to fix the problem:
// Create a Navigation Controller with the Director
navController_ = [[UINavigationController alloc] initWithRootViewController:director_];
navController_.navigationBarHidden = YES;
This frustrated me for awhile too. After digging around for awhile on the 'Net I found a couple of sources and some worked with iOS 6, some with iOS5, but I had to make some modifications so that it worked the way I wanted on both iOS5 and iOS6. This is the code I am using, it works on my iPhone using 5.1 and 6. Note that the Game Center login still comes up in portrait orientation, there doesn't appear to be anything you can do about that. But the rest of the game will remain in landscape mode.
enable portrait mode as a supported orientation in your build settings (info.plist).
Create a new subclass of UINavigationController. Name this class whatever makes sense to you.
In your AppDelegate, include your new custom UINavigationController header file.
In your App Delegate, comment out the original call and instead call your custom class.
That should do the trick. Here is the code from my custom class:
#import <UIKit/UIKit.h>
#interface CustomNavigationViewController : UINavigationController
-(UIInterfaceOrientation) getCurrentOrientation;
#end
And the implementation file:
#import "CustomNavigationViewController.h"
#interface CustomNavigationViewController ()
#end
#implementation CustomNavigationViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// This is required to allow GameCenter to login in portrait mode, but only allow landscape mode for the rest of the game play/
// Arrrgg!
-(BOOL) shouldAutorotate {
return YES;
}
-(NSUInteger) supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
-(UIInterfaceOrientation) preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight; // or left if you prefer
}
-(NSUInteger) application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
return UIInterfaceOrientationMaskLandscape;
else {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
}
-(BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return [[UIDevice currentDevice] orientation] != UIInterfaceOrientationPortrait;
}
-(UIInterfaceOrientation) getCurrentOrientation {
return [[UIDevice currentDevice] orientation];
}
#end
Note that last method getCurrentOrientation isn't required I just put that in there in case I wanted to determine what the current orientation is.
The custom class is called in AppDelegate.m like this: (comment out the original code)
navController = [[CustomNavigationViewController alloc] initWithRootViewController:director];
window.rootViewController = navController;
navController.navigationBarHidden = YES;
[window makeKeyAndVisible];
Hope this helps.

Making a created (programmatically) viewController Rotate

I have made a viewController programmatically and I want to force it to rotate whenever the device is rotated .
in simple viewController you create using the normal way by adding a new file and so there a "shouldAutoRotate" method ..
but in my case it's different especiall that I create this viewController in a viewController!
and I don't want to create a new viewController.
this is the code I used to create the viewcontroller
UIViewController *featuresViewController = [[UIViewController alloc]initWithNibName:#"featrures" bundle:nil];
[featuresViewController setView:[[UIView alloc]initWithFrame:CGRectMake(10, 10, 380, 450 )]];
[featuresViewController.view setBackgroundColor:[UIColor clearColor]];
featuresViewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
featuresViewController.modalPresentationStyle = UIModalPresentationPageSheet;
[self presentModalViewController:featuresViewController animated:YES];
Easier to add this as another answer...
Probably not the best place to do this, but if you're struggling on how to code your FeaturesViewController, it'll be something like this -
.h -
#import <UIKit/UIKit.h>
#interface FeaturesViewController : UIViewController {
// ivar declarations...
}
#end
.m -
#import "FeaturesViewController.h"
#implementation FeaturesViewController
-(id)init {
self = [super initWithNibName:#"FeaturesViewController" bundle:nil];
if (self) {
// other init stuff
}
return self;
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// return YES for whatever orientations you want
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
Then in your main VC, present it like this -
FeaturesViewController *featuresViewController = [[[FeaturesViewController alloc] init] autorelease];
featuresViewController.modalTransitionStyle=UIModalTransitionStylePartialCurl;
featuresViewController.modalPresentationStyle=UIModalPresentationPageSheet;
[self presentViewController:featuresViewController animated:YES completion:nil];
Add following code in you application...
//add following line before #implementation
#interface UIDevice (UndocumentedFeatures)
-(void)setOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated;
-(void)setOrientation:(UIInterfaceOrientation)orientation;
#end
//here you can use following code in any method..i just used here in sample method...
-(IBAction)rotateviewprogramatically
{
**//you can also add this in Viewdidload or Viewwillappear...it will work...**
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
//or
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
}
// Change following code.... add following method in you code...i checked it's working...
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);// don't use this
return YES; // use this...
}
Hope, it will help you...chill
I don't understand what you mean when you say you create the viewController in a viewController - I think where you create it is irrelevant. It's also irrelevant that you create it programmatically, as you still have to have written the code...
In your VC's code, simply implement the normal shouldAutorotateToInterfaceOrientation: method (this example allows either of the landscape orientations) -
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

IBAction to set UIImageview image in second View Controller?

I am trying to set up something where the IBAction from view controller 1 would set the ImageView image in View Controller 2's viewDidLoad using tags to differentiate between the buttons pressed.... Something like (not exact code...just hashing it out in my head):
In View Controller 1:
-(IBAction)buttonpressed:(id)sender {
if (button.tag == 1) {
ViewController2ImageView.image = [UIImage imageNamed:#"image1.png"];
}else if (button.tag == 2) {
ViewController2ImageView.image = [UIImage imageNamed:#"image2.png"];
}
In View Controller 2:
-(void)viewDidLoad {
ViewController2ImageView.image = [UIImage imageNamed:#"?.png"];
}
The issue would be how to get the info from the IBAction to pass to the second view controller and having the viewDidLoad method load the correct image.
Ive seen examples converting data to NSString and passing it that way however I was really wondering if there was a better way to go about this.
Ive been able to Google my way through putting a few apps together so far however this has had me scratching my head for a few days now.
Any help or suggested code would be helpful! Even if its just a push in the right direction.
Thanks!
When the button is pressed in viewController1, do something like this:
-(IBAction)buttonpressed:(id)sender {
if (button.tag == 1) {
[[NSUserDefaults standardUserDefaults] setObject:#"image1.png"
forKey:#"viewController2SelectedImageKey"];
} else if (button.tag == 2) {
[[NSUserDefaults standardUserDefaults] setObject:#"image2.png"
forKey:#"viewController2SelectedImageKey"];
} else {
//set some default string for the image
[[NSUserDefaults standardUserDefaults] setObject:#"yourDefaultImage.png"
forKey:#"viewController2SelectedImageKey"];
}
}
Then in your viewDidLoad method in viewController2 do something like this:
- (void)viewDidLoad {
ViewController2ImageView.image = [UIImage imageNamed:[[NSUserDefaults
standardUserDefaults] objectForKey:#"viewController2SelectedImageKey"]];
//other setup
}
This code sets a string to a key in the NSUserDefaults plist (the key I used is #"viewController2SelectedImageKey" but you can use any string you like), which is referenced by viewController2 upon its viewDidLoad method and uses the string as the name of the image file.
Why not use a NSString *imageName property to pass the filename to View Controller 2?
Hi You can do simply with many type as below
1)you can write the method
(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle )nibBundleOrNil andImageName:(NSString)ImageName;
write this mehod in the second view & call from the first view and when this method call at that time store this image name in the second local variable like below
(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle )nibBundleOrNil andImageName:(NSString)ImageName
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
//Local Variable Assignment
imageName=ImageName;
}
return self;
}
and then pass it in ur code means in ur
(void)viewDidLoad {
ViewController2ImageView.image = [UIImage imageNamed:imageName];
}
2)Second type is create imageName Varibale in the secound view and give #property and #synthesize it and than use it at below
SecoundView *ObjSecoundView=[[ObjSecoundView alloc] initWithNibName:#"SecoundView" bundle:nil];
ObjSecoundView.imageName=#"Image1.png";//put ur dynamic image name
[self.navigationController pushViewController:ObjSecoundView animated:YES];
pass this and use in the viewdid load method as below
(void)viewDidLoad {
ViewController2ImageView.image = [UIImage imageNamed:imageName];
}

Loading the Different Nib Files

I created two nib files for the iPhone and iPad, so my app will be universal.
I use this method to check if it is an iPad:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
but I don't know how to load the proper nib when it knows which it is.
Does anyone know the correct method to load to nib file, accordingly?
Actually, Apple does all this automatically, just name your NIB files:
MyViewController~iphone.xib // iPhone
MyViewController~ipad.xib // iPad
and load your view controller with the smallest amount of code:
[[MyViewController alloc] initWithNibName:nil bundle:nil]; // Apple will take care of everything
Your interface files should be named differently so something like this should work.
UIViewController *someViewController = nil;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
someViewController = [[UIViewController alloc] initWithNibName:#"SomeView_iPad" bundle:nil];
}
else
{
someViewController = [[UIViewController alloc] initWithNibName:#"SomeView" bundle:nil];
}
You should use a initializer -[UIViewController initWithNibNamed:bundle:];.
In your SomeViewController.m:
- (id)init {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
if (nil != (self = [super initWithNibName:#"SomeViewControllerIPad"])) {
[self setup];
}
} else {
if (nil != (self = [super initWithNibName:#"SomeViewControllerIPhone"])) {
[self setup];
}
}
return self;
}

iPhone: Error when using -(id)initWithNibName

I am trying to use the following code, but I always get an error that I can find little information about:
- (id)initWithNibName:#"MyRidesListView" bundle:nil {
if ((self = [super initWithNibName:#"MyRidesListView" bundle:nil])) {
// Custom initialization
}
return self;
}
Error:
expected identifier before 'OBJC_STRING' token
This seems like a simple method to be calling. This is for a UINavigationController.
Ideas?
It looks like you are trying to implement a constructor method in a subclass of UIViewController or UINavigationController.
YOur syntax is a bit off. Without seeing what you are doing in a broader context I don't really know what is going on here, but this might help you out a bit. Its the closesest to your code while being syntactically correct.
- (id)initWithNibName:(NSString *)nibNameOrNull bundle:bundle {
if ((self = [super initWithNibName:nibNameOrNull bundle: bundle])) {
// Custom initialization
}
return self;
}
Then you can do this outside of your class:
[[MyRidesListView alloc] initWithNibNamed:#"MyRidesListView" bundle:nil];