I have created an application to support all orientations.
Also I have created a table and search bar in viewDidLoad method (without using xib). I have also written following code to adjust the positioning of table view and search bar.
It works properly when I launch my application in portrait view and when I rotate to landscape view, it adjust the views perfectly.
But when I launch my application in landscape mode, views are not adjusted, instead they take positioning of portrait view. But again when I rotate in portrait and then landscape it works all right.
Also to ensure that correct orientation is captured, I have written NSLog inside this method, and it shows me the correct value.
Also to ensure that this method (adjustViewsForOrientation) is called explicitly, I have called the method in viewDidLoad as well.
[self adjustViewsForOrientation:self.interfaceOrientation];
Here is a rest of code snippet:
- (void) adjustViewsForOrientation:(UIInterfaceOrientation)orientation {
if (orientation == UIInterfaceOrientationLandscapeLeft ||
orientation == UIInterfaceOrientationLandscapeRight) {
aTableView.frame = CGRectMake(705, 220, 320, 280);
sBar.frame=CGRectMake(805, 0, 220, 40);
NSLog(#"inside landscape");
}
else if (orientation == UIInterfaceOrientationPortrait ||
orientation == UIInterfaceOrientationPortraitUpsideDown) {
aTableView.frame = CGRectMake(450, 220, 320, 280);
sBar.frame=CGRectMake(550,3,220,40);
}
}
- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
// [self adjustViewsForOrientation]
[self adjustViewsForOrientation:toInterfaceOrientation];
NSLog(#"landscap");
}
Try this approach, it may help. Register for UIDeviceOrientationDidChange in viewDidLoad method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(adjustViewsForOrientation:) name:UIDeviceOrientationDidChangeNotification object:nil];
and also change -(void) adjustViewsForOrientation:(UIInterfaceOrientation)orientation
to -(void) adjustViewsForOrientation:(NSNotification *)notification
and obtain the new orientation by getting the orientation value
e.g. UIDeviceOrientation newDeviceOrientation = [notification orientation];
Related
I know similar questions have been asked before, but this is a more specific use case (controlling orientation from a static library, cannot write in root view controller).
I have a static library which adds UI elements as overlays to a passed view controller (the client's root view controller) as subviews. Problem is our UI elements support portrait orientation only, while our client's application may support both portrait and landscape. This is fine, as long as our UI elements don't autorotate when our client's views do.
I'd like to lock the orientation to portrait only for our view controller only. In iOS 6, when I use the following code in my library's view controller, it doesn't affect the behaviour of autorotate at all:
-(BOOL)shouldAutorotate{
return NO;
}
-(NSInteger)supportedInterfaceOrientations{
NSInteger orientationMask = 0;
if ([self shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationPortrait])
orientationMask |= UIInterfaceOrientationMaskPortrait;
return orientationMask;
}
When I put the same code in the root view controller, it works perfectly, with the app no longer auto-rotating. However, this is not an option for us since in production we will not have access to our client's root view controller. Is there a way to either lock view orientation from NOT a root view controller, or lock orientation for a single view controller only? Any other way of achieving what we need that I'm not thinking of? Hoping for solutions that work in iOS <= 6 if at all possible
You can't lock orientation only for one view controller in whole application . So you have to set transform angle with parent view frame on view launch and on change of orientation.
You add code for those view controller class
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
[[self view] setBounds:CGRectMake(0, 0, self.view.frame.size.height, self.view.frame.size.width)];
CGFloat radians = atan2f(self.view.transform.b, self.view.transform.a);
if (radians!=M_PI_2) {
[[self view] setTransform:CGAffineTransformMakeRotation(M_PI_2)];
}
}
}
(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
[[self view] setBounds:CGRectMake(0, 0, self.view.frame.size.height, self.view.frame.size.width)];
CGFloat radians = atan2f(self.view.transform.b, self.view.transform.a);
if (radians!=M_PI_2) {
[[self view] setTransform:CGAffineTransformMakeRotation(M_PI_2)];
}
}else{
[[self view] setBounds:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGFloat radians = atan2f(self.view.transform.b, self.view.transform.a);
if (radians!=0) {
[[self view] setTransform:CGAffineTransformMakeRotation(0)];
}
}
}
How can I write my code which supports both landscape and portrait? I tried this one -
(void)setupForOrientation:(UIInterfaceOrientation)orientation {
if (UIInterfaceOrientationIsPortrait(orientation)) {
CGRect bounds = CGRectMake(0, 0, 768, 1004);
scrollView.frame = bounds;
// Other view positioning for portrait.
} else {
CGRect bounds = CGRectMake(0, 0, 1024, 748);
scrollView.frame = bounds;
// Other view positioning for landscape.
}
[self drawBackgroundForOrientation:orientation];
}
But its not working. Please help me.
Use this code:
- (void)viewDidLoad
{
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
}
- (void) orientationChanged:(id)object
{
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft || [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeRight )
{
self.view=self.landscapeView;
NSLog(#"ViewwillAppear= Land");
}
else
{
self.view = self.portraitView;
NSLog(#"ViewwillAppear= Port");
}
You'll always have to return yes for this function whenever you require orientation support.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
Edit 1: P.S. I've taken portraitView & landscapeView as two separate UIViews.
I think you want to override the shouldAutorotateToInterfaceOrientation: method of your main view controller to always return YES. That or you can specify the supported orientations for your app in the app properties.
You have to use following method to support both orientation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
//this method will call just before changing Orientation of device you can set frames in this method
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
}
If you have used viewcontrollers then there are a few methods that you can override. The first being
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; // Override to allow rotation. Default returns YES only for UIInterfaceOrientationPortrait
Where you can specify whether a particular view will support rotation or not.
If you need to configure your views manually according to the orientation you can override the following methods
As per the documentation
Your implementation of this method should simply return YES or NO based on the value in the interfaceOrientation parameter. Do not attempt to get the value of the interfaceOrientation property or check the orientation value reported by the UIDevice class. Your view controller is either capable of supporting a given orientation or it is not.
// Notifies when rotation begins, reaches halfway point and ends.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
AND
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
To achieve what you wish to implement you can return YES for supported orientation and then use the second method to configure your view accordingly by calling [self setupForOrientation:orientation];
Hope it helps!!!
I have a view controller in my iphone app setup so that when I change the orientation from portrait to landscape, I change the view. When I change the orientation back from landscape to portrait, the initial view comes back, except this time it is all crammed into the left hand side of the screen. Eventually, when I change orientations enough times everything disappears completely. Is this a common issue beginners have? What could I be doing wrong?
In my root controller I am allowing the orientation to change only when a specific view is being shown with this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if (self.currentView == (NSInteger*)Chart || self.currentView == (NSInteger*)Detail) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
return (interfaceOrientation == UIInterfaceOrientationPortrait);
Where self.currentView is an enum of what view I currently have up. The Detail view I want to make sure stays as a portrait view, but when I change the orientation while on that view I want it to change the view to the Graph. Again, this works fine the first time, but when I change back from Graph to Detail, it crams all the controls on the Detail view to the left hand side of the screen.
Here is how I'm changing the view:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
if (self.currentView == (NSInteger*)Detail && (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"changeView" object:self userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:Chart] forKey:#"view"]];
}
if (self.currentView == (NSInteger*)Chart && (toInterfaceOrientation == UIInterfaceOrientationPortrait)) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"changeView" object:self userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:Detail] forKey:#"view"]];
}
#justin I once did this which got me into same situation as you are. May be you can check if you haven't done something like this
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
CGRect rect;
if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
rect = CGRectMake(tableView.frame.origin.x,tableView.frame.origin.y,
tableView.frame.size.width - 50, tableView.frame.size.height - 30);
}
else {
rect = CGRectMake(tableView.frame.origin.x,aBar.frame.origin.y,
tableView.frame.size.width, tableView.frame.size.height);
}
[tableView setFrame:rect];
return YES;
}
All I wanted was a table view with small frame in Portrait mode, without saving the original Frame I was trying to reduce its width and height which eventually brought the table view to a very small size after multiple rotation..
Lolzzz. I should have first saved the original tableview frame and then done something like this
if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
tableView.frame = CGRectMake(tableView.frame.origin.x,tableView.frame.origin.y,
tableView.frame.size.width - 50, tableView.frame.size.height - 30);
}
else {
tableView.frame = originalTableViewFrame;
}
check if you have autoresizeSubviews ON (in XIB/Inteface Builder) on your view and possibly parent views and try to turn it off if you are changing frame manually , this solved it for my case
I have a simple UIViewController that uses a XIB for its interface.
As such, the interface simply comprises a UIView, a UIActivityIndicator and a UILabel. I have the sizing constraints in Interface Builder set to keep the activity indicator and the label centred when the view rotates.
The UIViewController is set to return YES for portrait and landscape orientations in the -shouldAutorotateToInterfaceOrientation: method.
The view is added to the view hierarchy manually, using
if (!activityOverlay)
activityOverlay = [[XBActivityOverlayViewController alloc] initWithNibName:#"XBActivityOverlayViewController" bundle:nil message:#"Connecting..."];
[activityOverlay.view setAlpha:0.0f];
[self.window addSubview:activityOverlay.view];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3f];
[activityOverlay.view setAlpha:0.9f];
[UIView commitAnimations];
The problem I'm having is that if the device is already in landscape orientation and the view is added to the hierarchy at this point, the view is still in portrait orientation, and doesn't auto rotate.
What do I need to do to add the view in the same orientation as the parent view?
I do not know if it is the right answer to your question but I hope can help:
For me, every time I add/dismiss a modal or display a new view, the following function is called:
-(void) detectOrientation
{
if (([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft) ||
([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight))
{
item1.frame = CGRectMake(147.0, 241.0, 56.0, 28.0);
item2.frame = CGRectMake(265.0, 241.0, 56.0, 28.0);
}
else if (([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait) ||
([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown) || !inLandscapeOrientation)
{
item1.frame = CGRectMake(35.0, 425.0, 35.0, 28.0);
item2.frame = CGRectMake(176.0, 425.0, 35.0, 28.0);
}
}
Here I change the position according to the current device orientation. Maybe if you detect that the current orientation is landscape you add a subview that has such orientation.
Alejandra :)
I would like to implement the following. When the user rotates the iPhone, I want to instantiate a new UIViewController (automatically upon rotation, not clicking a button or performing a similar action) and show to the user a view handled by this new UIViewController in landscape orientation. How to do this properly ?
I tried to instantiate the new controller in the methods willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation, but none of this methods gets called!. I suspect this is because the current controller is pushed in by a navigation controller which is itself handled by a tabBarController. Any clue? A simple code snippet would be greatly appreciated.
Thank you in advance.
A cleaner way is NOT to use UIInterfaceOrientation for the rotation.
Why, because then your interface or view 1 actually rotates. This means you have to rotate it back to redisplay it after view 2 is removed.
To compensate for this, I simple subscribe to UIDeviceOrientation notifications. Subtly different. View 1 does NOT have to support autorotation to make this method work. In viewDidLoad enter the following code:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(detectOrientation) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
Then just define the method detectOrientation:
-(void) detectOrientation {
if (([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft) ||
([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight)) {
//load view 2
} else if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait) {
// load view 1
}
}
Now neither of your views need support autoratation! You will however need to perform a transform on view 2 before loading:
-(void) transformView2ToLandscape {
NSInteger rotationDirection;
UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];
if(currentOrientation == UIDeviceOrientationLandscapeLeft){
rotationDirection = 1;
}else {
rotationDirection = -1;
}
CGRect myFrame = CGRectMake(0, 0, 480, 300);
CGAffineTransform transform = [[self view2] transform];
transform = CGAffineTransformRotate(transform, degreesToRadians(rotationDirection * 90));
[[self view2] setFrame: myFrame];
CGPoint center = CGPointMake(myFrame.size.height/2.0, myFrame.size.width/2.0);
[[self view2] setTransform: transform];
[[self view2] setCenter: center];
}
Thats how I swap views o rotation without supporting autorotation in my views.
I've implemented this exact type of behavior on an app and the key is to make sure that any parent controllers implement shouldAutorotateToInterfaceOrientation and that your current view controller also implements it. In my case I am using a tab controller which intercepts the call to shouldAutorotateToInterfaceOrientation and I had to override the tab controller to get the call to fall through. The default behavior of view controllers is to display in portrait mode only.
So you need to force them to allow all orientation changes through:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
Then in order to load a new view controller you should respond to:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if((fromInterfaceOrientation == UIInterfaceOrientationPortrait) ||
(fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown))
{
// Load the view controller you want to display in landscape mode...
}
}
and can also use:
-(void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
To detect that the orientation change is coming.
In my case I am using didRotateFromInterfaceOrientation to check if the last orientation was portrait and if so load the view I want displayed in landscape mode.
I then implemented the same methods in the viewcontroller I am loading and it is responsible for detecting when the orientation changes back to portrait and dismissing itself from the view stack.
Hope that helps a little.
Paul
Indeed only top-level controller is notified. You're responsible for notifying nested controllers.
Add this to your tab bar controller:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[self.selectedViewController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
}
Do you implement shouldAutorotateToInterfaceOrientation? If not, then that might be why you aren't getting the messages.