In iOS, what's the difference between autoresizing, AutoLayout, and constraints? - ios

In reading through the Apple documentation, I find references to autoresizing, AutoLayout, and constraints. What's the difference between using all of these in code? What is the correct way to apply these techniques programmatically in iOS 9?

There are really just two things here:
Autoresizing
AutoLayout
Autoresizing is basically a collective term for the old way Apple introduced in order to enable developers to build dynamic layouts. The number one usecase to address here was screen rotation. Since when a screen would be rotated (or otherwise resized), the subviews in the screen would most likely hold an incorrect frame (position and size) in the newly sized superview. To address this, Apple introduced a series of enumerable properties (called Autoresizing Masks), that tell the superview to treat a subview in a particular way. Among these are:
Flexible Width/Height, which causes a subview to expand to the fullest width/height available
Flexible Leading/Trailing/Top/Bottom space, which allows a specific edge to be variable, and so forth.
A view could contain any combination of these enum properties.
This was inadequate because, among other shortcomings, it lays down no rules regarding how a view should be layouted (if that's a verb) with respect to its other sibling views. It also required a lot of extra coding to manually resize views on orientation changes.
Here's where AutoLayout entered the picture. Apple built a framework which worked on the basis of constraints - rules that could be applied on views and between views, that would determine how a view would be sized in variable screen sizes. These constraints are structured in a class called NSLayoutConstraint, and each instance (constraint) of this class has the following important properties:
The item (view) on which the constraint is applied
The property of the view (height, width, leading edge, trailing edge, and so on) that the constraint is applicable to
The second item (a sibling or a child or a parent view) to which the constraint is related
The second item's attribute
The multiplier on the constraint: useful in order to specify ratio based constraints
A value (or constant) of the constraint: interestingly, the only property of a constraint that can be changed after instantiation.
A simple example of an NSLayoutConstraint, stated prosaically is: a view's width will be half the the width of its superview multiplied by 60%.
Your AutoLayout based UI would consist of many such constraints, which will all work together to express an unambiguous and non-conflicting UI Layout.
Now the AutoLayout engine, which makes it all work, interacts with views on the screen, calling AutoLayout messages such as layoutSubviews whenever needed in order automatically resize (layout) views whenever changes occur on screen, such as orientation change, a superview getting resized etc.
Constraints are most commonly added by InterfaceBuilder (.xib and .storyboard files), but adding them by code entails the same principle: create an instance of NSLayoutConstraint and add it to the highest view applicable (for eg., if there's a constraint between a child and a parent view, the constraint should be added to the parent view. If there's a constraint between two subviews, again, add it to the parent.)
Apple's AutoLayout guides, API documentation and introductory WWDC videos on AutoLayout are excellent, and those would be the best places to learn more.

Related

On iOS, what are the differences between margins, edge insets, content insets, alignment rects, layout margins, anchors...?

There seem to be several different options/terms and people in the iOS community use with respect to layout (e.g. UIEdgeInsets is a type, but sometimes I hear/read "set the insets" or layout margins vs layout guides).
I've always been able to find an option that works. But I'm never sure that I'm using the right tool for the job.
Can someone help provide some clarity between these different aspects of layout and when to use each in the best way?
See this UIKit: Apps for Every Size and Shape before and after reading this. Might also want to see the notes from here as well.
Being the Bounty offerer...I'd say the majority of my confusion came from not properly understanding the UILayoutGuide class. That is key, but also very simple.
Let me first introduce a problem:
In the old days, if you needed to constrain these circles like this:
Then you had to create clear UIViews and add them as your subviews and then add your constraints to them like below:
Today you don't need to add them as your subviews. You could instead just use
Layout Guides
To create a layout guide, you must perform the following steps:
Instantiate a new layout guide.
Add the layout guide to a view by calling the view’s addLayoutGuide(_:) method.
Define the position and size of the layout guide using Auto Layout.
You can use these guides to define the space between elements in your layout. The following example shows layout guides used to define an equal spacing between a series of views.
steps:
let space1 = UILayoutGuide()
view.addLayoutGuide(space1)
let space2 = UILayoutGuide()
view.addLayoutGuide(space2)
space1.widthAnchor.constraintEqualToAnchor(space2.widthAnchor).active = true
saveButton.trailingAnchor.constraintEqualToAnchor(space1.leadingAnchor).active = true
cancelButton.leadingAnchor.constraintEqualToAnchor(space1.trailingAnchor).active = true
cancelButton.trailingAnchor.constraintEqualToAnchor(space2.leadingAnchor).active = true
clearButton.leadingAnchor.constraintEqualToAnchor(space2.trailingAnchor).active = true
Layout guides can also act as a black box, containing a number of other views and controls. This lets you encapsulate part of your view, breaking your layout into modular chunks.
Three interesting notes:
If you are using the 'view debug hierarchy' then you would be seeing more instances of UILayoutGuide
Just like a UIView, a UILayoutGuide instance has all kinds of anchors
As for why not just create dummy UIViews and going through creating UILayoutGuides: "There are a number of costs associated with adding dummy views to your view hierarchy. First, there is the cost of creating and maintaining the view itself. Second, the dummy view is a full member of the view hierarchy, which means that it adds overhead to every task the hierarchy performs. Worst of all, the invisible dummy view can intercept messages that are intended for other views, causing problems that are very difficult to find."
For more see documentation.
topLayoutGuide vs. safeAreaLayoutGuide
topLayoutGuide (deprecated)
It's deprecated for but for learning purposes: A UIViewController has 2 dummy boxes. 1 property at the top named topLayoutGuide and another property at the bottom named bottomLayoutGuide. The viewController itself doesn't have any guides for its left/leading or right/trailing sides. Both of these are an instance of UILayoutGuide
if constrained to view.topAnchor ie:
tableView.topAnchor.constraint(equalTo: view.topAnchor)
tableView doesn't start from the bottom of the navigationBar. Notice the orange behind the navigationBar...
However if you constrained it to topLayoutGuide.bottomAnchor ie:
tableView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor)
then tableView starts from the bottom of the navigationBar
And depending on your layout design you might want your content to be blurred below the navigation bar.
And the idea was that you would display your content edge to edge. And
it would underlap the bars so that you could get these nice colorful
blurs with your content through the bars
For more see this moment from WWDC and this question here. I don't think the solutions are exactly related, just the image in the question.
safeAreaLayoutGuide
since iOS11
Apple has deprecated topLayoutGuide & bottomLayoutGuide. So
instead of having two dummy boxes, you now have one dummy box named
safeAreaLayoutGuide on the UIView instance. UIViewController no longer has any of this...
A visual comparison copied from useyourloaf:
side note: If you use storyboards then aligning your views to the topLayoutGuide or top of safeAreaLayoutGuide would render the same. If you don't use storyboards (do it programmatically) then you would have to dance between iOS11 and and LessThaniOS11 and have 2 different versions of code
For more on safeAreaLayoutGuide, I highly recommend that you set Apple's article on: Positioning Content Relative to the Safe Area
NOTE: safeAreaLayoutGuide is a UIView property. topLayoutGuide is a UIViewController property.
layoutMarginsGuide
UIView has only 1 dummy box. The property is named layoutMarginsGuide . But unlike UIViewController it doesn't sit at the top or bottom. It just sits at the center with 8points padding/inset (from all 4 sides) into the UIView.So where is this useful?: You would use this if you don't want your textView to be constrained to the edges of a UIView instance. This would improve the reading experience. Or instead of constraining a button to the leadingAnchor of its superview and making it look ugly, you add 8 points to the anchor...ie constraint the button to the leadingAnchor and then adding 8 constant points. The striked text, is actually where you would use readableContentGuide, layoutMarginsGuide is useful if for when you don't want your button or label anchored to the edge of its superview
someButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8)
But wait there is an easier way. Just use Apple's recommended margin ie use:
someButton.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor)
Also see the example provided in
documentation.
A good Raywenderlich tutorial can be found
here
readableContentGuide
Is slightly different from layoutMarginGuide. Both are properties of UIView. Sometimes they are identical sometimes they aren't. It's purpose is:
This layout guide defines an area that can easily be read without
forcing users to move their head to track the lines
For more see this moment from WWDC: building Adaptive layout and this awesome useyourloaf tutorial.
On the iPhone 7 Plus in portrait, readable content guides are the same
as the view’s margin guides, but in landscape there is more white
space on either side of the text view. On the iPad in landscape, the
white space is increased significantly.
The margin size depends on the system’s dynamic type. The larger the
font, the wider the guide will be.
From RayWenderlich
In the image below the cyan is anchored to the layoutMarginGuide, but the green is anchored to the readableContentGuide:
UIEdgeInsets
If you want to change your layoutMarginsGuide ie change the desired margin from 8 points to 16 points then you must change the layoutMargins's value and then the layoutMarginsGuide's anchors would get automatically updated.
UIEdgeInsets is just the type of your layoutMargins. layoutMargins is a property name of the UIView class
someview.layoutMargins = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)
The only place I found this code ☝️ to have its effect is inside viewDidLayoutSubviews.For more see here
Anchors
They're foundational but nothing special to it. They are the farthest edge of any UIView/UILayoutGuide. Both UIView and UILayoutGuide instances have it. EVERYTHING you constrain is eventually constrained using anchors, it's just a matter of to what entity's anchors you are anchoring it to. It could be a safeAreaLayoutGuide's anchor, it could be a layoutMarginGuide's anchor, it could be a topLayoutGuide's anchor it could be a view's anchor. (though you may also just anchor your heightAnchor to 50, so in that case there isn't another anchor)
A great visual comparison between layoutMarginsGuide and Anchorscan be found from this answer. It's done using storyboards so it makes it easier to understand.
contentInsets
While it's important to understand, is a totally different discussion and has nothing to do with the others. It's specific to UIScrollViews. For more see this great article
Conclusion
To be safe and sure everything is inside your view use safeAreaLayoutGuide. If you want to use the system provided margins to have better layout of views or have some of padding then, use layoutMarginGuide, if you want to make things more readable readableContentGuide.
The readableContentGuide's size is always smaller or equal to layoutMarginGuide.
The layoutMarginGuide's size is always smaller or equal to safeAreaLayoutGuide
layoutMargins is very similar to CSS's padding. safeAreaLayoutGuide is similar to CSS margins. I don't know if there is any CSS equivalent for readableContentGuide
Addendum: ContentInset vs. contentOffset
They are for scrollViews, and somewhat unrelated to the rest of the answer. As for what contentInset & contentOffset are, please see this moment from WWDC 2018: UIKit: Apps for Every Size and Shape
. The video is very simple. Also refer to Karthik's answer below. Having that said it's vital that you fully understand how a scrollView works and understand what contentSize is, otherwise it would be complicated. For more on contentSize and scrollView see Vacawama's answer here
I hope you will get the info from the following links/pictures.
You will be able to deduce the required information on layout parameters from below links.
alignment rects.
content inset and offset example 2
margins
Sorry if this is a boring answer, but I feel like the Apple Developer documentation is pretty good at describing how each UIView property is intended to be used.
As for what is the best way of implementing a layout when you have multiple methods/options that work... that's really a question of style/opinion. I'd focus on the following criteria to come to a decision:
Consider eliminating the options that are hardest to maintain/debug/understand
Imagine someone brand new joining your team or someone inheriting your code - which implementation would they have difficulty debugging and why?
Consider eliminating the options that make the layout more difficult to rearrange/expand/edit
Consider eliminating the options that are inconsistent with the rest of the app (this kind of goes back to the first point)
Consider eliminating the options that go against Apple's intent (see the docs/WWDC talks to get a sense of their intentions).
A team could work together through this criteria to come to agreement on "how we layout our UI" in each scenario. I guess what you're asking for is the sum-total product of such discussions: a layout-styleguide of sorts.
In my experience, this has always developed organically on the teams I've worked on, and things weren't really written down. It was a lot more of following point 3 above.
Some excerpts from the Apple documentation for classes that are described in the question:
On UILayoutGuide:
Use layout guides to replace the dummy views you may have created to represent inter-view spaces or encapsulation in your user interface. Traditionally, there were a number of Auto Layout techniques that required dummy views.
...
The UILayoutGuide class is designed to perform all the tasks previously performed by dummy views, but to do it in a safer, more efficient manner.
On layoutMargins:
In iOS 11 and later, use the directionalLayoutMargins property to specify layout margins instead of this property.
On directionalLayoutMargins:
Use this property to specify the desired amount of space (measured in points) between the edges of this view and its subviews. The leading and trailing margins are applied appropriately to the left or right margins based on the current layout direction.
On contentInset:
Use this property to extend the space between your content and the edges of the content view. The unit of size is points. The default value is UIEdgeInsetsZero.
On topAnchor:
Use this anchor to create constraints with the view’s top edge. You can combine this anchor only with other NSLayoutYAxisAnchor anchors. For more information, see NSLayoutAnchor.
On NSLayoutAnchor
Use these constraints to programatically define your layout using Auto Layout. Instead of creating NSLayoutConstraint objects directly, start with a UIView, NSView, or UILayoutGuide object you wish to constrain, and select one of that object’s anchor properties. These properties correspond to the main NSLayoutConstraint.Attribute values used in Auto Layout, and provide an appropriate NSLayoutAnchor subclass for creating constraints to that attribute. Use the anchor’s methods to construct your constraint.

What is the basic difference between Auto Layout and Auto Resizing in iOS

I have been searching the proper difference between Auto Layout and Auto Resizing, but didn't able to find the exact answer. Where I can use "Auto Layout" and where "Auto Resizing" in app? Any help would be a part of thanks.
As Matt Neuburg states in his book:
Autoresizing is a matter of conceptually assigning a subview “springs
and struts.” A spring can stretch; a strut can’t. Springs and struts
can be assigned internally or externally. Thus you can specify (using
internal springs and struts) whether and how the view can be resized,
and (using external springs and struts) whether and how the view can
be repositioned.
And
Autolayout, depends on the constraints of views. A constraint (an
instance of NSLayoutConstraint) is much more sophisticated than the
"autoresizingMask" it’s a full-fledged object with numeric values, and
can describe a relationship between any two views (not just a subview
and its superview).
I recommend watching the WWDC 2012 session https://developer.apple.com/videos/wwdc/2012/?id=202
Auto Layout is a new way to define dynamic GUIs. Before, we had autoresizing masks, that described how a subview will resize or move when its superview is resized. With Auto Layout you can do the same and also a lot more complicated GUIs quite easily.
Autoresizing is one of the most useful property for layouting the views in their hierarchies.
go through this link.
http://www.techpaa.com/2012/05/understanding-uiview-autoresizing.html
AutoResizing : Autoresizing means that how the content of a view will fit to the view. It probably depends on the self content of the view.
AutoLayout: AutoLayout means how the external constraints like the position of the view , the size of view supported by the other neighboring components.
AutoLayout triggers the AutoResizing Task and for autoresizing activity , auto-layout forcefully or normally change or break the default constraints of views which are related to the corresponding view if needed.
Use autolayout in the superview with constraints [this helps when there are changes in the space in the View due to different devices(such as 3.5 or 4 inches retina)] and use autoresize for resizing the GUI objects in a view [this helps when there are changes in the space in the View during the orientation of the device]

Can you add a derived constraint using auto layout?

In mechanical CAD software which uses concepts similar to autolayout constraints, you can often add a 'derived constraint'. This has no effect on the layout, but will allow you to directly read the value of an important dimension.
For example, consider the following layout for a view with two subviews:
32 64
|------| |------|
|-[imgOne]-[imgTwo]-|
|-------------------|
w
The width of the view is the sum of the default edge spacing on the left and right, the default spacing between image views, and the two width constraints (32 and 64) applied to the image views.
I would like to know w at runtime.
In theory, this should be view.bounds. However, it's not always safe to read that property (as the view may not have updated its layout constraints yet).
Is it possible to add a constraint which has no effect on the view size, but which will have its .constant property updated once the layout is complete?
(I have tried adding a width constraint to the view with a priority of 1, but .constant always reads the nominal value, instead of the actual value.)
Adding a "derived" constraint will put you in the same hole you began in--waiting for Auto Layout to finish laying out your views. It's all about timing. When using Auto Layout, a good place to read the final geometries of your views is in your view controller's viewDidLayoutSubviews method. In other words, read the bounds of the view in viewDidLayoutSubviews.

resize superview after subviews change dynamically using autolayout

I cant for the love of god the the hang of this resizing superview.
I have a UIView *superview with 4 UILabels. 2 function as header for the 2 others.
The content in all 4 are dynamic coming from database.
SizeToFit vs SizeThatFits:(CGSize) vs UIView systemLayoutSizeFittingSize:, passing either UILayoutFittingCompressedSize or UILayoutFittingExpandedSize.
I use autolayout programatically and have set the superview height to be equal or greater to a dummy number.
where and how do I use these SizeToFit vs sizeThatFits:(CGSize) vs UIView systemLayoutSizeFittingSize:, passing either UILayoutFittingCompressedSize or UILayoutFittingExpandedSize. I have read a lot of tips here on stack but ended up with nothing.
DO I need to recalculate the constraints for the superview somewhere specific. Maby setting the height to be ´#property` in its controller class and remove and readd it?
Atm I have tried to put everything everywhere and then some. Still I get the same size end result with the dummy height and text floating outside. Even after setting clipsToBound on subview.
I am scratching my hair of.. help
If you're using Auto Layout, here's what you need to do:
Make sure you aren't adding fixed width and/or height constraints to any of your subviews (depending on which dimension(s) you want to dynamically size). The idea is to let the intrinsic content size of each subview determine the subview's height. UILabels come with 4 automatic implicit constraints which will (with less than Required priority) attempt to keep the label's frame at the exact size required to fit all the text inside.
Make sure that the edges of each label are connected rigidly (with Required priority constraints) to the edges of each other and their superview. You want to make sure that if you imagine one of the labels growing in size, this would force the other labels to make room for it and most importantly force the superview to expand as well.
Only add constraints to the superview to set its position, not size (at least, not for the dimension(s) you want it to size dynamically). Remember that if you set the internal constraints up correctly, its size will be determined by the sizes of all the subviews, since its edges are connected to theirs in some fashion.
That's it. You don't need to call sizeToFit or systemLayoutSizeFittingSize: to get this to work, just load your views and set the text and that should be it. The system layout engine will do the calculations for you to solve your constraints. (If anything, you might need to call setNeedsLayout on the superview...but this shouldn't be required.)
Use container views
In the following example I have a 30x30 image, and the UILabel is smaller than the containing view with the placeholder text. I needed the containing view to be at least as big as the image, but it needed to grow to contain multi-line text.
In visual format the inner container looks like this:
H:|-(15.0)-[image(30.0)]-(15.0)-[label]-(15.0)-|
V:|[image(30.0)]|
V:|[label(>=30.0)]|
Then, set the containing view to match the height of the label. Now the containing view will ride the size of the label.
As #smileyborg pointed out in his answer, connecting the content rigidly to the superview informs the layout engine that the simple container view should cause it to grow.
Yellow alignment rectangles
If you want the yellow alignment rectangles add -UIViewShowAlignmentRects YES in your scheme's list of run arguments.
This almost follows #smileyborg answer and comes with a concrete example.
Won't describe all constraints, but those related to the calculation of the height of UI objects.
[Label] Labels must not have a fixed height constraint, in this case, AutoLayout won't resize labels to fit the text, so setting edge constraints is the key. (green arrows)
[Subview] Steps 1 and 3 are very easy to follow, but this step can be misunderstood. As in the case with labels, subviews must not have height constraint set. All subviews must have top constraint set, ignoring bottom constraint, which can make you think will trigger unsatisfied constraint exception at runtime, but it won't if you set bottom constraint for the last subview. Missing to do so will blow the layout. (red arrows)
[Superview] Set all constraints the way you need, but pay big attention to the
height constraint. Assign it a random value, but make it optional, AutoLayout will set the height exactly to fit the subviews. (blue arrows)
This works perfectly, there is no need to call any additional system-layout update methods.
This was made dramatically easier with the introduction of Stack Views in iOS 9. Use a stack view inside your view to contain all your content that resizes, and then simply call
view.setNeedsUpdateConstraints()
view.updateConstraintsIfNeeded()
view.setNeedsLayout()
view.layoutIfNeeded()
after changing your content. Then you can get your new size by calling
view.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
if you ever need to calculate the exact size required for a view.

Xcode IB Storyboards orientation and container views

In an IOS 6 iPad app, I have a container view controller with multiple container views. Currently I have locked my app to landscape and works fine but I'd like to support portrait as well. Everything is set up using auto layout and constraints via Interface builder. If possible I'd like to keep one storyboard for consistency, maintainability etc.
The layout is this: Header, Left-side menu, two content panes (side by side in landscape) and a foot pane, I have all panes resizing except the content. When rotating from landscape to portrait I'd like one content pane to slide below the other (currently it blows off the screen), and both to stick to the edges of the container view.
In HTML5/CSS3 this would be easy to do but I'm stuck in IB, any help/ideas would be great!
Thanks!
EDIT:
Solution -
Following #Charles A.'s suggestion, I linked the NSLayoutConstraints to IBOutlets and manipulated spacing/priority in code. Where I really struggled was using Height and Width constraints. If you moved anything, Interface Builder would delete or override these with Leading/trailing & Top/Bottom constraints. I finally gave in and got rid off all height & width constraints, and used only leading/trailing/top/bottom.
2 things I figured out: Constraints have milestones(one at priority #750 for instance) so if you pragmatically change priority from 749 to 750, you will get:
Mutating a priority from required to not on an installed constraint (or vice-versa) is not supported.
But if you changed from 750 to 800 you are fine.
Also, after manipulating constraints, I needed to call:
[self updateViewConstraints]
I had found a post that suggested [parent updateViewConstraints], which didn't work, the one above did!
Anyway, I hope this helps others in this spot since there's not much out there.
It's hard to answer the question specifically without knowing how your layout constraints are setup to handle your two content views. Having said that, I would probably go about this UI by having my layout constrained similar to this (I'll use the visual format to describe, I'm assuming that the superview is the parent of the two content views in this case):
|-[contentViewOne]-0-[contentViewTwo(==contentViewOne)]-|
If the constraints are setup as above in the storyboard, you could create an IBOutlet of type NSLayoutConstraint* and connect it to the horizontal space constraint between the two content views (the one that specifies a constant value of 0, in my case above). When you animate from landscape to portrait, just set it's constant value to the negative width of contentViewOne. This should have the desired affect. You'll obviously also have to set it back to 0 when rotating from portrait to landscape.
It's worth noting that I assume your content views are equal width. If they're not you would potentially also need to make an outlet for a width constraint on the one sliding under in order to temporarily set it to the same width as the one covering it.
There are many potential ways to address this layout using autolayout. What I've described above is just one such way. It comes down to what makes sense given the layout you already have.

Resources