Laying out a hierarchy of views - ios

So far in my iOS project, I've done almost everything in code. I've laid out my views by setting frames manually in the layoutSubviews and viewWillLayoutSubviews methods. However, this is a very manual way and I'm considering other methods, such as AutoLayout...my problem is that I'm inexperienced with AutoLayout (and other methods such as using Auto-resizing masks).
My problem with manually laying out is as follows:
Let's say I'm adding subviews to a view controller's view. Now let's say I'm adding subviews to those subviews, etc. My problem is that many of those child views are dynamically-sized, i.e. their size depends on data from a server.
So now it comes time to layout the child views' frames from the parent view - since parent views define the frame of their child views before the child views layout. But while the child views have been instantiated, they haven't been laid out, so they haven't set their frames, so I don't know how large they will be.
This has been extremely annoying. My solution to this problem has been to implement a method for these child views which returns how large they will be based on their data. This is extraneous, time consuming and tedious.
My question is - will switching to AutoLayout (or Auto-resizing masks) make these problems go away? Will they lend themselves to the same amount of re-usability that setting frames manually does? Am I just doing something wrong in general?

Related

What are the advantages of using Auto Layout with views created programmatically?

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.

Mass auto layout option for 40 viewcontrollers?

I designed about 40 view controllers using a 5.5 inch storyboard layout. After all of that I tested it on the iPhone 4S...big mistake. everything is jumbled together being for a larger screen size. I was able to fix one view controller up using Size Classes. I am wondering if there is any way I can adjust all 40 at the same time, or at least avoid doing this for every single one. It is really frustrating finding this out now. Thanks!
This is a relatively complicated issue you are attempting to solve, but I have two potential solutions. Both suggestions are based on moving your current interface into containing UIScrollView instances
If you are using storyboards, then for each of your view controller scenes, put a UIScrollView as a descendent of the view controller's view. From there, provided your subviews are contained within other views (like a container view for a set of buttons), you can move those into your scroll view. You will have to setup constraints to define the size of the scroll view's content, but this will allow the size of the device to have a smaller impact on the interface as you will get scrolling as needed.
If you are using nib files (.xib) then it is essentially the same thing, but easier. In this case, move a UIScrollView onto the canvas, but not as a subview of the default view. Once that is out there, move the original view to be a subview of the scroll view and set constraints to be 0 from the subview to the scroll view. Finally, right click drag from the File's Owner icon to the scroll view and set that as the view outlet.
Hopefully one of these will help you.

Using AutoLayout in Custom Views vs View Controllers

I've been using the old way of doing things for quite a while, by setting the origin and size on individual frames and putting things exactly how I want them on the screen. Now i'm trying to move to using AutoLayout. But I'm having a bit of trouble that I cant seem to find an exact answer for.
I'm used to mainly using code for creating views and view controllers. As such, I've come familiar with creating custom view classes which combine multiple views to my liking. For example, i have a custom Profile Pin view that combines a UIImageView and a UILabel into one view to represent a profileImage and username. I then use these custom views in different places as a part of other views and view controllers.
It seems to me that autolayout is intended for views that need re-adjust sizing when screen orientation changes, or for readjusting subviews for different screen sizes.
My question is, should I be meticulous and use autolayout for custom views that have static placement and sizes? Like the UIImageVIew and the UILabel view in the Profile Pin view I described? The positions for the UIImageView and the UILabel view wont change, but the position for the Profile Pin view (their parent) probably will. Should I be using Autolayout for everything? Or is this not a situation that AutoLayout was intended for?
If your custom view has static contents where the size and position of it's subviews never change, then feel free to stick with manual frames internally. There's not much benefit to Autolayout in that case.
What you should do, though, is override intrinsicContentSize and return the correct size. This allows any parent view to use Autolayout to position and know the size of your custom view.
Many UIKit components use this technique - UISwitch being a prime example.

Handling interface rotation in iOS 7

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

Best practice for modifying storyboard auto layout constraints in code?

I've always done my UIs in code but have decided that I should use storyboards and auto layout for a current project. Everything had been going well until I built a complex scene with about 50 views with lots of hierarchy and some grid of views.
The problem is that my auto layout is getting muddled on some devices and orientations. I'm finding it challenging to use IB to try fixing the dozens (hundreds?) of constraints or to track down the problems and resolve them. The situation is such that I'm not getting errors or warnings, just some unpleasant layouts at times. And IB can be a pain with all the clicking and changing settings you need to do to track down constraint information, let alone get a full idea of how they all relate in a scene.
I've just spent a day reading docs and background material on auto layout and constraints and it seems my best solution is to use the visual format to specify constraints in code and create some custom code to help. However, I can't seem to find anything on how to make the transition from IB to code.
Specifically, should I wipe all IB constraints and do them all by hand or is it possible to be selective? I ask because I have some groups of views in containing views where the content views have a perfect layout.
Secondly, where best do I put my code? I want to coexist storyboards and just want to selectively modify some complex scenes. Is a view controller's viewWillAppear: the right place to modify or remove/add constraints for the view it controls?
Connect an IBOutlet for the NSLayoutConstraint you want to be able to modify in the storyboard/xib file to your controller/view class.
Once you have the layout object connected, you can modify the .constant property and animate the view:
[self.containerView layoutIfNeeded]; //make sure all layout operations are done
self.containerViewBottomLayoutConstraint.constant = 200.0; //change the layout
[UIView animateWithDuration:duration animations:^{
[self.containerView layoutIfNeeded]; //animate the changes
}];
updated: you can add your modification code in viewDidLoad, awakeFromNib, viewDidAppear, or event based. It really depends on your intentions.
Sorry to take so so long to get back to this while other projects intruded.
I had to do a lot of refactoring to simplify my scenes so that auto layout could do the right thing, and yet I am not fully satisfied with the results. The problem seems to be that IB is just not easy to use with lots of items, and that auto layout is complicated, by necessity.
With that said, the best results I've seen so far are drawn from this article by Justin Driscoll: http://themainthread.com/blog/2014/02/building-a-universal-app.html
He advocates building custom views to encapsulate reusable UI components. I have taken this approach but have extended the idea to also bundle up related components that are not going to layout very differently as the layout changes. For example, I have a progress bar with button and two labels, so even though I am not reusing them as a group, they need to be adjacent and conceptually are related, so I've made a custom view for them which handles the auto layout as Justin suggests.
I'm now taking the approach that each level of auto layout should only have a handful of elements. If one level gets too complex, I'll bundle up some related items in a custom view and push some auto layout inside that new view. So far it isn't too bad.
Auto layout can be really tricky when using that many views. I have used similarly complex views structures, and I find that it is best to try to keep all of the constraints in code or in IB. Right now we are keeping them in IB. The only time that we move a constraint into code is when we are supporting a different screen size, and we need to modify a single constraint for the view to work right. I have always modified the constraints in viewDidLoad myself.
When something gets messed up I almost always have to nuke all of the constraints on that view and start over. It sucks, but it's often quicker than tracking down the problem. One thing that we do that makes it easier to deal with that sort of thing is to use .xibs along with your storyboard. That way, each view can handle it's own layout, and you can pull that into a view that is sitting in a storyboard.

Resources