Details on rotation and custom view objects - ios

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).

Related

Finer grained UIView rotation in a UIViewController

I have a UIViewController that overlays controls on a view presenting what the camera sees. I have a couple of scenarios I would like to allow.
For the iPad, I want to keep the controls on the right most edge of the device, by your right thumb, no matter what the device's rotation. The controls should rotate their content so that their top is always upwards (away from the ground). I don't want the camera view to rotate at all, because that would just be silly – its position & size should stay the same and its contents shouldn't rotate either.
For the iPhones, I want to keep the controls at the bottom of the device's screen, by to the home button, wherever the home button actually is. The controls should rotate their content so that up is always pointing upwards. Again, I don't want the camera view's frame or content to take part in any view rotation animation at all.
I'm using auto-layout.
I'm wondering if there is any way to describe some or all of this in a storyboard. In particular, it'd be great to be able to describe that some view positions need to autorotate (ie, the controls, on iPad), but that other views don't (the camera view).
A question from 2011 indicates this wasn't possible at the time, but perhaps things have moved on since then? If it's not directly supported, can you suggest an approach and are there some sensible places to be hooking in to autorotation to achieve this?
Ok, this isn't quite a complete answer, but I tried a few things which look promising.
First, you can create a separate set of constraints for portrait vs. landscape using the size specifiers: landscape is w Regular, h Any; portrait is w Any, h Regular (I think -- double-check these) This is accessible via the pop-up control in the bottom-center of the storyboard view. By installing different constraints for portrait and landscape, it should be possible to scale the width and height of your controls' container view so it appears to be in a constant position w.r.t. device orientation; in other words, the container doesn't actually counter-rotate -- it scales so it effectively looks like it has counter-rotated.
I got this close to working. It looks like it's doing the correct thing in the storyboard view, but when I actually run it, I get debug messages about conflicting constraints. Not sure how to fix this, but maybe play with the constraint priorities? That sometimes helps.
A second thing I (partially) tried was creating a custom container view class which counter-rotates itself to the correct position based on the device orientation (in the UIDevice class). You implement this by overriding layoutSubviews. For each orientation, you define a transform which puts it in the correct position, and set the view's transform property.
Another possible solution is to override updateConstraints in your view controller and add/remove constraints to position/scale your container to the correct place for each orientation.
For all of these, the idea is that you "force" the container to be in the correct place, but leave the subviews (the actual controls) alone. The controls should do the right thing if their constraints are independent of the specific orientation of the container view.
So, those are some ideas anyway... if they lead you to an actual solution, could you post it? I anticipate having a need for this myself.

UIImageView not rotating when device orientation changes

I have a VC with two imageViews. One takes up the whole screen, the other is actually slightly larger and can be moved around on the screen (Pan Gesture).
When the device changes orientation only the static view changes orientation, or just updates its frame maybe (automatically).
The second Pan UIImageView does not move. Is there a reason for this, or a switch to make this happen somewhere possibly in the storyboard properties of the UIImageView itself? I would like the view to move too.
I understand that an option would be to use the methods that confirm the orientation has completed a change and transform the imageView myself, which is not a problem... but checking I am not missing something that could do this for me automatically first.
Ensure you are using correct AutoResizeMasks on the imageview. Provide your code or storyboard description for a more complete answer.
-Additionally if you are using AutoLayout, you may need to add the appropriate constraints (making it slightly larger than the view may confuse it as to your intention without proper suggestions via constraints).

iOS recommended way of handling portrait and landscape orientation interfaces

I am working on an iPad app and want to support both portait and landscape orientation.
Currently, I am seeing three ways to handle orientation:
1) Apple recommends using two view controllers (http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/RespondingtoDeviceOrientationChanges/RespondingtoDeviceOrientationChanges.html)
2) The Beginning iPhone Dev book I have uses a single xib file with multiple UIView (one for portrait, one for landscape), swapping the view in the willAnimateRotationToInterfaceOrientation() method.
3) Others have said the "proper" way to do it was using layoutSubviews and autosizing mask.
Before, when I build an app, I would write my entire app purely in code, including interface setup and layout. The result was I ended up with a massive and very painful to tweak interface code.
Now after writing a number of apps in purely code, I kind of appreciate why there is Interface Builder - to help manage the interface and more so, a view controller should ideally only be for managing data and sending it to the views, not managing how the views should look.
Using Interface Builder, it makes sense to have two separate custom UIViews, one for portrait and one for landscape but I've been told otherwise that layoutSubview is the better option.
But if I were to use layoutSubview, that would mean I have to go back to writing and managing my interface using code, which doesn't seem right to me.
Is using layoutSubviews() really the way to go ?
I really don't see any benefit it has over other method. Some say layoutSubviews allow you to animate the transition to the other orientation.
My two interface isn't a simple resize button or text width when the device rotates. I have adjacent sliders next to text fields that should be stacked vertically when rotating to landscape and other sliders that needs to be moved to the other side of the screen etc.
edit
might be a duplicate questions of:
Handle iPhone orientation Landscape/Portrait
iPad/iPhone multiple orientations best practice?
Moderators can close if need to.

What is the best way to create a portrait and a landscape view for one controller [Storyboard]?

Storyboards are an awesome feature - but is there a possibility to create a lanscape and a portrait view for one controller? What is the best way to do that?
I found a lot on StackOverflow, from copying a storyboard to add subviews for each orientation. It would be great, if there is a better way to do that...
Autosizing works only on simplest straighforward cases. If you have to tweak just a little bit the end result, it becomes too hard. What I do to make this much more visual is I set autosizing, then duplicate the portrait view right for the same controller in the same xib and rotate it to landscae. Then I adjust the positions. But before that I set tag properties for each control. So I create actually 2 duplicate views, 1 portrait and 1 landscape and since the tags are the same, during "willRotateTo..." or "didRotateTo.." I copy the .frame property of each control from the duplicate View that I visually adjusted in xib, and that puts the control where I wanted.
I can even add animation along the way.
You want to use autosizing of subviews for each UIViewController of your storyboard. It's the cleanest way of managing rotation.
Check: http://www.techotopia.com/index.php/IOS_5_iPhone_Rotation,_View_Resizing_and_Layout_Handling
You aren't supposed to create multiple scenes for every orientation. You're supposed to use autoresizing, springs and struts, custom implementations of several rotation methods and respond to notifications.

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).

Resources