I need a view controller(on ipad) to share two modes, one in portrait and one in landscape. Actually, I pretty much want to mimic the functionality of UISplitViewController, but I want to be able to use not as the top level view controller. HIG guidelines aside, I have a general problem that I think anyone who is switching views between orientations will run into.
1) To provide for smooth transitions between views, I would like to call my view changes( and animations) inside willRotateToInterfaceOrientation:duration instead of didRotateToInterfaceOrientation method. The problem is, at this stage, the view frame and bounds have not yet changed to their new ones, so you end up having to set the frame manually, like:
subview.frame = CGRectMake(0,0,320,768);
instead of something nicer, maybe:
subview.frame = CGRectMake(0,0,320, self.view.frame.height);
2) Furthermore, even if you try such shenanigans, if the view autoresizes its subviews, you still get nowhere with it. If you disable the autoresizing of subviews, then stuff like this does not even work anymore:
subview.frame = self.view.frame; //because then you'd have to always specify the exact rect.
Does anyone have any insight to offer on this?
Thanks!
Try using willAnimateRotationToInterfaceOrientation:duration: instead.
Also, if you have your autoresizing masks set up correctly on the subviews, you shouldn't need to care whether the main view has been adjusted yet or not. Just size the subviews to fit appropriately inside the main view as it is sized now and it will just work.
If you don't (or can't) have your autoresizing masks set up correctly, you should then already have code to handle size changes in the main view's layoutSubviews method. So again, you shouldn't have to care much whether the main view has been resized yet or not.
Related
Auto Layout Constraints allow me to size and lay out views perfectly, without knowing beforehand what screen they will be rendered on. This works reasonably well when I'm drawing a UI on a Storyboard.
Let's move on to creating views programmatically.
My app is running, the interface has been rendered on the screen, I have all the necessary coordinates and the sizing has been done.
I would like to create a few views dynamically, for instance:
a UIImageView, that appears at the press of a button, gets animated, then disappears
a collection of custom UIView, created and laid out on the screen depending on the underlying data model (imagine a sequence of events here, arranged on a custom timeline)
In similar scenarios, I still tend to use the good old frames, e.g.:
let myView = UIView(frame: CGRectMake(x, y, w, h))
without adding any NSLayoutConstraint.
Is there any definite advantage of using constraints instead, given the added complexity, especially when you need to animate views?
The most important aspects are when you actually calculate your frame (which method), if you want universal support and if you need to support various orientations.
Inside a viewController, if you print out your self.view frame in viewDidLoad and viewWillAppear: or viewWillLayoutSubviews, it might have different values. The final frame (for example, the one for iPhone 6 plus) is not calculated in viewDidLoad. So if you make your view setup there, the calculation will be wrong.
But with constraints, it does not matter where you add them. You might need to call layoutIfNeeded after the view changes, but you don't need to worry weather the frames have their final value.
For views that are visible only for a short time, auto layout could make a difference for different orientations. By using frames, you will have to update the frame of your temporary view in the orientation change callback.
Also, if you use auto layout in the storyboard, using static frames for views created programmatically might not give you the results you are expecting. But, of course, it depends on the particularities of your project.
We're currently having a problem that only seems to affect iOS7 devices.
Within our .xib file we have two views within a container view (i.e.: not at the top level of the view hierarchy) that need to be circular on display. The views have constraints applied to their position and horizontal spacing within the container, and an aspect ratio condition requiring they are square. The views should expand in width/height on larger screen sizes, respecting the constraints described.
In our VC, we have the following in viewDidLayoutSubviews to force these views to appear circular:
- (void)viewDidLayoutSubviews {
self.progressContentContainerView.layer.cornerRadius = self.progressContentContainerView.frame.size.width/2;
}
This seems to work fine on iOS8, however on iOS7 there is a period after the view has been displayed where the constraints have not yet been applied and the size of the view/views is incorrect (see attached screenshots). This resolves itself and correctly renders a circle after half a second. This only appears to happen when the views that we intend to be circular are NOT at the top level of the VC's view hierarchy which seems to imply that viewDidLayoutSubviews is called before the subviews of subviews have also been laid out.
My guess is that we could potentially fix this issue by subclassing UIView for the nested container, adding references to the circular view within this subclass and overriding viewDidLayoutSubviews here to make the cornerRadius adjustment. This seems like a bit of a workaround though and I'm interested to see if there are other options.
Is there a cleaner/more idiomatic solution to this problem?
I know this is an old question but have you tried calling either:
[self.progressContentContainerView setNeedsUpdateConstraints];
or:
[self.progressContentContainerView layoutIfNeeded];
Very simple question, but I'm asking as there seems to be a lot of conflicting views and I've been completely unable to find a definitive answer, let alone a modern one.
I use Auto Layout for 99% of my handling of the user changing from portrait to landscape or vice-versa in a UIViewController. Works great. But sometimes, I have to rely on frames still for one reason or another.
How would I best handle this?
You have willAnimateRotationToInterfaceOrientation, willRotateToInterfaceOrientation, the NSNotification methods with checking status bar, and I'm sure there's some others.
If I want to change the position of a view when the user rotates, which method should I be changing the frame in? And is it best to do it with a simple setFrame: or should I be using autoresizing masks?
Since iOS6, you should not be using willRotateToInterfaceOrientation: and didRotateFromInterfaceOrientation:. These are only called on the front-most presented view controller, and will not be called on others. This means if you have a pushed view controller or a presented view controller, all others will not layout correctly.
Instead, you should always use viewWillLayout and viewDidLayout to handle rotation. They are both called inside an animation block, so anything you do which is animatable, will be animated.
For positioning views, you can either set the frames yourself, or use auto-layout constraints and adjust the constraints in viewDidLayout. If you go the auto-layout route, never remove and add constraints. Use the same constraints as much as possible and just adjust their constant values.
When I'm changing the main view frame, I typically adjust the frame in willRotateToInterfaceOrientation if I need to. Then I adjust any subviews by overriding layoutSubviews for my main view.
I don't know that this is a definitive answer, though - I don't think there really is a definitive answer - it depends on how your application is structured.
wilLRotateToInterfaceOrientation and didRotateFromInterfaceOrientation are best used for stuff you need to do before and after rotation, respectively (for example, disabling user interaction before the rotation begins, and reenabling it after). Everything else should be done in layoutSubviews if possible.
Autoresizing masks are useful sometimes, but I usually lay everything out manually to avoid any surprises when things change between iOS releases (as they often do).
Have a very large program where there is always a superview that just encompasses a custom segment controller. This view sits at the top of screen and controls navigation in several ways.
So the problem arose in only a selected few view controllers where everything was 100% programmaticly created. Essentially CGRect are not being defined in the property dynamic coordinates. But are not being recalculated on orientation change. Does anyone have a simple way to control this in the subview? I'm about to code something in the superview to pass to orientation to other subviews.. but there has to be a better way. Ideas?
Couple of pointers:
You can use auto-resizing masks to determine what happens to your views when their bounds change (ie, when the orientation changes). So UIViewAutoresizingFlexibleWidth means your view will 'stretch' proportionally with the superview when the bounds are changed. UIViewAutoresizingFlexibleLeftMargin means your view will effectively be right-aligned, as the left margin will adjust according to the width, etc etc.
Sometimes auto-resizing masks aren't enough - perhaps you have to change the view's content on an orientation, or do a complex animation. In this case, you use the willAnimateRotationToInterfaceOrientation method in your view controller. Your subviews might have a custom adjustForOrientation method that you've written that you can trigger when willAnimateRotation... is called.
Finally, on iOS 5 you can actually nest view controllers inside of view controllers, in which case orientation events get passed through automatically...but this is probably needlessly complex for what you're trying to do.
After reading about UIView's autoresizingMask on SO and developer.apple.com I'm still unclear what the purpose is. What's a situation where setting this property is necessary?
Yes, it is often necessary to set it if you don't want to resize the views manually. Note that it is mostly useful for subviews (i.e. those views that don't take the whole screen) rather then the main view of your app.
Views typically may need resizing if:
the device is rotated
an extra view (say, an ad) is added to the view, so the existing subviews have less available space.
For example, suppose if you have a view with two buttons on it, one in the top-left corner, another in the top-right corner. In order for the buttons to get wider when the view transitions from portrait to landscape, you need to set the FlexibleLeftMargin to the right button, FlexibleRightMargin to the left button.
Edit: autoresizingMask is also the first thing to look at if you see weird holes or overlaps when device is rotated or a new subview is added. Quite often the proper setting of these masks for subviews can get you a nice looking view in both orientations without having to lay out subviews manually - but usually it takes some experimenting.
Edit2: (since this is still gathering upvotes) Autoresizing masks are now mostly superseded with "Auto Layout", which allows for much more flexible constraints on views' sizes and positions. That being said, translatesAutoresizingMaskIntoConstraints is still occasionally useful for dynamically added views.
The purpose is that UIView properly shifts and resizes when its superview changes due to resizing, orientation change, showing editing controls in tableview cells etc.