How can I make a custom orientation lock action? - iphone

I would like to make a custom orientation lock button for a reader app of mine, and I was thinking it wouldn't be too bad to whip up, but alas I am the one getting whipped.
To start off I do have this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
And then I was thinking that I could handle the actual locking in an action method like this:
- (IBAction) screenLock:(id)sender{
if([UIDevice currentDevice].orientation == UIDeviceOrientationPortrait){
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
}else{
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
}
}
But alas, this code will not hold sway over the former that instructs the view to rotate...
Am I going about this all wrong? What is a better way to do it? I just want to have local, easy way to have my users lock the orientation of their screen. I guess it would be using a boolean value where they hit a button to lock and then hit again to unlock...
Thoughts?
Thanks!!

shouldAutorotateToInterfaceOrientation ripples up your view hierarchy so your logic needs to be put into your app delegate (or as the most senior ViewController that might return YES). Put a BOOL property in your appDelegate and set it via your lock button (e.g. target pointers/delegates (AppDelegate)) then in your appDelegate do something like this:
#define ROTATION_MASTER_ENABLED 1
//Setting MASTER_ROTATION_LOCK_ENABLED to 0 will stop the device rotating
//Landscape UP>landscape DOWN and Portrait UP>Portrait DOWN,
//This is not generally desired or app store safe, default = 1
-(BOOL)compareOrientation:(UIInterfaceOrientation)interfaceOrientation
{
UIInterfaceOrientation actual = [[UIDevice currentDevice] orientation];
if(UIInterfaceOrientationIsLandscape(interfaceOrientation) && UIInterfaceOrientationIsLandscape(actual))return YES;
else if(UIInterfaceOrientationIsPortrait(interfaceOrientation)&& UIInterfaceOrientationIsPortrait(actual))return YES;
else return NO;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if(!MASTER_ROTATION_LOCK_ENABLED)return NO;
else if(self.rotationEnabled || [self compareOrientation:interfaceOrientation])return YES;
return NO;//self.rotationEnabled is a BOOL
}

Related

How to change the orientation of the app without changing the device orientation in iphone app

I want to change the orientation of the app without changing the device orientation in iphone app.
I want to change my view from portraid mode to landscap mode programmatically.
And also want to know that will this be accepted by the apple store or not ?
Thanks
Now I got the solution from other that is as follow
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
when you add this line at that time one warning appear and for remove this warning just add bellow code on you implementation file..
#interface UIDevice (MyPrivateNameThatAppleWouldNeverUseGoesHere)
- (void) setOrientation:(UIInterfaceOrientation)orientation;
#end
and after that in bellow method just write this code if required..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
But now want to know is this accepted by apple app store or not ?
thanks
use this line for programmatically change orientation...
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
and also when you add this line at that time one warning appear and for remove this warning just add bellow code on you implementation file..
#interface UIDevice (MyPrivateNameThatAppleWouldNeverUseGoesHere)
- (void) setOrientation:(UIInterfaceOrientation)orientation;
#end
and after that in bellow method just write this code if required..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
// return NO;
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
i hope this help you..
:)
Add a class variable
Bool isInLandsCapeOrientation;
in viewDidLoad
set this flag to
isInLandsCapeOrientation = false;
Add the following function
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (!isInLandsCapeOrientation) {
return (UIInterfaceOrientationIsPortrait(interfaceOrientation));
}else {
return (UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
}
To changing orientation from portrait to landscape, let it happens on a button action
- (IBAction)changeOrientationButtonPressed:(UIButton *)sender
{
isInLandsCapeOrientation = true;
UIViewController *viewController = [[UIViewController alloc] init];
[self presentModalViewController:viewController animated:NO];
[self dismissModalViewControllerAnimated:NO];
}
This works fine for me.
To change Orientation portraid mode to landscap mode use this code
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
use this code for programmatically change orientation...
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
If you want to change the particular view only in landscape..then u can try the following in its viewDidLoad
float angle = M_PI / 2;
CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
[ [self.view] setTransform:transform];
The documentation describes the orientation property as being read-only, so if it works, I'm not sure you can rely on it working in the future (unless Apple does the smart thing and changes this; forcing orientation changes, regardless of how the user is currently holding their device, is such an obvious functional need).
As an alternative, the following code inserted in viewDidLoad will successfully (and somewhat curiously) force orientation (assuming you've already modified you shouldAutorotateToInterfaceOrientation ):
if (UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation]))
{
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];
}
Clearly, this does it if the user is currently holding their device in portrait orientation (and thus presumably your shouldAutorotateToInterfaceOrientation is set up for landscape only and this routine will shift it to landscape if the user's holding their device in portrait mode). You'd simply swap the UIDeviceOrientationIsPortrait with UIDeviceOrientationIsLandscape if your shouldAutorotateToInterfaceOirentation is set up for portrait only.
For some reason, removing the view from the main window and then re-adding it forces it to query shouldAutorotateToInterfaceOrientation and set the orientation correctly. Given that this isn't an Apple approved approach, maybe one should refrain from using it, but it works for me. Your mileage may vary. But this also refers to other techniques, too. Check
SO discussion

[UIDevice currentDevice].orientation == UIDeviceOrientationUnknown following UINavigationController push

This is an observation more than anything, because I appear to have found a work-around...
The code below fails to work when it's in a controller which has been pushed onto a UINavigationController stack. In this situation [UIDevice currentDevice].orientation consistently returns UIDeviceOrientationUnknown.
-(void)layoutAccordingToOrientation {
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
NSLog(#"layoutAccordingToOrientation/orientation==%i", orientation);
if (UIInterfaceOrientationIsLandscape(orientation)) {
:
_detailToolbar.frame = CGRectMake(0, 660, 1024, 44);
} else {
:
_detailToolbar.frame = CGRectMake(0, 916, 768, 44);
}
}
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self layoutAccordingToOrientation];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
The following call has been made:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
To work around this UIDeviceOrientationUnknown-problem, I now use the following instead:
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsLandscape(orientation)) {
etc.
} else {
etc.
}
... which works every time.
Still, I fail to see why the first variation would not work in the context of a pushed view controller. Ideas anyone? Is it simply a bug?
I could think of a couple of things here, but I'm not sure they're what you're looking for...
First, when the app is launched, usually the device orientation returns UIDeviceOrientationUnknown for performance reasons. One thing you could try is to layout your subviews inside a .nib or in your Storyboard, with the correct autoresizing options, and let iOS do its thing.
Secondly, if you're like me, maybe you're testing on a device that's right beside your computer, laying on a table. Well, in that case, you are not going to get the UIDeviceOrientationUnknown thing. You should actually test for UIDeviceOrientationIsValidInterfaceOrientation([[UIDevice currentDevice] orientation]). If the device is laying down on its back, for example, it returns yes to that statement. This also means that when you use UIInterfaceOrientationIsLandscape(orientation), you're getting the right result for the wrong reason. The UIInterfaceOrientationIsLandscape is returning false not because the device's orientation is UIDeviceOrientationPortrait, but because it's invalid.
Not so sure if this helped, but it took me a while to figure that out and I thought it would be nice to share this info! :)

Hide UIView on device rotation - doesn't work when device is horizontal

I'm trying to hide an image in a view controller when the device is rotated. I'm posting a notification in PlayerViewController and am listening for it in the app delegate, which is responsible for the bannerView:
- (void)orientationChanged:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if ((orientation == UIDeviceOrientationLandscapeLeft) ||
(orientation == UIDeviceOrientationLandscapeRight)) {
bannerView.hidden = ([[self.navigationController visibleViewController] isKindOfClass:[PlayerViewController class]]) ? YES : NO;
} else {
bannerView.hidden = NO;
}
}
The PlayerViewController sends a notification and the app delegate hides the bannerView. However, when the device is laid flat on a table, the image shows. Works fine when the device is held vertically but horizontally the image appears... odd.
Here is the code to send the notification:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
... hide other stuff in this view controller
}
Any ideas why this odd behavior is occurring?
Just one tidbit more information. In the simulator the image shows when the device is in upside-down orientation, even though I have:
- (BOOL)shouldAutorotateToInterfaceOrientation (UIInterfaceOrientation)interfaceOrientation
{
if (interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIDeviceOrientationPortrait) {
return YES;
} else {
return NO;
}
}
Your error might be happening because of when you're posting the notification.
willAnimateRotationToInterfaceOrientation is called before the orientation change takes place (hence the "will" in the method name). So if we're going from portrait to landscape, the current orientation may still be reported as portrait (it may not, it depends).
Now, the willAnimate... call returns the toInterfaceOrientation - the orientation that is going to happen.
You trigger your notification when you receive the willAnimate... call, and inside that notification call [[UIDevice currentDevice]orientation]: which will return portrait. Instead of requesting the orientation in your notification method you should instead pass the orientation provided in the willAnimate call.
If that wasn't clear, the one sentence summary: willAnimateRotationToInterfaceOrientation is called before the rotation changes.

Weird landscape UITabBarController Application startup

My application is quite simple, but I have some problems when it starts. I setted in the Info.plist to be landscaped, but it seems to ignore the order. In fact, when the app is loading the Simulator is landscaped, but then it returns in portrait mode.
This is the hierarchy of the views and controllers:
MainViewController (extends UITabBarController just to override shouldAutorotateToInterfaceOrientation:)
Three extended UITableViewControllers as tabs (also those have the shouldAutorotateToInterfaceOrientation correctly setted up).
If I kinda force the orientation of the device to Landscape with:
[[UIDevice currentDevice] setOrientation: UIInterfaceOrientationLandscapeRight];
Then for an instant the Simulator flashes in portrait mode, and then it goes landscaped. The problem is that in this way, the auto-rotation animations gets started, which is something I cannot tollerate. I just want a fixed, landscaped application.
Any clues? Am I missing something?
Try the following. Not sure why it does not work for you
1) set the key UIInterfaceOrientation
to UIInterfaceOrientationLandscapeRight in your .plist file
2) override your UITabBarController shouldAutorotateToInterfaceOrientation() method; in the following the code only deals with tab zero and one, and only with one controller: if you have a navigation controller and you want to control different controllers that may be on the stack, you have to modify the code accordingly
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
BOOL tabZeroController = [[[self.viewControllers objectAtIndex:0] visibleViewController] isKindOfClass:[YourTabZeroTableViewController class]];
BOOL tabOneController = [[[self.viewControllers objectAtIndex:1] visibleViewController] isKindOfClass:[YourTabOneTableViewController class]];
if(self.selectedIndex == 0 && tabZeroController)
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
if(self.selectedIndex == 1 && tabOneController)
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
return NO;
}
2) setting
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
in your delegate's applicationDidFinishLaunching() method is only required for the simulator, not on a device
3) add the following shouldAutorotateToInterfaceOrientation(method) in your controllers
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
4) run the app on your device and verify that it works properly by using the Hardware menu item Rotate Left and Rotate Right. You should see the display in landscape-mode.
maybe this can help
http://www.dejoware.com/blogpages/files/iphone_programming_landscape_view_tutorial.html

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
}