modalViewController presented from UISplitViewController comes up as the wrong orientation - objective-c

I have a UISplitViewController that is set at the rootView of my application. When viewDidLoad is called in my left view controller I do a check, then present a modal view controller using the following:
SiteConfiguration *config = [[SiteConfiguration alloc] initWithStyle:UITableViewStyleGrouped];
config.firstLoad = YES;
UINavigationController *configNav = [[UINavigationController alloc] initWithRootViewController:config];
if ([Utility isIpad]) {
configNav.modalPresentationStyle = UIModalPresentationFormSheet;
configNav.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[[AppDelegate instance].splitViewController presentModalViewController:configNav animated:YES];
} else {
[self presentModalViewController:configNav animated:YES];
}
If the iPad is in landscape mode while the app loads, the modalView is shown with an incorrect orientation:
I can rotate the iPad to fix this, but WHY does it load up wrong? I have shouldAutorotateToInterfaceOrientation: returning YES in my SiteConfiguration viewController. What could be causing this?

Be careful of where you choose to present your modal controller.
I've had experience with some custom modal controllers and setting the orientation of the modal controller (and its shadows!) in
- (void)viewDidLoad:(BOOL)animated
didn't always behave as expected.
Put your code (presentModalViewController:configNav animated:YES) in
- (void)viewDidAppear:(BOOL)animated
instead. (Do this as well with any code that sets a subviews frame or does any manipulation of layers, e.g. the shadow layer and shadow properties).
As far as I can tell, the rotation may not be apparent to subviews of the rotated view until after - (void)viewDidLoad:(BOOL)animated due to threading issues (one thread may start drawing your subview or modal controller's view before rotation is passed down to the subviews (and modal controllers) by the main thread). Someone with more experience with threads than myself might be able to shed more light on this.

shouldAutorotateToInterfaceOrientation: doesn't actually rotate the interface, the app does that upon receiving a UIDeviceOrientationDidChangeNotification notification.
Try adding a check for the device orientation in the -(void) viewDidAppear:(BOOL)animated method.
To force an interface rotation, use the following piece of code.
UIDeviceOrientation toInterfaceOrientation = [[UIDevice currentDevice] orientation];
[UIApplication sharedApplication].statusBarOrientation = toInterfaceOrientation;

Related

Check orientation in AwakeFromNib – Objective-c

I want to check the orientation of the iphone in the AwakeFromNib method. This is my code:
- (void)awakeFromNib
{
orientation = [[UIApplication sharedApplication]statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait)
{
NSLog(#"orientation portrait");
}
else if (orientation == (UIInterfaceOrientationLandscapeLeft | UIInterfaceOrientationLandscapeRight))
{
NSLog(#"Orientation landscape");
}
}
The logs aren't messaged when I'm turning my iPhone, what's the problem?
Your comments on Michael Dautermann's (now-deleted) answer are contradictory. You said you're creating your view programmatically, but you also talked about “when the view is loaded”.
Either you're creating the view in code, or you're loading it from a nib/storyboard. You can't be doing both with the same view instance.
The fact that your awakeFromNib method isn't being called matches your claim that you are creating the view programmatically. But another possibility is that you are loading the view from a nib but have not correctly set the “Custom Class” of the view in the nib, so it's not actually creating an instance of your view subclass.
If you are truly creating the view programmatically, then somewhere you should have code like this:
view = [[MyView alloc] initWithFrame:someRect];
or maybe this:
view = [[MyView alloc] init];
The second case, under the covers, just calls [self initWithFrame:CGRectZero]. So either way, your view's initWithFrame: method is getting called. So you should override that method and check the interface orientation there.
If you're not creating the view in one of those ways, then edit your question to explain exactly how you are creating it.

presentViewController not working after orientation change to portraitupsidedown

Code is:
OptionsTableViewController *optionsTableVc = [[OptionsTableViewController alloc] initWithItems:itemNames andSelectedItem:-1 andOrigin:-1];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:optionsTableVc];
optionsTableVc.delegate = self;
[self presentViewController:nav animated:YES completion:nil];
OptionsTableViewController is a custom viewcontroller which inherits from UITableViewController, a simple object
This code works well when on portrait, when rotating right once, or when rotating left once - but when I try to execute this code after the orientation changes to UpsideDown - it stops working - when I try to execute it, the current view gets "Flipped" (i.e from upside down to upside, or from landscape left to landscape right) and immediately disappears.
Your view controller that you are changing to probably is not returning yes to the following function for upsideDown
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
}
if your destination view controller does not already have this function in it you'll want to add it and return yes for all the orientations you want to support (sounds like probably all). If you do want to support all orientations you can just return yes from this function and that should probably fix your problem.
Adding this line fixed the odd behavior:
nav.modalPresentationStyle = UIModalPresentationPageSheet;

How to handle autorotation in AVCaptureVideoPreviewLayer?

My application supports all orientations except PortraitUpsideDown.
In my view hierarchy I have an AVCaptureVideoPreviewLayer as a sublayer in the top view which is UIImageView. Then below it in view hierarchy are several overlay views showing controls.
Overlay views are working properly with orientation changes, but I don't how to be with this AVCaptureVideoPreviewLayer. I want it to behave like in Camera app, so that previewLayer stays still and controls are smoothly reorganized. Right now since the main view is rotated on orientation change, my preview layer is also rotated, which means that in landscape view it stays in portrait view, taking only part of the screen and the picture from camera being also rotated by 90 degrees. I've managed to rotate the preview layer manually, but then it has this orientation change animation, which leads to the background being seen for a while during the animation.
So what is the proper way to autorotate the controls while making previewLayer stay still?
In my implementation I have subclassed the UIView for my view which I want to rotate and something like viewController for this view which is just a subclass of NSObject.
In this viewController I do all the the checks related to changes of orientation, make decision if I should change orientation of my target view, and if yes, then I call method of my view for changing its orientation.
First of all we need to fix the orientation of whole application interface to Portrait mode, so that our ACCaptureVideoPreviewLayer always stays still.
This is done in the MainViewController.h:
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation`
{
return interfaceOrientation==UIInterfaceOrientationPortrait;
}
It returns NO to all orientations except Portrait.
In order to our custom viewController be able to track the changes of device orientation we need to make it an observer of corresponding notifications:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(orientationChanged) name:UIDeviceOrientationDidChangeNotification object:nil];
I put these lines in the (void)awakeFromNib method of my viewController.
So each time the device orientation is changed, the viewController's method orientationChanged will be called.
Its purpose is to check what is the new orientation of device, what was the last orientation of device and decide if to change it. Here is the implementation:
if(UIInterfaceOrientationPortraitUpsideDown==[UIDevice currentDevice].orientation ||
lastOrientation==(UIInterfaceOrientation)[UIDevice currentDevice].orientation)
return;
[[UIApplication sharedApplication]setStatusBarOrientation:[UIDevice currentDevice].orientation animated:NO];
lastOrientation=[UIApplication sharedApplication].statusBarOrientation;
[resultView orientationChanged];
If the orientation is the same as before or in PortraitUpsideDown then do nothing.
Else it sets the status bar orientation to the proper one, so that when there is an incoming call or ossification, it will appear on the proper side of the screen. And then I call also method in the target view where all the corresponding changes for new orientation are done, like rotating, resizing, moving the other elements of interface in this view corresponding to the new orientation.
Here is the implementation of the orientationChanged in target view:
Float32 angle=0.f;
UIInterfaceOrientation orientation=[UIApplication sharedApplication].statusBarOrientation;
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
angle=-90.f*M_PI/180.f;
break;
case UIInterfaceOrientationLandscapeRight:
angle=90.f*M_PI/180.f;
break;
default: angle=0.f;
break;
}
if(angle==0 && CGAffineTransformIsIdentity(self.transform)) return;
CGAffineTransform transform=CGAffineTransformMakeRotation(angle);
[UIView beginAnimations:#"rotateView" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationDuration:0.35f];
self.transform=transform;
[UIView commitAnimations];
Of course here you can add any other changes like translation, scaling of different views of your interface that need to respond to new orientation and animate them.
Also you may not need the viewController for this, but do all just in the class of your view.
Hope that the general idea is clear.
Also don't forget to stop getting notification for orientation changes when you don't need them like:
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
[[UIDevice currentDevice]endGeneratingDeviceOrientationNotifications];
iOS 8 solution:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) {
self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
} else {
self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
}
}
in your setup code:
self.layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
self.layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) {
self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
} else {
self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
}
THe main problem is that when I get the notification, the statusbar has not yet rotated, so checking the current value give in fact the value before the rotation. So I added a little delay (here 2 seconds) before calling the method that check the statusbarorientation and rotates my subview :
-(void) handleNotification:(NSNotification *) notification
{
(void) [NSTimer scheduledTimerWithTimeInterval:(2.0)
target:self
selector:#selector(orientationChanged)
userInfo:nil
repeats:NO ] ;
}
The rest of the code is the one from BartoNaz.

How to load a particular view when device's orientation is landscape any where in the app and switch to the lat view when portrait?

Hey in my app i have to load a particular view whenever the device is in landscape mode no matter in which view the user is. And also have to switch it back to the last view where the user was when device turned to portrait.
Any suggestions? or link to any tutorial??
Maybe you try something like write your own UIViewController subclass, wich only implements the appeareance of your custom view on change of interfacorientation. Something like this:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
DetailViewController *dvc = [[DetailViewController alloc] initWithNibName:#"DetailViewController_iPhone" bundle:[NSBundle mainBundle]];
}
//navigation controller ruft den bdv Controller auf
[self.navigationController pushViewController:dvc animated:YES];
[dvc release];
}
All your other classes now inherit from this custom UIViewController.
Of course this would only be a solution if you have ViewControllers for all your views.
Hope it helps

UIView is shown displaced to the top

I have a simple app that has two view controllers. Both of them have a UINavigationBar at the top, as a header. The second UIViewController is displayed as a modal view, when the user clicks on a button on the first one.
When my app first launches, the initial view doesn't completely cover the main UIView and seems "pushed" to the top (see image below).
After I click on the "instructions" button, which displays another view with presentModalViewController:animated:, and dismiss the modal ViewController, everything is displayed correctly.
Anybody knows what I might be doing wrong?
I have nothing in viewWillAppear, and this is my viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
if (!self.model) {
self.model = [[FRRSushiRiceModel alloc] init];
[[self.header.items objectAtIndex:0] setTitle: #"Perfect Sushi Rice: Ingredients"];
}
}
and my application:didFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Create and add the main controller (ingredients)
self.ingredientsController = [[FRRIngredientsViewController alloc] init];
[window addSubview:self.ingredientsController.view];
[window makeKeyAndVisible];
return YES;
}
This small project reproduces this behavior:
Test Case
Did you untick the "Wants Full Screen" setting in IB, either for the UINavigationController or UIViewController?
I found the error, guys.
Basically I was trusting the system to correctly set the frame of my views to match the usable portion of the screen. This works when you add it to some controller of controllers (such as UINavigationController), or add it via IB.
If you add your controllers programmatically, you need to set the view's frame explicitly. A good default is:
[[UIScreen mainScreen] applicationFrame]
represents the part of the screen available to applications: the whole screen minus the status bar.