I have my app 98% complete but stumbled across an issue which is stumping me!
Any help would be great.
Basically....I have a 5 tab controller. I have done some remodelling on the first tab view so that moving it from portrait to landscape moves everything around so that it looks great.
The other 4 tabs also move from portrait to landscape and back with ease.
Now....the issue I stumbled across was that if I had say tab 5 in portrait, moved it to landscape and then tapped tab 1, the only bits in tab 1 that orientate to landscape are the bits i fixed in the sizing inspector.
The bits I've re-positioned in code won't landscape.
If I however turn that tab 1 portrait and then back to landscape, it works!
The label fields I've moved with code using .frame and CGRectMake is in the
-(void)willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation duration: (NSTimeInterval)duration
method.
.......do I need to put some code in the AppDelegate which the TabController resides in??
It makes sense to me that the TabBarController knows the orientation.
When you tap a tab.....what method gets actioned first before the view loads??
I think I need to catch the orientation then, adjust my label positions and then load the view.......
I would appreciate any thoughts?
Gaz.
EDIT: It's like what I want to do is be able to change things in tab 1 if the orientation changes in other tabs.can you do that? It seems that the 5 tab views are separate...
Try making a call to your rotation method in your view controllers viewWillAppear method, checking for correct orientation, and moving what you need to move. Should look something like this
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self autoRotate];
}
I think that if you move something in code it maintains it's position, so you need to change it's position every time the orientation changes in every view controller where you move objects in code. Like:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
if(UIInterfaceOrientationIsLandscape(toInterfaceOrientation)){
//change positions with animation using duration
}else if(UIInterfaceOrientationIsPortrait(toInterfaceOrientation)){
//change positions with animation using duration
}
}
I hope it helps.
Related
I'm using an NIPagingScrollView to display several pages on the iPhone.
Everytime I flick to a page, the next page is also pre-loaded, which is fine.
When I rotate the iPhone from Portrait to Landscape mode, I let layoutSubviews do the re-layouting in my subclass of NIPageView. The NIPagingScrollView is set to auto-stretch in width and height to stay fullscreen. This works for the current page.
But when I flick to the next page, the layout is broken, as it was prefetched before and also layouted by an automatic call to layoutSubviews.
I guess the origin is not updated right on the next page on rotation, or something like that.
Has someone a hint on how I can avoid this problem (other than not using Landscape)? And is this a bug in Nimbus?
EDIT: I discovered that NIPagingScrollView provides the methods willRotateToInterfaceOrientation:duration: and willAnimateRotationToInterfaceOrientation:duration: which should be called by the view controller. I implemented these calls, but it still does not help.
Indeed NIPagingScrollView provides those methods, but if you look at them, you'll see that the layout computations are based on the scrollview frame values.
So if you want the correct values to be given to your paging scroll view, just for example, the frame or your main view (the controller view) to the paging scroll view (_scrollView in the example).
That way, just before the animation, your paging scroll view will have the correct waited frame, and your layout will be recomputed correctly.
- (void)willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation
duration: (NSTimeInterval)duration {
// Your missing line of code to set the scroll view frame values
[self->_scrollView setFrame:self.view.bounds];
[self->_scrollView willAnimateRotationToInterfaceOrientation: toInterfaceOrientation
duration: duration];
[super willAnimateRotationToInterfaceOrientation: toInterfaceOrientation
duration: duration];
}
I need a way to force the orientation back to portrait on rotate.
The problem is that I have a Tab bar controller, but only want one of the tabs to autorotate.
So I have allowed rotation on all tabs and now I need a way to intercept a rotation on a tab where I don't want to allow rotation.
I'm guessing I can do this on - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration?
Thanks
Tom
There is no way to force rotation. In case of a UITabBarController you are out of luck. It is an all or nothing situation wrt interface rotation. If one of your tabs cannot rotate then the whole UITabBarController stays in portrait mode fixed.
Maybe this would even work if implemented in all your viewControllers:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return (return ([[self.tabBarController.selectedViewController class] isSubclassOfClass:[TurnableViewController class]]) || UIInterfaceOrientationIsPortrait(toInterfaceOrientation);
}
This will, however, not turn back your view if you switch back to a non-turnable, until you tilt your device...
I have been spending many frustrating hours trying to get rotations working on the iPhone version of Tunepal.
Firstly, I have a tab bar controller, with a navigation controller controlling each of the views.
I actually only want one of my views to be able to rotate and that is the TuneDisplay.
I have a subclassed the UITabBarController and overridden the shouldAutorotateToInterfaceOrientation:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation {
if (self.selectedViewController != nil)
{
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
else
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
}
In each of the view controllers for each of the tabs I have overridden the method and returned YES for each orientation I want to support. All well and good and everything works as it should. If I try and do a rotation on a tab that doesn’t support the rotation, nothing happens.
The problem occurs if I move from a tab thats rotated to a tab that isnt supposed to support that rotation. The new tab is displayed rotated too! Screenshots for all this are included here:
http://tunepal.wordpress.com/2010/04/20/rotation-woes/
Is there any way I can make it rotate back to portrait on tapping the tab?
I have tried the unsupported setOrientation trick, but firstly it doesnt work correctly and secondly I received a warning from Apple for including it in my last build.
If (as I suspect) there is no way to limit this behavior:
How do I make the microphone image scale when I rotate the device?
How do I make the buttons and the progress bar expand to fit the witdh of the toolbar?
Also, one of the tabs that rotates ok has a table, with a search bar. The first time I rotate to the right or to the left, I get a black bar to the right of the search bar. If I subsequently rotate back and rotate again, the bar disappears! I have enabled the struts and springs things on the search bar in the interface builder and it looks like it should behave correctly.
Any ideas about how to fix this?
Ideas, feedback much appreciated
Bryan
This isn't a full answer. Rotation is seriously inconsistent. You have done the right things. Several aspects don't work in the simulator, so you need to confirm all your testing on a device. Table headers and search bars don't resize to full width in older OS versions, so stick with 3.1.3 or higher.
Commonest problems:
implement the shouldAutorotateToInterfaceOrientation: to return YES;
if you use navigation controllers the root view controller must support the orientation;
if you have a toolbar the view controller for all items must support it;
and same for a tab bar controller.
You may need to turn on orientation notifications to get more useful information out of the device:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification object:nil];
Remember to turn it off and remove yourself from the notifications when you are done; this is supposed to have a high overhead.
Set the view contentMode property for your image to resize; together with autoresizingMask, which you are setting in IB, you should be OK.
Remember also that you can use two different nibs for portrait and landscape modes. There is an example project that sort of does this ("WhichWayIsUp"); see the View Controller Programming Guide also ("Creating an Alternate Landscape Interface").
If the rotation methods are being called, then the UI should be rotated consistently. You will find that they aren't always called when they should be.
It isn't hard to call a rotational transform on your views to force a rotation. It shouldn't be needed, but sometimes that's the only way they will rotate.
view.transform = CGAffineTransformMakeRotation(M_PI * n);
If you get it figured out, let us know.
It sounds like you are handling the rotation correctly while the tab is displayed. However, as you know, there's no quick way to switch rotations. What you will have to do is rotate the view yourself using CGAffineTransform. See this question: Is there a documented way to set the iPhone orientation?
To scale the image, you should be able to click the arrows inside the UIImageView housing the image in Interface Builder. There's a little arrow in the upper right hand corner you can click to see how the view behaves when it's rotated to make sure it scales correctly. But you'd probably be better off not scaling the image and hadling the rotation as in the answer to the linked question.
I have two apps, both of which force the user to use the iPhone in landscape mode, in order to have a wider screen, instead of a taller one.
One of the things I have found is that my first view will look fine, but all other views come up with their subviews (UIButtons, UIPicker, UIViews) squeezed to one side or clipped (depending on whether the elements were set to move, resize or stay in the same position as the view size changed). All my views are designed in IB in the landscape orientation. My underlying UIWindow, and everything I can think of has been laid out in landscape orientation. Even my plist file has the UIInterfaceOrientationLandscapeRight flag set.
Now, if I load all my views at the same time as my rootview controller, then I have no problems. But if I have views loaded later, they get clipped or squeezed.
The only way to get around the problem was to add the following line in my code that flips in a new view:
[coming.view setFrame:CGRectMake(0, 0, 480, 300)];
Anyone know why I need to do this? Is it just that the iPhone assumes that loaded views are 300x480 unless a transform gets applied to them?
Thanks.
ps. This is what the view looks like if I don't call setFrame, as described above:
alt text http://files.me.com/mahboud/ljhvun
All viewcontrollers that get loaded after the first one will have their screen similarly squeezed down. For some reason the first viewcontroller doesn't have this issue.
I think you want to use landscape mode in each single view in your app. And you want the nib to be landscape mode too. You can resize the view to (0,0,480,300 for statusbar, 320 for non-statusbar) in nib. And design what you want. Finally, in view controller return no for autorotate. And finally transform the view and rotate.
I had a similar problem, asked the question on SO, and then figured it out and answered it myself. You may want to check it out.
A proper answer will depend on knowing how you are forcing landscape orientation. If you are doing this through UIViewController and company, it should be relatively simple; for other methods probably more complex.
In the simple case, you should be able to override shouldAutorotateToInterfaceOrientation: on your view controller, setup your views in Interface Builder, and set the UIInterfaceOrientation key to UIInterfaceOrientationLandscapeRight in your Info.plist and be set.
A simple way I fixed this was to have my root view controller subclass UINavigationController, and implement shouldAutorotateToInterfaceOrientation to handle landscape view ie,
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return ((interfaceOrientation == UIInterfaceOrientationLandscapeLeft) ||
(interfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
Every view controller that is pushed to the navigation controller seems to appear in landscape too.
I have been reading a ton on rotation, but not finding a solution to my query. Here goes:
I have a portrait application with a tabbar and hidden navigation controller in my tab. At a point in the app, the next view must be landscape.
The XIB layout has been done in landscape, so I want to bring up the xib without any translation or pixel moving code. (not thinking this is required) I have tried just pushing the view (remains in portrait), shifting the view using various methods (non seem to line thing up properly).
Is there a way to tell the view that it is already laid out for landscape prior to it being opened?
Thanks in advance!
Found it, this code does the trick in the viewdidload:
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
self.view.bounds = CGRectMake(0.0, 0.0, 460.0, 320.0);
Still have 1 odd thing. No matter what I do to set the navigation bar to hidden, it does not rotate, and stays at the left side of the view.
[[self navigationController] setNavigationBarHidden:YES animated:NO];
Has anyone seed this behavior and solved?
I'm positive that you cannot 'force' a rotation. The system decides when to change the orientation of the device; so the 'orientation' properties are essentially read-only. I looked into this same problem a long time ago when I wanted to make sure a particular view always displayed in one orientation.
Due to this, most apps allow all of their views and view controllers to work in any of the orientations the app supports. I've found that trying to restrict the behavior of some views and view controllers ultimately creates more hassle, and can cause issues when transitioning between views and view controllers.
The code you posted will work for your view. You are not actually changing the orientation at all; your view is just behaving like it has been rotated by drawing in a rotated fashion. I'm not sure if you can do the same thing to the navigation bar or not, but it's worth a shot. If you are able to control the view properties of the navigation bar (it is a UIView as well), applying the same pattern you are using for your custom view should work.
Most apps that want a view to only be in landscape ultimately force their entire app to be in landscape. For instance, Flight Control only supports one orientation. Thus, the drawing code is pretty simple; regardless of orientation, just draw the view and rotate it to the one orientation it supports (either landscape left or right).
Your app's design wouldn't be that easy... it sounds like you are not designing a full-screen app. You would have to worry about the navigation bar and status bar being properly drawn.