iOS orientation change not working also UIDeviceOrientationDidChangeNotification not received - iphone

I am quite new to iOS and I am trying desperately to get orientation changes working in my app. After researching here's what I do:
In my app I have a NavigationController managing seven UIViewController subclasses.
In the projects summary tab I activated all 4 device orientations.
Each UIViewcontroller subclass has a xib file, all xib files have "autoresize subviews" activated.
The UIViewController subclasses all have:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait ||
interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight );
}
they also all have:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
and:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
implemented with an NSLog(...) statements (never printed, debugger also never entering these methods).
Also I was trying to use:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
BOOL getOrientationUpdates = [[UIDevice currentDevice] isGeneratingDeviceOrientationNotifications];
NSLog(#"will receive orientation notifications: %#", getOrientationUpdates?#"YES":#"NO");
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
with
-(void)orientationChanged: (NSNotification*) notification
{
NSLog(#"orientationChanged");
}
and
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] removeObserver:self];
respectively.
when I do beginGeneratingDeviceOrientationNotifications etc. in the AppDelegate's - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
method, orientationChanged: is called once on startup but never again however I rotate the device, when I do it in one of the UIViewController subclasses it is never called!
So far, all I want to achieve is getting orientation notifications to rotate an UIImageView and UIImage (without any layout changes in the different orientations).
UIDeviceOrientation o = [UIDevice currentDevice].orientation;
always returns UIDeviceOrientationPortrait
It might be that I missed something in the docs or on stackoverflow, but I obviously cannot figure out what I need to do/add to get it working in my setup. also I am quite new to stackoverflow, so I hope my post is not violating any platform conventions.
Any help/hints are greatly appreciated. Thank you so much!
EDIT:
getOrientationUpdates is always YES, which appears strange to me as the notification callback selector is never called when I rotate it!
EDIT: in my AppDelegate's didFinishLaunchingWithOptions I am doing:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.regScreenController = [[RegistrationScreenController alloc] initWithNibName:#"RegistrationScreenController" bundle:nil];
navCtrl = [[UINavigationController alloc]initWithRootViewController:self.regScreenController];
[navCtrl setNavigationBarHidden:YES];
self.window.rootViewController = self.regScreenController;
[self.window addSubview:navCtrl.view];
[self.window makeKeyAndVisible];
return YES;

See if you are setting the self.window.rootViewController in your AppDelegate's didFinishLaunchingWithOptions: method, because if you're adding only subviews to the window the orientation change notification does not fire.

Related

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

Adding Two Listener For UIResponder

I have two view Controllers added as a sub-view in the UIWindow. Now the problem is one of the view gets view rotation calls but second is not getting calls for view changing orientation.
Now my problem is how we can get change orientation call for two different view controllers added in UIWindow.
UIWindow only sends rotation messages to its rootViewController. If you want the other view controller to receive them, you have two options:
Write code to make your rootViewController send the rotation messages to the other view controller.
Implement view controller containment. Watch the Implementing UIViewController Containment video from WWDC 2011 to learn how to do this.
Register for device orientation.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(detectOrientation) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
check in the method ,
if (([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft) ||
([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight)) {
}

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

View rotation notifications: why didRotateFromInterfaceOrientation: doesn't get called?

I'm trying to detect any device orientation change so that I can update the views.
I want to update the views whether the orientation is portrait or landscape, so I implemented this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
I know that if I want to update the views to show properly for the current orientation, I will need to implement some of the following methods:
– willRotateToInterfaceOrientation:duration:
– willAnimateRotationToInterfaceOrientation:duration:
– didRotateFromInterfaceOrientation:
– willAnimateFirstHalfOfRotationToInterfaceOrientation:duration:
– didAnimateFirstHalfOfRotationToInterfaceOrientation:
– willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration:
Now, the problem is that I don't understand why none of these methods get fired when I rotate the simulator.
I tried as well this piece of code:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
But still nothing. So I'm wondering, does the simulator fire rotation notifications?
If yes, what am I doing wrong?
You need to add notification observer something like
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
and add the method
- (void) didRotate:(NSNotification *)notification
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationLandscapeLeft)
{
NSLog(#"Landscape Left!");
}
}
Quick summary, change this:
[window addSubview:viewController.view];
to this:
[window setRootViewController:viewController];
I'm sorry if I took somebody's time for a silly mistake of mine. I found out why the method – willRotateToInterfaceOrientation:duration: would never get called. Something brought my attention to the navigation controller:
Only the root view controller's willRotate method is being called. Most likely you have a strange hierarchy of view controllers.
I found these post in another forum and I had a look at the app delegate. My code was as follows:
CGRect bound = [[UIScreen mainScreen] bounds];
window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, bound.size.width, bound.size.height)];
TestViewController *viewController = [[TestViewController alloc] init];
[window addSubview:viewController.view];
[viewController release];
[self.window makeKeyAndVisible];
The problem was that I was not setting any view controller for the window, but I was just adding a view. It was a mistake I did due to the hurry to test something. I had to fix the code like this:
CGRect bound = [[UIScreen mainScreen] bounds];
window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, bound.size.width, bound.size.height)];
TestViewController *viewController = [[TestViewController alloc] init];
[window setRootViewController:viewController];
[viewController release];
[self.window makeKeyAndVisible];

UIImagePickerController in Landscape

I have been searching for an answer to this, but cannot come up with anything. Apparently, iPhone SDK 3.0 made it possible that UIImagePickerController can be displayed in landscape mode - but I am not finding any method that will allow this. I would think that if the application is in landscape by default it would automatically adjust the image controller, but that is not working for me.
Thanks for any help!
No need to subclass; simply override the modalPresentationStyle property.
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.modalPresentationStyle = UIModalPresentationFormSheet;
[viewController presentViewController:picker animated:YES completion:NULL];
I haven't checked whether this is illegal, but it worked for me.
If you want the UIImagePickerController to start(and stay) in Landscape orientation code:
//Initialize picker
UIImagePickerController * picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
//set Device to Landscape. This will give you a warning. I ignored it.
//warning: 'UIDevice' may not respond to '-setOrientation:'
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
//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];
//Set the picker source as the camera
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
//Bring in the picker view
[self presentModalViewController:picker animated:YES];
The method didRotate:
- (void) didRotate:(NSNotification *)notification
{
//Maintain the camera in Landscape orientation
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
}
If you just need to get rid of the warning try
#interface UIDevice ()
-(void)setOrientation:(UIDeviceOrientation)orientation;
#end
I've developed a UIImagePicker class in landscape mode. Works great for applications I've developed: hope it works for you too:
GitHub: https://github.com/imaginaryunit/iOSLandscapeThreadedImagePicker.git
I have an all-landscape app using UIImagePickerController too. Please be noted that if you call UIImagePickerController in Landscape mode, your app is possible to be rejected by Apple Review Team.
I devised a simple work around this issue which make use the shouldAutoRotate delegate. Apple approves this method for an all-landscape app.
See here for the details and downloadable full project source code.
Make a category of UINavigationController and add this method
- (BOOL)shouldAutorotate
{
return NO;
}
Subclass UIImagePickerController and override modalPresentationStyle as follows:
- (UIModalPresentationStyle)modalPresentationStyle
{
if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
return UIModalPresentationFormSheet;
}
return [super modalPresentationStyle];
}
The image-picker is a form-sheet now and no longer in fullscreen-mode, but it looks good in landscape-mode.
This should be totally app-store-safe.
This works for the gallery, not for taking pictures.
I solved this problem as follows: after each change in orientation, I simple re-create picker. Try this. Differently is too crooked...
I am developing a class that tries its best to work in landscape mode.
Check it out on GitHub: RACameraController