populate entire landscape screen with new uiview upon device orientation change from portrait to landscape - iphone

So my iPhone application currently has a tabviewcontroller that populates the entire screen. The app only runs in portrait mode. My task has been to detect device orientation changes, and once it changes to landscape, have a new uiview populate the entire screen.
I already have the device orientation change detection working. I've used an NSNotificationCenter to successfully call a helper method, deviceOrientationChanged, once an orientation change is detected. IF the change was to landscape mode, I run a certain block of code.
In this block of code I have already tried various things, none of which are successful. Simply saying self.view = newViewThing; does not work because the statusbar is still present at the top and the tabs are still present at the bottom.
I have also tried adding this newViewThing as a subview to the UIWindow. This did not work because while the view was added, it was not oriented correctly.
THE QUESTION IS: is there a way to load an entirely new uiview once a device orientation change is detected? Thank you in advance.

Yes, there is a way to load a new view. I make it in my app that way:
- (void)orientationChanged:(NSNotification *)notification
{
// We must add a delay here, otherwise we'll swap in the new view
// too quickly and we'll get an animation glitch
[self performSelector:#selector(updateLandscapeView) withObject:nil afterDelay:0];
}
- (void)updateLandscapeView
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
[self presentModalViewController:self.landscapeView animated:YES];
isShowingLandscapeView = YES;
}
else if (deviceOrientation == UIDeviceOrientationPortrait && isShowingLandscapeView)
{
[self dismissModalViewControllerAnimated:YES];
isShowingLandscapeView = NO;
}
}
And also I have added this code to viewDidLoad:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification object:nil];
and this code to dealloc:
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];

Related

Detecting iOS orientation change instantly

I have a game in which the orientation of the device affects the state of the game. The user must quickly switch between Landscape, Portrait, and Reverse Landscape orientations. So far I've been registering the game for orientation notifications via:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
But it is far too slow - there seems to be about a second delay between rotating the phone and the notification actually being fired. I need a way to INSTANTLY detect changes in the device's orientation. I have tried experimenting with the gyroscope, but am not yet familiar enough with it to know whether or not it is the solution I am looking for.
Add a notifier in the viewWillAppear function
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
The orientation change notifies this function
- (void)orientationChanged:(NSNotification *)notification{
[self adjustViewsForOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}
which in-turn calls this function where the moviePlayerController frame is orientation is handled
- (void) adjustViewsForOrientation:(UIInterfaceOrientation) orientation {
switch (orientation)
{
case UIInterfaceOrientationPortrait:
case UIInterfaceOrientationPortraitUpsideDown:
{
//load the portrait view
}
break;
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
{
//load the landscape view
}
break;
case UIInterfaceOrientationUnknown:break;
}
}
in viewDidDisappear remove the notification
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
I guess this is the fastest u can have changed the view as per orientation
That delay you're talking about is actually a filter to prevent false (unwanted) orientation change notifications.
For instant recognition of device orientation change you're just gonna have to monitor the accelerometer yourself.
Accelerometer measures acceleration (gravity included) in all 3 axes so you shouldn't have any problems in figuring out the actual orientation.
Some code to start working with accelerometer can be found here:
How to make an iPhone App – Part 5: The Accelerometer
And this nice blog covers the math part:
Using the Accelerometer
Why you didn`t use
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
?
Or you can use this
-(void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
Or this
-(void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
Hope it owl be useful )
For my case handling UIDeviceOrientationDidChangeNotification was not good solution as it is called more frequent and UIDeviceOrientation is not always equal to UIInterfaceOrientation because of (FaceDown, FaceUp).
I handle it using UIApplicationDidChangeStatusBarOrientationNotification:
//To add the notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didChangeOrientation:)
//to remove the
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
...
- (void)didChangeOrientation:(NSNotification *)notification
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsLandscape(orientation)) {
NSLog(#"Landscape");
}
else {
NSLog(#"Portrait");
}
}
Try making your changes in:
- (void) viewWillLayoutSubviews {}
The code will run at every orientation change as the subviews get laid out again.
#vimal answer did not provide solution for me. It seems the orientation is not the current orientation, but from previous orientation. To fix it, I use [[UIDevice currentDevice] orientation]
- (void)orientationChanged:(NSNotification *)notification{
[self adjustViewsForOrientation:[[UIDevice currentDevice] orientation]];
}
Then
- (void) adjustViewsForOrientation:(UIDeviceOrientation) orientation { ... }
With this code I get the current orientation position.

BAD_ACCESS while landscape orientation

I want to keep my view landscape. For that i am using this code but BAD_ACCESS is coming.
Here I am writing this code for camera overlayView.
-(void)viewDidLoad
{
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeLeft];
//Set Notifications so that when user rotates phone, the orientation is reset to landscape.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
//Refer to the method didRotate:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:#"UIDeviceOrientationDidChangeNotification" object:nil];
[super viewDidLoad];
}
- (void) didRotate:(NSNotification *)notification
{
//Maintain the camera in Landscape orientation
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeLeft];
}
Why it is giving BAD ACCESS ?
To keep your view landscape, just return NO in your shouldAutorotateToInterfaceOrientation: method for portrait orientations, like tihs :
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}

Trying to understand how shouldAutorotateToInterfaceOrientation and UIDeviceOrientationDidChangeNotification

I have an interface that I want to startup in landscape orientation. After startup when the user rotates the device to portrait I am displaying a day view calendar. When returning to landscape orientation the calendar is dismissed. Everything works great in every orientation with my application user interface displaying properly in landscape orientation and the calendar displaying properly in portrait orientation.
The problem is if the user is holding the iPhone in landscape orientation on startup. No matter what I do I cannot get it to startup with my user interface in landscape mode. My UIDeviceOrientationDidChangeNotification method fires twice, the first time [UIDevice currentDevice].orientation is landscape, the second in it is portrait. The end result is the the user interface rotates to portrait mode and displays the day view. Not what I want. I want the user interface to stay in landscape orientation until the user physically rotates the device from landscape to portrait.
I don't understand why it fires with a landscape [UIDevice currentDevice].orientation when the user is holding the device in portrait orientation.
Here is what my code looks like in the viewController...
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if ((interfaceOrientation == UIDeviceOrientationPortrait)|| (interfaceOrientation == UIDeviceOrientationPortraitUpsideDown)) {
return NO;
}
return YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
showingCalendar = NO;
initializing=YES;
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
-(void)didRotate:(NSNotification *)notification {
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if ((deviceOrientation == UIDeviceOrientationPortrait) || (deviceOrientation == UIDeviceOrientationPortraitUpsideDown)) {
if ((!showingCalendar) && (!initializing)) {
showingCalendar = YES;
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:YES];
GCCalendarPortraitView *calendar = [[[GCCalendarPortraitView alloc] init] autorelease];
navigationController = [[UINavigationController alloc] initWithRootViewController:calendar];
[self presentModalViewController:navigationController animated:YES];
}
}else if ((deviceOrientation == UIDeviceOrientationLandscapeRight) || (deviceOrientation == UIDeviceOrientationLandscapeLeft)) {
if (showingCalendar) {
showingCalendar = NO;
if (deviceOrientation == UIDeviceOrientationLandscapeRight){
[self dismissModalViewControllerAnimated:YES];
}else if (deviceOrientation == UIDeviceOrientationLandscapeLeft){
[self dismissModalViewControllerAnimated:YES];
}
}else {
initializing = NO;
}
}
}
I found a workaround to my problem. In viewDidLoad I started a scheduledTimerWithTimeInterval and moved beginGeneratingDeviceOrientationNotifications to the selector method.
Now the notification never fires more than once. The user gets landscape at startup no matter which way the device is being held and after startup all the rotations work perfectly.
Here is my modified code. Everything else stayed the same...
- (void)viewDidLoad {
[super viewDidLoad];
showingCalendar = NO;
initializing=YES;
timer = [NSTimer scheduledTimerWithTimeInterval:.55 target:self selector:#selector(startOrientationNotifications) userInfo:nil repeats: NO];
}
-(void)startOrientationNotifications {
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
i wouldn't generate a beginGeneratingDeviceOrientationNotifications,
a simple way could be to use a BOOL to check when portrait is allowed in
shouldAutorotateToInterfaceOrientation
something like this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if ((interfaceOrientation == UIDeviceOrientationPortrait)|| (interfaceOrientation == UIDeviceOrientationPortraitUpsideDown)) {
return portraitIsAllowed;
}
return YES;
}
then just change it when needed in other methods .
And keep in mind that shouldAutorotateToInterfaceOrientation is called every time user rotate device AND also when you load (instantiate) your controller the first time

interfaceorientation in certain views only

I have a Main View that contatins uitableview, and I am pushing another view when the user taps on a row. I want only the child views to rotate and have written code for that. But the problem is once I push the view, it also enables the orientation on the Main View.
When I rotate the main view to the left, the statusbar also changes to the left. I have not applied any orientation on the Main View. I have done the following on the MainView but code still the status bar doesn't change.
-(void) viewDidLoad
{
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(receivedRotate:) name: UIDeviceOrientationDidChangeNotification object: nil];
}
-(void) receivedRotate: (NSNotification*) notification
{
UIDeviceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
if(interfaceOrientation != UIDeviceOrientationUnknown)
{
if (interfaceOrientation==UIInterfaceOrientationLandscapeLeft)
{
[self shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
}
}
How can I disable interfaceOrientation in the Main view?
in your main view controller, override this delegate:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; // Override to allow rotation. Default returns YES only for UIDeviceOrientationPortrait
{
if((toInterfaceOrientation == UIInterfaceOrientationLandscapeRight))
return YES;
return NO;
}
Now your main view controller will always be in landscape mode.
And remove all your code which you mentioned above.
you should not call the "shouldAutorotateToInterfaceOrientation:" method.

Change View on only one Tab in Tabbar-Application (Landscape-Portrait)?

How can I change the view when rotating the iphone (change nib's).
But it should only happens in one single tab!
I tried it with:
- (void)viewDidLoad {
LandscapeViewController *viewController = [[LandscapeViewController alloc]
initWithNibName:#"LandscapeView" bundle:nil];
self.landscapeViewController = viewController;
[viewController release];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification object:nil]; }
- (void)orientationChanged:(NSNotification *)notification
{
[self performSelector:#selector(updateLandscapeView) withObject:nil afterDelay:0];
}
- (void)updateLandscapeView
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
[self presentModalViewController:self.landscapeViewController animated:YES];
isShowingLandscapeView = YES;
}
else if (deviceOrientation == UIDeviceOrientationPortrait && isShowingLandscapeView)
{
[self dismissModalViewControllerAnimated:YES];
isShowingLandscapeView = NO;
}
}
But then the Landscape-View appears in all Tabs. (When this code is loaded once).
Any idea?
What's happening is your view controller is receiving the UIDeviceOrientationDidChangeNotification notification whenever the rotation changes, whether or not it is being displayed. Instead try using the built-in methods of UIViewController that are meant for responding to rotations.
http://tinyurl.com/ycb8of2
This is from Apple's Docs (View Controller Programming Guide):
Tab Bar Controllers and View Rotation
Tab bar controllers support a portrait
orientation by default and do not
rotate to a landscape orientation
unless all of the root view
controllers support such an
orientation. When a device orientation
change occurs, the tab bar controller
queries its array of view controllers.
If any one of them does not support
the orientation, the tab bar
controller does not change its
orientation.
So, I'm not sure that the Tab Bar Controller is designed to rotate just for a single view.
Tanks for your comments!
I found a workaround:
//Remove Observer if not in Landscape
- (void)viewDidDisappear:(BOOL)animated {
if (isShowingLandscapeView == NO) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
}
//Add Observer if not in Landscape
- (void)viewDidAppear:(BOOL)animated {
if (isShowingLandscapeView == NO) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
}