Loading the Different Nib Files - iphone

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

Related

iPhone - During the app launch the app goes into background

Recently I have come across a problem, where my app goes into background during initial launch, but this only happens on iPhone 4S with iOS6. I have tested on:
simulator, with different hardware/software configurations
iPhone 5 (iOS 6)
iPhone 4S (iOS 5.1)
iPad2 (iOS 6)
and it is working on all of them, but on the iPhone 4S with iOS6 the launch of the app takes about 20s before going into background, if you "re-launch" the app after a couple of seconds you see that it is still running and works without any problem.
Is there any know issue with iPhone4S (iOS6) that causes this or there is something special about this model? [I already tested it on different iPhone4s (iOS6) and it is happening on all of them]
EDIT
I noticed something weird will doing some more testing, iPhone 4s (iOS6) is the only one that doesn't show the loading screen (first view controller), it only shows the launch image.. anyway, here is the code for the AppDelegate and the first view controller:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Optional: automatically track uncaught exceptions with Google Analytics.
[GAI sharedInstance].trackUncaughtExceptions = YES;
// Optional: set Google Analytics dispatch interval to e.g. 20 seconds.
[GAI sharedInstance].dispatchInterval = 20;
// Optional: set debug to YES for extra debugging information.
[GAI sharedInstance].debug = NO;
// Create tracker instance.
__unused id<GAITracker> tracker = [[GAI sharedInstance] trackerWithTrackingId:#"UA-APP-ID"];
return YES;
}
EcraPrincipalViewController.m
#import "EcraPrincipalViewController.h"
#import "ListaPercursosTableViewController.h"
#import "GlobalVars.h"
#import "AppDelegate.h"
#interface EcraPrincipalViewController ()
#end
#implementation EcraPrincipalViewController
{
int progresso;
int sizePercursos;
}
#synthesize lbl_versao;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.trackedViewName = #"iPhone - EcrãPrincipal";
// Do any additional setup after loading the view.
NSMutableArray *views = [[NSMutableArray alloc] initWithArray:[self.navigationController viewControllers]];
UIStoryboard *story = [UIStoryboard storyboardWithName:#"WalkMeStoryBoard" bundle:nil];
ListaPercursosTableViewController *listaV = [story instantiateViewControllerWithIdentifier:#"view_lista"];
[views replaceObjectAtIndex:0 withObject:listaV];
[self.navigationController setViewControllers:views];
lbl_info.text = NSLocalizedString(#"downloading", NULL);
lbl_versao.text = NSLocalizedString(#"walkme_versao", NULL);
dispatch_queue_t queue = dispatch_queue_create("com.WalkMe.downloadPercursos", NULL);
dispatch_async(queue, ^{
[[GlobalVars Instance] getLevadas:self];
});
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (BOOL)shouldAutorotate
{
return [[GlobalVars Instance] podeRodar];
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)novaAtualizacao
{
NSLog(#"Atualização encontrada");
lbl_info.text = NSLocalizedString(#"atualizacao_encontrada", NULL);
}
-(void)atualizarPercursosInternos
{
NSLog(#"VERIFICAÇAO Interna");
lbl_info.text = NSLocalizedString(#"a_atualizar_percursos", NULL);
}
-(void)setNewDownload:(NSNumber*)size
{
NSLog(#"Iniciou VERIFICAÇAO");
progresso = 0;
sizePercursos = [size intValue];
lbl_info.text = [NSString stringWithFormat:#"%# 0/%d", NSLocalizedString(#"novos_percursos", NULL), sizePercursos];
}
-(void)setProgress
{
NSLog(#"Progresso");
progresso++;
lbl_info.text = [NSString stringWithFormat:#"%# %d/%d", NSLocalizedString(#"novos_percursos", NULL), progresso, sizePercursos];
}
-(void)goToLista
{
NSLog(#"ACABOU VERIFICAÇAO");
UIStoryboard *story = [UIStoryboard storyboardWithName:#"WalkMeStoryBoard" bundle:nil];
[[GlobalVars Instance] getLevadas];
[self.navigationController pushViewController:[story instantiateViewControllerWithIdentifier:#"view_lista"] animated:NO];
}
- (void)viewDidUnload {
[self setLbl_versao:nil];
[super viewDidUnload];
}
#end
Thank you very much for your attention and help :)
While trying to figure out what was happening, the suggestions made in the comments to the question made me realize that for some reason the app was calling the same method twice and for some reason that cause the app to go to the background.
To solve the problem, simply remove this lines from viewDidLoad
NSMutableArray *views = [[NSMutableArray alloc] initWithArray:[self.navigationController viewControllers]];
UIStoryboard *story = [UIStoryboard storyboardWithName:#"WalkMeStoryBoard" bundle:nil];
ListaPercursosTableViewController *listaV = [story instantiateViewControllerWithIdentifier:#"view_lista"];
[views replaceObjectAtIndex:0 withObject:listaV];
[self.navigationController setViewControllers:views];
and add them to goToLista (this is called after the initial loading is complete).
I ended up with:
- (void)viewDidLoad
{
[super viewDidLoad];
self.trackedViewName = #"iPhone - EcrãPrincipal";
// Do any additional setup after loading the view.
lbl_info.text = NSLocalizedString(#"downloading", NULL);
lbl_versao.text = NSLocalizedString(#"walkme_versao", NULL);
dispatch_queue_t queue = dispatch_queue_create("com.WalkMe.downloadPercursos", NULL);
dispatch_async(queue, ^{
[[GlobalVars Instance] getLevadas:self];
});
}
-(void)goToLista
{
NSLog(#"ACABOU VERIFICAÇAO");
[[GlobalVars Instance] getLevadas];
NSMutableArray *views = [[NSMutableArray alloc] initWithArray:[self.navigationController viewControllers]];
UIStoryboard *story = [UIStoryboard storyboardWithName:#"WalkMeStoryBoard" bundle:nil];
ListaPercursosTableViewController *listaV = [story instantiateViewControllerWithIdentifier:#"view_lista"];
[views replaceObjectAtIndex:0 withObject:listaV];
[self.navigationController setViewControllers:views];
[self.navigationController pushViewController:[story instantiateViewControllerWithIdentifier:#"view_lista"] animated:NO];
}
This solves the problem, but I don't know why. If anyone knows please do tell, because I really would like to know.
Thank you :)

iPhone - self.view is nil in initWithNibName subclass, and viewDidLoad not called

I have this cascade :
Somewhere in the app
- (void) go
{
MyCustomViewController* controller = [[MyCustomViewController alloc] init];
controller.delegate = self;
[controller run];
}
in MyCustomViewController
- (id) init
{
// there is an if statement here in real to choose another XIB if needed
// but I only display here the code that is called in my tests
self = [super initWithNibName:#"MyXIB" bundle:nil];
if (!self) return nil;
self.delegate = nil;
return self;
}
- (void) run
{
// do things with self.view
}
MyCustomViewController inherits GenericWindowController
in GenericWindowController
/*- (id) initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil
{
if (!(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) return nil;
self.view.userInteractionEnabled = NO; // THE APP CRASHES HERE ! self.view is nil
...
return self;
}*/
// METHOD created following first answers : NOT CALLED
- (void) viewDidLoad
{
self.view.userInteractionEnabled = NO;
// and many other things done with self.view
}
MyXIB has its File's owner set to MyCustomViewController and the view is connected.
All files are included and checked into the project.
GenericWindowController is designed to make some standard stuff.
MyCustomViewController extends this stuff to work with a custom View, as designed in MyXIB.
Why self.view is nil in GenericWindowController ?
Why viewDidLoad is not called ?
A view controller should not try to access its view in initWithNibName:bundle:. It should wait until viewDidLoad.
self.view is only valid after viewDidLoad -- in init... it is still nil.
Incredible !!! The problem was because of a missing localization for the XIB. Added the localization, and no problem anymore.

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

iOS switching view controllers depending on the device orientation

I'm developing an Augmented Reality application, everything worked properly till now that I need two different kind of visualization (AR and Map) depending on the device orientation. In particular the application should use the landscapeViewController when the device is in landscape mode while it should use another controller (named faceUpViewController ) when the device's orientation is "face up". I tried doing it with two simple view controllers and it works fine. The problem happens when the landscapeViewController uses the AR controller. The view is completely white and I don't understand why. Both the two controllers are "contained" by a Root View Controller. I'm doing everything by coding so without nib files. Here is the code:
RootViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
- (void)deviceOrientationDidChange:(NSNotification *)notification{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationLandscapeLeft) {
if (self.landscapeViewController.view.superview == nil) {
if (self.landscapeViewController == nil) {
LandscapeViewController *lvc = [[LandscapeViewController alloc] init];
self.landscapeViewController = lvc;
[lvc release];
}
[self.faceUpViewController.view removeFromSuperview];
[self.view addSubview:self.landscapeViewController.view];
}
}
if (orientation == UIDeviceOrientationFaceUp) {
if (self.faceUpViewController.view.superview == nil) {
if (self.faceUpViewController == nil) {
FaceUpViewController *fvc = [[FaceUpViewController alloc] init];
self.faceUpViewController = fvc;
[fvc release];
}
[self.landscapeViewController.view removeFromSuperview];
[self.view addSubview:self.faceUpViewController.view];
}
}
}
#end
LandscapeViewController.m
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
UIView *landscapeView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
landscapeView.backgroundColor = [UIColor yellowColor];
self.view = landscapeView;
[landscapeView release];
ARController *arC = [[ARController alloc] initWithViewController:self];
arC.landscapeViewController = self;
self.arController = arC;
[arC release];
}
//When the view appear present the camera feed
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[_arController presentModalARControllerAnimated:NO];
}
FaceUpViewController.m
- (void)loadView
{
UIView *faceUpView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
faceUpView.backgroundColor = [UIColor blueColor];
self.view = faceUpView;
[faceUpView release];
}
ARController.m Very simple version
- (id) initWithViewController:(UIViewController *)theView{
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
self.rootController = theView;
//Retrieve screen bounds
CGRect screenBounds = [[UIScreen mainScreen] bounds];
UIView *overlaidView = [[UIView alloc] initWithFrame: screenBounds];
self.overlayView = overlaidView;
[overlaidView release];
self.rootController.view = overlayView;
// Initialise the UIImagePickerController
UIImagePickerController *picker= [[UIImagePickerController alloc] init];
self.pickerController = picker;
[picker release];
self.pickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.pickerController.cameraViewTransform = CGAffineTransformScale(
self.pickerController.cameraViewTransform, 1.0f, 1.12412f);
self.pickerController.showsCameraControls = NO;
self.pickerController.navigationBarHidden = YES;
self.pickerController.cameraOverlayView = _overlayView;
}
return self;
}
- (void)presentModalARControllerAnimated:(BOOL)animated{
[self.rootController presentModalViewController:[self pickerController] animated:animated];
self.overlayView.frame = self.pickerController.view.bounds;
}
#end
I say again that I'm doing everything by coding thereby without nib files.
I really appreciate any advice!
Thanks
The primary problem with adding and removing your "child" view controllers' views as you've done here is that the view controller life cycle methods (viewWillAppear:, viewDidAppear:, etc.) won't ever get called on your child view controllers. Containers like UINavigationController and UITabBarController have always known how to delegate methods like these appropriately to their children, but UIViewController didn't officially support the ability to nest view controllers under your own custom container before iOS 5. It was possible, but it took a lot more work to do it right.
If you want to stick with the approach of adding and removing subviews, you have two options:
Require iOS 5+, and call addChildViewController:, removeFromParentViewController,
transitionFromViewController:toViewController:duration:options:animations:completion:,
willMoveToParentViewController:, and
didMoveToParentViewController: as described in the Implementing a Container View Controller section of the UIViewController Class Reference.
To support older iOS versions, you'll have to override many of the methods of the UIViewController class and delegate those calls manually to your child view controllers to make them behave as expected. I'd pay particular attention to the sections titled, "Responding to View Events", and "Responding to View Rotation Events" in the UIViewController Class Reference.
A different approach for pre-iOS 5 support is to present your child view controllers using presentModalViewController:animated: rather than adding their views as subviews to a container. Apple describes this approach in the View Controller Programming Guide for iOS under the section, Creating an Alternate Landscape Interface. The advantage of this approach is that your child view controllers are officially supported as first-class members of the view controller hierarchy, so UIKit will automatically manage their life cycles appropriately. You won't have to override and delegate all those methods manually.
You might want to try getting your acceptance rate up a little bit - more people would be willing to help you.
Anyway, wild guess: in your root controller, try putting the contents of
deviceOrientationDidChange
into
deviceOrientationWillChange.

Dynamically load nib for iPhone/iPad within view controller

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