When I rotate my iDevice form portrait to landscape, the screen rotates fine, but I'm seeing black borders moving with it, so it looks more 'real'. I find it embarrassing to see in iOS 7 and many apps have thrashed this behaviour (like Instagram).
What I want to do is hide those black borders that look totally unnecessary when rotating a device. How do I disable this standard animation?
In the parent view controller viewdidload method add this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
Then add this method
- (void) didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (UIInterfaceOrientationIsLandscape(orientation) && !self.modalViewController) {
[self presentModalViewController:carouselView animated:YES];
[Globals sharedGlobals].startedAtLandscape = YES;
}
if (UIInterfaceOrientationIsPortrait(orientation) && self.modalViewController) {
[self dismissModalViewControllerAnimated:YES];
[Globals sharedGlobals].startedAtLandscape = NO;
}
}
Then to prevent animation:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if(UIInterfaceOrientationIsLandscape(interfaceOrientation)) {
return NO;
}
return YES;
}
I found the best solution is turn animation off before rotation, and turn it back after rotation.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration
{
...
[UIView setAnimationsEnabled:NO];
}
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
...
[UIView setAnimationsEnabled:YES];
}
Related
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.
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
What would be the correct way to achieve an effect similar to the one in the standard iPod app of the iPhone - when the device is rotated to landscape mode, the view changes to cover flow, but the type of transition is fade and not the rotating screen?
This is how I am loading the modal view:
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
carouselView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:carouselView animated:YES];
}
}
Thanks!
Andrius
I later found that it is more stable to use this solution:
In the parent view controller (in my case it is tab view controller) viewdidload method add this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
and then add this method:
- (void) didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (UIInterfaceOrientationIsLandscape(orientation) && !self.modalViewController) {
[self presentModalViewController:carouselView animated:YES];
[Globals sharedGlobals].startedAtLandscape = YES;
}
if (UIInterfaceOrientationIsPortrait(orientation) && self.modalViewController) {
[self dismissModalViewControllerAnimated:YES];
[Globals sharedGlobals].startedAtLandscape = NO;
}
}
And finally if you want to prevent the rotation animation, modify this method like this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if(UIInterfaceOrientationIsLandscape(interfaceOrientation)) {
return NO;
}
return YES;
}
I am developing a iphone/ipad app.
Problem is that when i change the orientation of ipad portrait to Landscape, back button of navigation controller stops working.
It"s work fine when orientation not changed.
i am using this code
- (BOOL) isPad{
#ifdef UI_USER_INTERFACE_IDIOM
return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
#else
return NO;
#endif
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if ([self isPad]) {
return YES;
}
else
{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
}
What"s the wrong with this code?
This will work :
-(void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
-(void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
Now implement the following method :
-(void)checkOrientation
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationLandscapeLeft||orientation==UIDeviceOrientationLandscapeRight)
{
// Set x coorinate of views you want to change
}
else
{
// Set x coordinates of views to initial x xoordinates.
}
}
Create recievedRotate :
- (void)receivedRotate:(NSNotification *)notification
{
[self checkOrientation];
}
In viewDidLoad :
-(void)viewDidLoad
{
// Code
[self checkOrientation];
}
My guess is that your button is out of the view's frame and thus won't receive touch events. I think something is wrong with the way your app redraws on orientation change.
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];
}
}