i'd like to handle orientation change on an iPad application with one UIViewController and two XIBs, let's say MenuView and MenuViewLandscape.
So, in the willRotateToInterfaceOrientation method of the MenuViewController, how can i change XIB without using another controller for the landscape mode ?
I'm using the following code:
if( toInterfaceOrientation != UIInterfaceOrientationPortrait ){
MenuViewController *landscape = [[MenuViewController alloc]
initWithNibName: #"MenuViewLandscape"
bundle:nil
];
[self setView:landscape.view];
}
else {
MenuViewController *potrait = [[MenuViewController alloc]
initWithNibName: #"MenuView"
bundle:nil
];
[self setView:potrait.view];
}
But when i go to landscape view the XIB the landscape view controls are not properly rotated.
I'm not sure there are any strange side-effects with this implementation, but try something like this and see if it works for you:
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration {
if (UIInterfaceOrientationIsPortrait(orientation)) {
[[NSBundle mainBundle] loadNibNamed:#"MenuView" owner:self options:nil];
if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
self.view.transform = CGAffineTransformMakeRotation(M_PI);
}
} else if (UIInterfaceOrientationIsLandscape(orientation)){
[[NSBundle mainBundle] loadNibNamed:#"MenuViewLandscape" owner:self options:nil];
if (orientation == UIInterfaceOrientationLandscapeLeft) {
self.view.transform = CGAffineTransformMakeRotation(M_PI + M_PI_2);
} else {
self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
}
}
}
This assumes that the File's Owner in your MenuView and MenuViewLandscape XIBs are both set to MenuViewController and that the view outlet is set in both XIBs as well. All of your outlets should be reconnected properly on rotation when using loadNibNamed.
If you are building for iOS 4, you could also replace the loadNibNamed lines with these:
UINib *nib = [UINib nibWithNibName:#"MenuView" bundle:nil];
UIView *portraitView = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
self.view = portraitView;
and
UINib *nib = [UINib nibWithNibName:#"MenuViewLandscape" bundle:nil];
UIView *landscapeView = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
self.view = landscapeView;
These assume that the UIView that you want to display immediately follows the File's Owner and First Responder proxy objects in the XIBs.
Then you just need to make sure the views are rotated properly for the interface orientation. For all of the views that are not in the default portrait orientation, rotate them by setting the transform property of the view and using CGAffineTransformMakeRotation() with the appropriate values as shown in the example above.
The rotation alone might solve your issue without the extra loading of the NIBs. However, loading a whole new instance of a MenuViewController and setting its view to the existing MenuViewController's view might cause some strange behavior with lifecycle and rotation events, so you might be safer trying the examples above. They also save you the trouble of having to create new MenuViewController instances when you only need the view from it.
Hope this helps!
Justin
Perhaps the answer from Jon Rodriguez here will do what you want:
Want to use muliple nibs for different iphone interface orientations
If you have two UIViewController classes, a base class for portrait mode and a subclass of that for landscape mode, you can put almost all the code in the base class. So that gives you most of the advantages of a single view controller class while also allowing you to use other solutions like this:
Easiest way to support multiple orientations? How do I load a custom NIB when the application is in Landscape?
Related
I'm having a little trouble with essentially flipping a UIView to imitate a card turning over.
As it stands, I have created two UIViews (front and rear) inside a XIB and loaded it into the storyboard as such:
//Initiate the view from the XIB
CustomClass *drag = [[CustomClass alloc] initWithFrame:rect];
//Add the view to the view (container) within the storyboard
[self.draggableView addSubview:drag];
Inside my custom class is the following:
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
self = [[[NSBundle mainBundle] loadNibNamed:#"Draggable"
owner:self
options:nil] objectAtIndex:0];
return self;
}
This satisfactorily presents the front view...
Now I cannot for the life of me work out how to flip the view to display another UIView inside the same XIB... This UIView is available at "objectAtIndex:1".
I'm pretty certain the code I need includes:
[UIView transitionWithView:UIVIEW
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromBottom
animations: ^{}];
If xib has many views, You should use loadNibNamed to create the view in xib file:
NSArray *views = [[NSBundle mainBundle] loadNibNamed:#"BlueView" owner:nil options:nil];
self.blueView = views[0];
self.yellowView = views[1];
Quick question. Is this possible to create storyboard to landscape orientation? I have to change some positions of elements in views and I can't use auto-layout to deal with it. Of course I can write it in code, but I think it's unnecessary. I'm quite new in iOS developer, but I was working as Android developer. There we could create different folders for every instance. I think it should look like this:
Main.storyboard
Main_iPad.storyboard
Main_landscape.storyboard
Main_landscape_iPad.storyboard
Of course creating different files is not a problem. Problem is: how to link it properly in my code/plist/etc.
Warning: Don't create a separate nib/storyboard for landscape version of the same view unless your layout is completely different. Auto-Layout should handle layout changes upon orientation change.
Assuming you have good reasons to do this:
You can have different nibs/storyboards for the same view/view controller. Just implement each as if you have only one nib/storyboard. Then create the view/view controller as follows:
For nib:
NSString *nibName = #"NAME OF ONE OF THE NIBS";
NSArray *nibs = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
id view = [nibs firstObject]; // Ideally, iterate through the array and check class, then return the view.
For storyboard:
NSString *storyboardName = #"NAME OF ONE OF THE STORYBOARDS";
NSString *viewControllerIdentifier = #"VIEW CONTROLLER IDENTIFIER";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:viewControllerIdentifier];
I have my normal view controller, but once in awhile I'll present something of an options screen atop the view controller. I don't know how to set this up in a Storyboard though, as if I had it on top and then set hidden to true, it would obstruct all the other views and be rather annoying to fiddle around with.
What should I be doing in this case?
You can create the view in a separate XIB file, and then load the view from your view controller programmatically. If this view is showed often you can keep the view in a property, otherwise you can just load it from the XIB every time. To load a view contained in a XIB:
UIView *view = [[[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil] firstObject];
[self.view addSubview:rootView];
If this view is something like a settings, I'd recommend to use a separate view controller. You could use a Storyboard and then programmatically add it as a child view controller.
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MyStoryboard" bundle:nil];
UIViewController *vc = [sb instantiateInitialViewController];
[self addChildViewController:vc];
content.view.frame = self.view; // here you set the frame of your view
content.view.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
[self.view addSubview:vc.view];
[vc didMoveToParentViewController:self];
Are you trying to create a view that is smaller than the view controller?
If that's the case then you can create one programmatically by sub classing UIView.
But if it will cover your entire view controller then you can create a view controller with a .xib file. You can then draw everything on it using IB and then call that view controller morally.
I have a UIViewController that only works in landscape, i.e. is configured with
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationLandscapeLeft;
}
I have a custom UIView (not View Controller) with its own NIB. I load it in the view controller with
- (void)viewDidLoad
{
[super viewDidLoad];
ISMainView *customView = [[[NSBundle mainBundle] loadNibNamed:#"ISMainView" owner:nil options:nil] lastObject];
[self.view addSubview:customView];
}
When the view loads it's displayed rotated by 90 degrees, as if it wants to be in portrait orientation.
Any idea what's wrong? A UIView doesn't know anything about its own orientation, right? So the problem must be with the View Controller?
So it appears the problem was being caused by
-(BOOL)shouldAutorotate {
return NO;
}
I'm not sure why, but turning off shouldAutorotate prevented the subview from being set to the proper device orientation, even though the device never rotates. Odd.
Hi can I ask if can I mix the xib with my UIViewController in storyboard? Like they share a controller in their files owner because I'm planning to create a expandable view by using nib as the expandedview and I want to pass a value from nib file to the UIViewController in storyboard. Thanks.
I don't recommend you mix xib and view controller from storyboard and hook them all together.
If you want to add a UIView as an extended view to your view controller you can do something like this:
// Load the first view inside our xib from main bundle
UIView *view = [[NSBundle mainBundle] loadNibNamed:#"nib_name" owner:self options:nil][0];
// Define new X,Y to our view
float new_x_position = 0;
float new_y_position = 400;
view.frame = CGRectMake(new_x_position, new_y_position, CGRectGetWidth(view.frame), CGRectGetHeight(view.frame));
// UILabel inside our nib, '111' is the tag we set to our UILabel
UILabel *label = (UILabel*)[view viewWithTag:111];
// Add our view to the current view as a sub view
[self.view addSubview:view];
I hope I understood you correctly.
In storyboard you are not provided with xibs, but if you want to use them to load from nib then use :
MyViewController *viewController = [[MyViewController alloc] initWithNibName:#"CustomDownloadedXib" bundle:nil];