Change nib programmatically when orientation changes - iphone

I load my nibs like this in the init:
self = [super initWithNibName:#"PageView_iPhone" bundle:nil];
But how do I change this during runtime (i.e. after the init) if the orientation changes? initWithNibName won't work.

If you really want to load a different nib, you could use:
[[NSBundle mainBundle] loadNibNamed:#"PageView_iPhone" owner:self options:nil];
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"PageView_iPhone"
owner:self
options:nil];
PageView_iPhone* myView = [nibViews objectAtIndex:0];

During init, I setup my views (create them programatically) and create extra CGPoint's for center positions or CGRect's for frames for each view in the view controller.
After the views are created in init, I call method named layoutViewsForOrientation to set the initial position, passing in the current status bar orientation. When the orientation changes, I use this code:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
BOOL result = NO;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
result = (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
result = YES;
}
if( result == YES ) [self layoutViewsForOrientation:interfaceOrientation];
return result;
}
There may be an easier way, but this works well for my needs.

Related

Loading Alternative UITabBarController for iPhone5 support

I have to add support for iPhone5 to my app. Currently the appdelegate uses a nib that is a UITabBarController, and code like this. Works fine.
[window addSubview:rootController.view];
[window makeKeyAndVisible];
rootController is an instance of UITabBarController.
So I have created a new nib for iPhone5 and changed the code to...
if ([self IsTall])
rootController = [[[UITabBarController alloc] initWithNibName:#"MainWindow_5" bundle:nil] autorelease];
else
rootController = [[[UITabBarController alloc] initWithNibName:#"MainWindow" bundle:nil] autorelease];
[window addSubview:rootController.view];
[window makeKeyAndVisible];
But, the screen is blank with this code, like the nib is not loading.
If I try this I get the correct nibs loading and displaying on the screen but the "MORE" button is not shown and only the first 4 tabs are shown (there are 7 tabs in the tabBarController
if ([self IsTall])
rootController = [[rootController initWithNibName:#"MainWindow_5" bundle:nil] autorelease];
else
rootController = [[rootController initWithNibName:#"MainWindow" bundle:nil] autorelease];
[window addSubview:rootController.view];
[window makeKeyAndVisible];
I also tried...
if ([self IsTall])
[[NSBundle mainBundle] loadNibNamed:#"MainWindow_5" owner:rootController options:nil];
else
[[NSBundle mainBundle] loadNibNamed:#"MainWindow" owner:rootController options:nil];
But this causes a crash on the tab buttons for the nib not declared in the plist under Main
"nib file base name" setting.
Any help very much greatly appreciated. This has stumped me for a couple of days now.
Kind Regards
Rob.
Miscellaneous thoughts:
iPhone 5 is iOS 6. Was your app working under iOS 6 before? If not, get it working for iOS 6 first. My apps came up blank when linked against iOS 6 even if no other changes were made. That's because view controllers work in a whole different way. So step one is to get the simple of act of launch ironed out for iOS 6. This is particularly true if you are launching into landscape; everything is totally changed in this regard.
Do not add the subview yourself. Just set the window's rootViewController. It adds the subview for you.
On the whole you should NOT be loading a different nib in any case. You should be using layout to lay out the same interface in such a way that it doesn't matter whether the screen is tall or not.
Hope something in there will help...
Your second code is totally illegal. Never never never say "init" except in the very same line where you just said "alloc" (except in an initializer, of course).
Here is the method I use
+ (BOOL)isPad{
BOOL result = NO;
if ([[UIDevice currentDevice] respondsToSelector:#selector(userInterfaceIdiom)]){
result = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
}
return result;
}
+ (BOOL) isPhone{
BOOL result = NO;
static NSString *model;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
model = [UIDevice currentDevice].model;
});
if ([model hasPrefix:#"iPhone"]){
result = YES;
}
return result;
}
+ (BOOL)isWidescreen{
BOOL result = NO;
static CGFloat ratio;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
CGSize size = [UIScreen mainScreen].bounds.size;
CGFloat numerator = MAX(size.height, size.width);
CGFloat denominator = MIN(size.height, size.width);
ratio = numerator / denominator;
});
if (ratio > 1.77 && ratio < 1.78){
result = YES;
}
return result;
}
I then have Define statements to make them easier to reach
#define IS_IPAD() [StaticContainer isPad]
#define IS_WIDESCREEN() [StaticContainer isWidescreen]
#define IS_IPHONE() [StaticContainer isPhone]
I also have a UIViewController category
#implementation UIViewController (initHelpers)
- (id) initClassDevice{
NSString *device = #"";
if (IS_IPAD()) {
device = #"iPad";
}
else if (IS_WIDESCREEN()){
device = #"iPhone5";
}
else {
device = #"iPhone";
}
NSString *nibName = [NSString stringWithFormat:#"%#_%#",[self class],device];
self = [self initWithNibName:nibName bundle:nil];
return self;
}
#end
You will need to have a nib file for each one that has the different items in it. and they need to be connected to everything separately.
Mine currently only separates iphone and ipad so for a view controller i would have
MyMagicViewController.h/MyMagicViewController.m
and for the xib files i would have
MyMagicViewController_iPad.xib, MyMagicViewController_iPhone5.xib and MyMagicViewController_iPhone.xib
the important thing is to match the name of the xib file with the call to initWithNibName:
Also Another thing to think of. is the View you are initializing needs to be the same size as the View it is going into .
view.frame = superView.bounds;
then it will fit properly
for me adding Default-568h#2x.png to my project resolved it.

How to invoke loadNibNamed without losing UISwipeGestures?

I'm loading a different xib when the user flips to landscape and that's working great, but I've noticed that my swipe events aren't registered.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if ([self currentlyInLandscapeMode:toInterfaceOrientation]) {
[[NSBundle mainBundle] loadNibNamed:#"PhotosLandscape" owner:self options:nil];
}else{
[[NSBundle mainBundle] loadNibNamed:#"PhotosPortrait" owner:self options:nil];
}
}
- (BOOL)currentlyInLandscapeMode:(UIInterfaceOrientation)interfaceOrientation
{
return (UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
How can you switch xib's and keep all the state from my previous view/xib?
UPDATE
turns out my IBOutlets still work but my swipe isn't registered
You can't use a nib file to lay out existing objects. A nib file is stored as an archived object graph, so when you load a nib, either with NSBundle's loadNibNamed: or UIViewController's initWithNibName:, a fresh set of objects gets instantiated.
The only way around this would be to use loadNibNamed to instantiate a new set of objects, and use their frame properties to set the frames for your existing objects, which isn't a great solution.
Turns out I just needed to re-register the swipe event after each nib is loaded (like so)
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if ([self currentlyInLandscapeMode:toInterfaceOrientation]) {
[[NSBundle mainBundle] loadNibNamed:#"VotePhotosLandscape" owner:self options:nil];
}else{
[[NSBundle mainBundle] loadNibNamed:#"VotePhotosViewController" owner:self options:nil];
}
[self wireupSwipeEvents];
}
- (BOOL)currentlyInLandscapeMode:(UIInterfaceOrientation)interfaceOrientation
{
return (UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
- (void)wireupSwipeEvents
{
UISwipeGestureRecognizer *recognizer;
recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeFrom:)];
[recognizer setDirection:UISwipeGestureRecognizerDirectionLeft];
[[self view] addGestureRecognizer:recognizer];
[recognizer release];
}

Auto orientation

In my project I am required to implement the orientation feature of iPhone. For this I have created two xib files and one UIViewController controller file, one for landscape and other for portrait. I have taken one UIViewController because the content of the xib files are same.
Now I want to load the respective xib file depending on the rotation and the content should remain populated in the TextField when the specific file loads. How should I implement this?
CODE:
if( orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight)
{
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"landscapeView" owner:self options:nil]; UIView *viewItem = (UIView *)[nibArray objectAtIndex:0]; self.view = viewItem;
}
else if(orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown)
{
//same above lines only different nib name
}
You can check in your init method of UIViewController device orientation using following code
if (UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation])
{
// load view for landscape mode
}else
{
// load view for portrait mode
}
And choose .xib file that correspond to current orientation.
Hope, this will work.
it is a very bad approach please set xib by code and use
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
above one for rotation and below one to set the xib.
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
I hope this helps you.

Flipping a Video Horizontally in Xcode

I have a video such that when I play it in full view and flip the simulator horizontally the video would not flip. How can I make the video flip according to the iPhone's accelerometer?
Here is the code for the video if it helps:
- (void)viewDidAppear:(BOOL)animated {
NSBundle *bundle=[NSBundle mainBundle];
NSString *moviePath = [bundle pathForResource:#"MainPageMovie" ofType:#"mp4"];
NSURL *movieURL=[[NSURL fileURLWithPath:moviePath] retain];
MPMoviePlayerController *theMovie = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
theMovie.scalingMode = MPMovieScalingModeAspectFill;
theMovie.view.frame = CGRectMake(115.0, 156.0, 200.0, 150.0);
[self.view addSubview:theMovie.view];
[theMovie play];
[super viewDidLoad];
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
}
Your MPMoviePlayerController view is added as a subview to another view controller's view. In that parent view controller, have you overridden shouldAutorotateToInterfaceOrientation method?
If you are just looking for landscape (horizontal) rotation, you can have your code as below:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return ((interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
In order to rotate specific subviews of the larger view controller view, (which in your situation would be the case with only rotating the video), you'd have to do CGAffineTransforms. This would allow you to resize a view, rotate it, etc. AFAIK, this is probably the best route to go for rotating a single view.
However, you may want to research more into MPMoviePlayerViewController. Take a look at this post, it seems to address your problem.

How to change a new nib when I rotate the device?

I have a helloController which is a UIViewController, if I rotate the device, I want it change it to load a new nib "helloHorizontal.xib", how can I do? thank you.
You could you something like this, (I dont have xcode handy so this code might not be completely accurate)
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if((interfaceOrientation == UIInterfaceOrientationLandscapeRight) || (interfaceOrientation == UIInterfaceOrientationLandscapeLeft)){
WhatYourNewViewClassISCAlled* newView = [[WhatYourNewViewClassISCAlled alloc] initWithNibName:#"NIBNAME" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:newView animated:YES];
}
This is the correct way, I believe. I'm using it in my apps and it works perfectly
triggers on WILL rotate, not SHOULD rotate (waits until the rotate anim is about to start)
uses the Apple naming convention for landscape/portrait files (Default.png is Default-landscape.png if you want Apple to auto-load a landscape version)
reloads the new NIB
which resets the self.view - this will AUTOMATICALLY update the display
and then it calls viewDidLoad (Apple will NOT call this for you, if you manually reload a NIB)
(NB stackoverflow.com requires this sentence here - there's a bug in the code formatter)
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if( UIInterfaceOrientationIsLandscape(toInterfaceOrientation) )
{
[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:#"%#-landscape", NSStringFromClass([self class])] owner:self options:nil];
[self viewDidLoad];
}
else
{
[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:#"%#", NSStringFromClass([self class])] owner:self options:nil];
[self viewDidLoad];
}
}