Can a UIView "ignore" interface orientation? - ios

I have a UIView associated with it's own UIViewController that I want to always be locked to portrait mode.
Imagine that it wants to act like a window into the internals of the iPhone. Weird, I know.
That view will have subviews on top of it, or perhaps other viewcontrollers' views which want to respond appropriately to interface rotation. I basically want to lock a single view to portrait while letting my implementation of shouldAutoRotateToInterfaceOrientation handle the rest.
Is there a way to do this?

I believe adding it as a subview of the main window will prevent it from being autorotated (because only UIViewControllers handle rotation). Calling [self.view.window addSubview:internalView]; should work perfectly. (though there are no guarantees as to what orientation the view is in when it is added).

Related

Most efficient way to have UIViewController view fill out all of usable space?

I have a universal single view app which has a UINavigationBar toolbar at the bottom, then the UIViewController view takes up the rest of the space.
I usually do everything programmatically, so for the longest time I've been calculating the UIViewController's view frame manually in loadView, taking into account all of the components that a device could have i.e. home indicator height on new iPhone device, opaque status bar on old devices.
However, with all the new devices I'm finding this way to be inefficient and have thought about switching to a storyboard or xib. I've never really worked with these so I don't understand how it works. For instance, if I create a storyboard with the ViewController's view taking the entire size of the usable area, will this translate for all devices? Will it resize correctly when there's rotation?
In general I'm kind of wondering, what is the most elegant / efficient way to create an app where the UIViewController's view takes up the entire usable area of the screen on all devices, including when the phone rotates?
I usually do everything programmatically, so for the longest time I've been calculating the UIViewController's view frame manually in loadView
There was never any need to do that; you can just delete that code. The navigation controller itself will give its child view controller's view the correct frame!
All you have to do is provide the child view controller's views subviews with correct positioning. And that is something you should be doing just by giving them autolayout constraints, so that they will be correctly arranged no matter what size the view controller's view ends up being. You can do that in code or through a storyboard; it makes no difference.
In general I'm kind of wondering, what is the most elegant / efficient way to create an app where the UIViewController's view takes up the entire usable area of the screen on all devices, including when the phone rotates?
The elegant efficient way is: do nothing. That is what already happens, automatically, with no input from you.

Details on rotation and custom view objects

I have setup a custom view with two subviews and a button. I am trying to understand CGRect better and work out what should be used for heights/widths and how to center items correctly. I imagine that using exact numbers like this:
UIButton *facebookLoginButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 300,75)];
... is not the correct way to complete this. When the device is rotated nothing fits to screen anymore.
I would also like to know how to not allow rotation on one view controller. For example a setting which I can stop rotate doing anything with the views, then later in the app rotation will be used again.
I have had a look through the design apple documentation but not sure exactly what I am looking for. I am looking for best practice for setting bounds/center items/sizes(width/heights)/working with multiple sub views and how they layout when programatically creating the views.
You can check for the device orientation and then adjust the sizes regarding to that.
[UIDevice currentDevice].orientation
If you're using iOS6 you can take a look at constraints. Or just use the interface builder, where it's pretty simple to manage that stuff (if you don't need to make it programmatically).
For portrait / landscape orientation, check this out:
Alternate landscape triggered at unwanted view
With that I managed to perform a landscape rotation only on one view controller while all the rest of the app stays in portrait (but you can adopt for your needs of course).

iOS subviews of window not rotating

I have a number of subviews on the main window. These subviews do not rotate. Is there an easy way to fix this problem?
Use UIViewController, it handles rotation automatically. There's rarely the need for adding subviews directly to the window.

Why is ViewController's initial orientation is not landscape when it should be?

I'm making an app that only supports landscape mode of an iOS device. So I have set it to only return YES for UIInterfaceOrientationLandscapeRight and UIInterfaceOrientationLandscapeLeft in shouldAutorotateToInterfaceOrientation: and set the initial orientation in Info.plist to UIInterfaceOrientationLandscapeRight. The app is displaying fine.
The problem is that the coordinate system is not in landscape until the view is finished loading (I'm not particularly sure here when it is being applied correctly). What I know is that it changes to the correct coordinate space when it calls the: shouldAutorotateToInterfaceOrientation upon launch. The problem is that when both viewDidLoad and initWithCoder: are called it is in the wrong coordinate space, before shouldAutorotate....
What I mean is that if I set a view to the full width of the screen with CGRectMake(0, 0, 480, 320) (instead of using self.view.frame or something) it is borked when the coordinate spaces are rotated "corrected". The views created in viewDidLoad have a rotation applied to them so they are wrong. This makes setting up views in those methods a pain and really illogical. I'm guessing that this is an issue with how orientations are handled. Why does it behave like this? I have told it in every way possible that I only want landscape position. Can this be something with Interface builder and properties there?
This is using a standard UIViewController. The new views are however also loaded from .xib files in viewDidLoad. Could this be the issue?
Support for orientation changes is something that all iOS developers struggle with at one time or another. It's often confusing and frustrating. Here's a rule of thumb which will handle 95% of all orientation issues:
Orientation related interface changes (such as frame resizing) don't happen until viewWillAppear:. Until then, your view will be in portrait mode even if you only support landscape in your app or your app is already in landscape mode.
Since viewDidLoad: occurs before viewWillAppear:, orientation layout changes haven't occured yet. So, doing any of the following in viewDidLoad will often have wonky results (depending on how the view's autoResize is set):
inserting a layer (such as a gradient) inside a view with a frame that is equal to the view's bounds. The layer inserted will always be the size of the view in portrait mode. If that view stretches when rotating to landscape view, your layer will be smaller than the view.
trying to determine the size of a table view and using that frame for some other view
manually adding a child view controller and using a container view's frame to determine the child view controller's view frame.
I was just reading over the UIViewController documentation today, and I remember reading about this.
From the “Handling View Rotations” overview section:
By default, the UIViewController class displays views in portrait mode only. To support additional orientations, you must override the shouldAutorotateToInterfaceOrientation: method and return YES for any orientations your subclass supports. If the autoresizing properties of your views are configured correctly, that may be all you have to do. However, the UIViewController class provides additional hooks for you to implement additional behaviors as needed.
…
Note: At launch time, applications should always set up their interface in a portrait orientation. After the application:didFinishLaunchingWithOptions: method returns, the application uses the view controller rotation mechanism described above to rotate the views to the appropriate orientation prior to showing the window.
http://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006926-CH3-SW57
One thing I can think of that might be causing you extra trouble is if you’re setting autoresizingMask options on your subviews. Could make predicting what will happen when the view is rotated and resized very difficult if all your numbers are wrong.
If you're using custom UIVIews, you might want to override layoutSubviews: to handle the different times when it may be necessary to layout the subviews again (or when you call setNeedsLayout).
You can reorder the Supported interface orientations in [project]-info.plist file, set item 0 value to Landscape (right home button).

Main view that should not rotate but subviews that should, including UIPopoverController

I would appreciate some help before spending any more time on trial and error.
Imagine the following: I'm just starting to create something for the iPad that will look something like a dashboard with a number on dials on it. When rotating the iPad (portrait, landscapeLeft etc) the background should not rotate, the dials position should remain but the inside of the dials should rotate to correct position. So, main view should not rotate, but the subviews (inside of the dials) should. I have done this on the iPhone before by telling the viewController to only be in portrait and then checking UIDeviceOrientation, so I thought this was gonna be easy. But my headache starts when displaying a UIPopoverController. Since I'm not changing the UIInterfaceOrientation, the UIPopoverController will always be in portrait.
Ideal solution would be to have the main view (self.view from the viewController) not observe changes in rotation, but allowing the subviews to do it, but from what I understand that is not possible. Only other solution I could think of is to not animate the change in rotation, and jut move the subviews (dials) into their new position. Animating them (subviews) make the dance all over the place. But I have not found any good solution on how to do that.
Any thoughts anyone?
You are correct in thinking that if the main view does not rotate, having the subviews automatically rotate is not possible.
A workaround that springs to mind is this: What is animated when you rotate the view is a change in the view's transform. I am pretty sure that you could register to receive device orientation changes and when you get the change, animate a transform change to a container view that contains all the subviews you want to rotate.
Edit: just read about you having a popover controller.
As far as the popover is concerned, the way the API manages autorotation is to hide the popover and then show it again at the end of the rotation. It shouldn't be too hard to implement similar behavior.
Another thing that occurs to me is this: Is what you want to not rotate just a background? Would it work to just have two backgrounds, one for portrait and one for landscape that you could switch between? It might not be the most pretty looking, but it would probably be easier than recreating autorotation behavior yourself.

Resources