UIViewController: Identify orientation is in progress - iphone

I'm developing an iPhone application. Is there any default state/accessor in UIViewController that tells if orientation is in progress?
Currently I have done the following:
A BOOL member in subclass of UIViewController say isOrientationChangeInProgress
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
isOrientationChangeInProgress = YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
isOrientationChangeInProgress = NO;
}
Is there any better way?

Apple advises starting any animations in willRotateToInterfaceOrientation:duration:, so he rotation happens soon after that method is called. The duration property tells you how long it takes didRotateFromInterfaceOrientation: is called once the rotation is finished, or about duration seconds later.
So, I suppose your BOOL is the best thing to to do in hindsight. I don't understand why you need to know this though; maybe your desired outcome can be achieved a different way. What are you trying to do?

You can ask to be told device orientation information as well:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification object:nil];
You then get told in orientationChanged: when the change happens

Related

How to allow only particular view to rotate? (iPhone)

I am installing AdMob's ad, and there is a GADBannerView.
After installing, a banner show, and if you click on it, a page will slide out coving the whole screen, and displaying advertising contents in it.
The question is, some advertising contents, such as video, had to be played landscape. However, I don't want other part of my application to rotate, as the app is not designed to be viewed in landscape.
So, how can I implement something which can achieve such function?
Try to use Notification for this. a notification calls a selector every time when ur device orientation is changed.
write this in your viewDidLoad:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(setScreenWithDeviceOrientation:) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
and then define the selector as follows:
-(void)setScreenWithDeviceOrientation:(NSNotification *)notification
{
UIDeviceOrientation orientation=[[UIDevice currentDevice] orientation];
if(orientation==UIInterfaceOrientationPortrait) //Portrait orientation
{
// setView frame for portrait mode
}
else if(orientation==UIInterfaceOrientationPortraitUpsideDown) // PortraitUpsideDown
{
// setView frame for upside down portrait mode
}
else if(orientation==UIInterfaceOrientationLandscapeLeft)
{
// setView frame for Landscape Left mode
}
else if(orientation==UIInterfaceOrientationLandscapeRight) //landscape Right
{
// setView frame for Landscape Right mode
}
else
{
NSLog(#"No Orientation");
}
}
this method fired everytime when ur device changes orientation. Based on the current orientation you should adjust your view.
I hope this will help you.
Are you working with iOS 6? You should be able to just restrict what orientations your view controller handles in this case. For example, in your view controller that handles your GADBannerView, you can just put:
// Tell the system what we support
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
// Tell the system It should autorotate
- (BOOL) shouldAutorotate {
return NO;
}
// Tell the system which initial orientation we want to have
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
And that should make it so that your viewcontroller only supports portrait.
Have you looked at this question and answer? This explains how a single view can work in a certain orientation that is not supported by the rest of the application:
AutoRotate ONLY MpMoviePlayerControler iOS 6

Notification about iphone oriention

I want to get notification when a user is rotate the screen to landscape or portrait,
it is possible?
I find couple of article but i didn't found answer for this.
If you want to be notified when the device has been rotated you can either implement the shouldAutorotateToInterfaceOrientation: method in your view controller or you can register to receive the UIDeviceOrientationDidChangeNotification.
Before we start, the device orientation and the interface orientation can be different. The device may be landscape but the interface may remain portrait depending on how the app has been written. Device notifications are sent shortly before the interface orientation is changed to match the device orientation. If you don't want the interface orientation to change you should implement the shouldAutorotateToInterfaceOrientation: method in your view controller to return NO. This will stop the interface orientation being updated.
From your question it sounds like you want to receive notifications so I think you want to use the second method. You can enable UIDeviceOrientationChangeNotifications using:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
There is a corresponding:
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
You can register to receive the notifications in the normal way, using the NSNotificationCenter and registering to receive the UIDeviceOrientationDidChangeNotification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
Finally, you would implement the method to be called when the notification is received as follows:
- (void)orientationChanged:(NSNotication *)notification {
UIDeviceOrientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationPortrait ||
orientation == UIDeviceOrientationPortraitUpsideDown) {
// Portrait
} else {
// Landscape
}
}
As you can see, the orientation can be accessed using the orientation instance method of UIDevice.
You need to add local notification in the landscape orientation in shouldAutorotateToInterfaceOrientation method
try like this:
if(interfaceOrientation == UIInterfaceOrientationLandscapeRight || UIInterfaceOrientationLandscapeLeft)
{
here schedule your local notification
}

Detecting when camera's iris is open on iPhone

For a cutom camera overlay I need to find out, when the iris is opened, because my overlay will allways shown while the iris is close (and then animating to open).
Any ideas ?
You can listen for the PLCameraViewIrisAnimationDidEndNotification notification. Since this is not officially documented, you might be in violation of the Apple TOS, but I think so long as you write your code so that it's defensive against the possibility that the name or contract of this notification might change (so in the future you might not get the event) you'll probably be ok. In other words, use a timer or other technique to ensure that the thing you want done when the iris is open will definitely happen eventually even if you never get the notification...
Trivial example without the defensive programming. (Of course, you can register an interest only for this specific notification as well, see the docs for the notification center.)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(notificationCallback:)
name:nil
object:nil
];
- (void) notificationCallback:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"PLCameraViewIrisAnimationDidEndNotification"]) {
NSLog(#"Iris open");
// we don't need to listen any more
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
}
It seems that PLCameraViewIrisAnimationDidEndNotification no longer gets notified in iOS5.
I can't figure out what is a suitable solution when the iris has finished opening, there must another option rather than using a 3 second timer.
Check here: https://devforums.apple.com/message/561008#561008
I have a ViewController (ALImagePickerController) which holds, initializes and presents the UIImagePickerController as a child view controller (I have another child view controller for presenting the taken image which is not shown here) and I present (as a modal) the ALImagePickerController when I want to use the camera. So during this the viewDidAppear of the ViewContoller I add an animation to bring in the camera overlay gracefully as the shutter animation disappears.
#interface ALImagePickerController ()
#property (nonatomic) UIImagePickerController *cameraController;
#property (nonatomic) CameraOverlayView *overlayView;
....
#end
#implementation ALImagePickerController
....
- (void)viewDidLoad {
[super viewDidLoad];
[UIApplication sharedApplication].statusBarHidden = YES;
self.cameraController = [UIImagePickerController new];
self.cameraController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.cameraController.delegate = self;
self.cameraController.allowsEditing = NO;
self.cameraController.showsCameraControls = NO;
....
self.overlayView = [CameraOverlayView new];
....
self.overlayView.alpha = 0;
self.cameraController.cameraOverlayView = self.overlayView;
....
// add as child view controller
[self addChildViewController:self.cameraController];
[self.view addSubview:self.cameraController.view];
[self.cameraController didMoveToParentViewController:self];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[UIApplication sharedApplication].statusBarHidden = NO;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// smoothly bring in the overlay as the native camera shutter animation opens.
[UIView animateWithDuration:0.2 delay:0.3 options:UIViewAnimationCurveEaseOut animations:^{
self.overlayView.alpha = 1.f;
} completion:nil];
}
....
#end
The way I solved this problem is I initialize all the elements with the hidden property set to YES, then call a 3-second delayed selector after I call the camera, where I set all the elements to hidden = NO. It's not an ideal solution but it seems to work, and any lag after the iris is opened is negligible.
You should already know when the camera is ready to take a picture. At least the way I use a custom camera overlay, I init the view with something like self.sourceType = UIImagePickerControllerSourceTypeCamera; and the other usual setup, and the camera is ready (or "iris is open") at that point.
In summary, if one is using a custom camera overlay the way I am used to using it, one will know when the iris is open because it is under your control.

how do I detect the iPhone orientation before rotating

In my program I'm moving things based on rotation, but I'm not rotating the entire view. I'm Using :
static UIDeviceOrientation previousOrientation = UIDeviceOrientationPortrait;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:viewController.view];
[window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:#"UIDeviceOrientationDidChangeNotification" object:nil];
}
- (void) didRotate:(NSNotification *)notification{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
[self doRotationStuff:orientation: previousOrientation];
previousOrientation = orientation;
}
This works as long as, when the program is launched, the device orientation is portrait, but not if the initial orientation is landscape or upside down, because [self doRotationStuff] makes changes relative to the difference from the previous orientation.
Is there a way to detect the orientation either at launch, or right before the device is rotated?
Depending on your circumstances, a simpler option may be the interfaceOrientation property of the UIViewController class. This is correct before a rotation.
Updated:
So, from the comment discussion, it appears that you can't rely on [UIDevice currentDevice].orientation until the orientation actually changes for the first time. If so, you could probably hack it by getting raw accelerometer readings.
#define kUpdateFrequency 30 // Hz
#define kUpdateCount 15 // So we init after half a second
#define kFilteringFactor (1.0f / kUpdateCount)
- (void)applicationDidFinishLaunching:(UIApplication *)app
{
[UIAccelerometer sharedAccelerometer].updateInterval = (1.0 / kUpdateFrequency);
[UIAccelerometer sharedAccelerometer].delegate = self;
accelerometerCounter = 0;
...
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)accel
{
// Average out the first kUpdateCount readings
// acceleration_[xyz] are ivars typed float
acceleration_x = (float)accel.x * kFilteringFactor + acceleration_x * (1.0f - kFilteringFactor);
acceleration_y = (float)accel.y * kFilteringFactor + acceleration_y * (1.0f - kFilteringFactor);
acceleration_z = (float)accel.z * kFilteringFactor + acceleration_z * (1.0f - kFilteringFactor);
accelerometerCounter++;
if (accelerometerCounter == kUpdateCount)
{
[self initOrientation];
[UIAccelerometer sharedAccelerometer].delegate = nil;
}
}
- (void)initOrientation
{
// Figure out orientation from acceleration_[xyz] and set up your UI...
}
Original response:
Does [UIDevice currentDevice].orientation return the correct orientation during applicationDidFinishLaunching:? If so, you can set up your initial UI according to that orientation.
If that property doesn't get set until some later time, you might try experimenting with performSelector:afterDelay: to initialize the UI after a small delay.
This code sample is from Kendall's answer below, added here for completeness:
[self performSelector:#selector(getOriented) withObject:nil afterDelay:0.0f];
I'm not sure if a zero-second delay is sufficient -- this means the code for getOriented will run during the first pass through the event run loop. You may need to wait longer for the accelerometer readings to register on UIDevice.
Mort, these answers seem somewhat of a red herring; I can't see why you can't use the following built-in method for a UIViewController class:
-(void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {}
This method gets called automatically after a rotation has occurred (rather than with shouldAutorotateToInterfaceOrientation which only tells you it's about to happen). Handily, the variable 'fromInterfaceOrientation' contains the previous orientation. As the documentation also says, you can assume that the interfaceOrientation property of the view has already been set to the new orientation, so you then have one method with access to the old orientation and the new!
If I've missed something and you've already dismissed being able to use this method, my apologies! It just seems odd that you're creating and storing a variable for the "previous orientation" when it's provided for free in the above method.
Hope that helps!
Use this for the orientation of the UI if you need to determine what way are you pointing.
Not 100% sure this is right but going off the top of my head:
[[[UIApplication sharedApplication] statusBar] orientation]
Here's one way to get the orientation when the app first loads and the UIDeviceOrientation is set to UIDeviceOrientationUnknown. You can look at the transform property of the rotated view.
if(toInterface == UIDeviceOrientationUnknown) {
CGAffineTransform trans = navigationController.view.transform;
if(trans.b == 1 && trans.c == -1)
toInterface = UIDeviceOrientationLandscapeLeft;
else if(trans.b == -1 && trans.c == 1)
toInterface = UIDeviceOrientationLandscapeRight;
else if(trans.a == -1 && trans.d == -1)
toInterface = UIDeviceOrientationPortraitUpsideDown;
else
toInterface = UIDeviceOrientationPortrait;
}
A more complete example on how to obtain device orientation from accelerator readings can be found here
As the solution relies on accelerator readings, it wouldn't work on the simulator, so you'll have to work on the device... still looking myself for a solution that works on the simulator.
In response to your comment, I thought I could better put code here than in a comment (though really Daniel deserves credit here):
in applicationDidFinishLaunching:
[self performSelector:#selector(getOriented) withObject:nil afterDelay:0.0f];
Then you just need the method to call:
- (void) getOriented
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
// save orientation somewhere
}

How do I detect a rotation on the iPhone without the device autorotating?

Anyone know how to do this?
I thought this:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
}
- (void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
}
- (void)didAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
}
- (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration {
}
may prevent the device from rotation (overriding all rotate methods of my UIViewController and not calling the superclass) but I fear it's not the UIViewController that actually performs the rotation.
My UIViewController is in a UINavigationController.
Anyone have any ideas?
Cheers,
Nick.
You can register for the notification UIDeviceOrientationDidChangeNotification (from UIDevice.h), and then when you care about orientation changes call this method:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
When you no longer care about orientation changes, call this method:
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
The notification will be sent when applicable, and you can check [UIDevice currentDevice].orientation to find out what the current orientation is.
On a side note shouldAutoRotateToInterfaceOrientation used to be called on the time for rotation in 2.x, but in 3.0 it's much less consistent. The notification mentioned in the other post is the way to go for reliable indication of rotation.
You're returning YES from shouldAutorotateToInterfaceOrientation:, which I suspect you didn't mean to do. That's the only method you need to implement to prevent the device from rotating.
As for getting the current orientation of the device, you can use [[UIDevice currentDevice] orientation].