How to get Rotation events on ViewController within UITabBarController->UINavigationController? - iphone

I wanna show a full-screen Landscape view whenever the user rotates the iOS device from portrait to landscape, where the portrait view is a view within a TabBar and NavigationController.
However, willRotateToInterfaceOrientation:duration: is never called. I tested also to add the ViewController as Observer of UIDeviceOrientationDidChangeNotification events, but this notification is called also with undefined orientation.
What would be the best and easiest way for the given task?

There are also UIApplicationWillChangeStatusBarOrientationNotification and UIApplicationDidChangeStatusBarOrientationNotification notifications.
The userInfo dictionary contains an NSNumber object that encapsulates a UIInterfaceOrientation value. Use UIApplicationStatusBarOrientationUserInfoKey to access this value
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
- (void) didRotate:(NSNotification *)notification{
NSNumber *num = [[notification userInfo] objectForKey:#"UIApplicationStatusBarOrientationUserInfoKey"];
NSLog(#"%d", [num intValue]);
}

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.

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

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];

Present view when local notification is received

With iOS 5 and storyboarding, what is the best way to present a view when the user enters the app after having received a localnotification?
I have read that using the NSNotificationCenter is the way to do it but is that also so with storyboarding and segues?
This is exactly how I implemented it. In the AppDelegate's didFinishLaunchingWithOptions: method, I did the following:
UILocalNotification *notification =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
[self application:application didReceiveLocalNotification:notification];
I did this so that I could keep logic in a single place. In the didreceiveLocalNotification: method, I then used the NSNotificationCenter:
// Let another view handle the display
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:#"SHOW_VERSE"
object:self
userInfo:notification.userInfo];
The view that handles the display is the first UIViewController for the Storyboard. In that class, in the viewDidLoad method:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receivedLocalNotification:)
name:#"SHOW_VERSE"
object:nil];
This works very well for me. Hope it helps.

Problem in dismissing fullscreen mode of MPMoviePlayerViewController

In my application i am using MPMoviePlayerViewController to play online video. The video should play in fullscreen mode when in landscape and the fullscreen mode should be dismissed when it rotates to portrait mode.
I am able to play the video in fullscreen mode. But not able to dismiss it when the device orientation changes to portrait mode.
I am using [mpController.moviePlayer setFullscreen:FALSE animated:YES];
Someone please help.
Thanks in advance.
I assume you're trying to detect the orientation change in the view controller that present MPMoviePlayerViewController? This code won't be fired after the movie player view controller is presented because it—not its parent—will receive the rotation events.
You can, however, subscribe to the device rotation notifications and dismiss the movie player view controller whenever you detect a rotation to portrait:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self selector:#selector(deviceOrientationDidChange:) name: UIDeviceOrientationDidChangeNotification object:nil];
// Present MPMoviePlayerViewController here
Elsewhere in the same view controller:
- (void)deviceOrientationDidChange:(NSNotification *)notification
{
UIDevice *currentDevice = [UIDevice currentDevice];
[currentDevice endGeneratingDeviceOrientationNotifications];
if (...) // Check currentDevice.orientation to see if it's what you want
{
// Do whatever you want now that you have the orientation you want
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
Set observer for UIDeviceOrientationDidChangeNotification or UIApplicationWillChangeStatusBarOrientationNotification, check new required orientation and set new mode for MPMoviePlayerViewController.

Is there a way to catch an WillRotateToInterfaceOrientation event from an UIView?

every UIViewController has a method called willRotateToInterface.
Is it possible to do this within a UIView too?
Does this match the idea of model view controller ?
The only way I can think of is to send the event from the UIViewController to the UIView.
Is there a global variable for the current orientation?
Observe UIDeviceOrientationDidChangeNotification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
...
- (void)orientationDidChange:(NSNotification *)note
{
NSLog(#"new orientation = %d", [[UIDevice currentDevice] orientation]);
}
UIDevice Class Reference
I should note that yo uneed to add -beginGeneratingDeviceOrientationNotifications when you want these notifications to be sent, and call -endGeneratingDeviceOrientationNotifications when you want them to stop. There is a battery impact of generating these, so you should only do so when your view is on screen. UIViewController does all this for you, so if you have a view controller, it is worth letting it do the work.
If you just want to adjust view to new size/layout when orientation changes, you should just override its layoutSubviews method.
It will be called whenever size of the view changes, which usually happens when view is rotated.
You can subscribe to a global notification and get a call when the device is rotated, it wont do anything for you though..
[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:#"UIDeviceOrientationDidChangeNotification" object:nil];