What is auto layout Baseline? - ios

When i use Auto Layout i typically attach objects to bottom, instead of baseline. But what is - baseline? I could not find good answer from apple docs, either on SO.
From here i only figured out that baseline is just a horizontal line, that is upper from bottom, but lower the centered Y line.
Can someone provide explanation what is it and why should anyone use it? For me it is much more easier to just attach view to bottom.

According to Apple docs, the baseline of a UILabel or other UI element with text is the "vertical alignment of the text within the label". In the docs for UIView it states:
When you make a constraint to a view’s baseline attribute, Auto Layout
uses the baseline of the view returned by this method. If that view
does not have a baseline, Auto Layout uses the view’s bottom edge

Related

Intrinsic Content Size vs Alignment Rect

When would you need to override alignmentRect instead of intrinsicContentSize in some UIView subclass?
Is it just for when the position as well as the size is different? If you supply an alignmentRect, is the content size ignored?
intrinsicContentSize is used to tell the layout system what the size of a view is. Use this to inform the layout system how large to draw a view based on its contents. Content size, not position.
For positioning, there are two things you should look at:
layoutMargins, which is used to determine the layout of the inside of a view (i.e. padding on the left and right of a stackView's contents), and alignmentRectInsets, which is used to inform the object holding your view how it should align your view. For example, if you have a shadow or attached view (like a notification dot), you might want to lay your view out centered on the primary feature, not including the shadow or dot's width/height.
The article Auto Layout in iOS 8 - Layout Margins at Carpeaqua does a good job of explaining layout margins with examples, and the article Auto Layout and Alignment Rectangles at Use Your Loaf does a good job of explaining and showing why you might want to use an alignmentRectInsets.

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 should I do to make generic size of all UI controls in iOS?

I was trying to practice Auto Layout in iOS, and I started with very simple UI. Please see image for understanding my problem.
All the text files are in middle of screen (I have deliberately kept on guide line), still you can see in preview, controls are not fully shown. I have not chosen specific size. Size is 'Inferred' still I am not able to see all the controls on UI.
I tried both adding and removing Auto Layout, but no luck. What should I do to create generic UI which will work with all the sizes of iPhone and iPad.
This image is without use of Auto Layout.
After enable autolayout and size classes you have to apply autolayout constraints.
Autolayout is a detail topic. Few basic things when applying autolayout is:
UI element need four constraints.
position x
Position y
height
width
So you will select first label (Number 1). Then press control and drag to superview. You will be provide options. Select Leading space (This will handle x position)
This is the way you can press control and drag:
http://www.appcoda.com/wp-content/uploads/2014/07/auto-layout-login-trailing.gif
Go to size inspector. You can see the constraint.
Press edit and change its value to 25(for test).
similarly control and drag again to superview and select Top space. (This will set y position for label)
This is simple way for the above taken from AppCoda
http://www.appcoda.com/wp-content/uploads/2014/07/auto-layout-control-drag.gif
You can change the value of these constraints according to your need.
UILabel and uitextfield get width and height from their content size. So don't need width and height constraints.
Now when you preview on any device this label will be stick on top left side of screen.
So this is a complete mechanism. You have to apply constraint to every ui element.
Below is a link to very comprehensive tutorial by
http://www.raywenderlich.com/115440/auto-layout-tutorial-in-ios-9-part-1-getting-started-2
At start this tutorial tried to create three views using autolayout. At the end it shows very similar scenario like yours by applying constraints to button and labels.
The problem here is that your constraints are not set correctly to work with every size of iPhone and iPad. You are setting the leading edge constraint to be a fixed size from your view controller's view to the subviews.
The simplest way to solve this issue would be to have a container view that you center in the view controller's view and then use constraints to set 'Center X Alignment Constraint' and 'Center Y Alignment Constraint' to set the container view's center to that of the view controller and then add your subviews to the container view.
As a side note auto layout has a reputation of being hard to learn, you have to put the time in to learn it, I would start with Apple's Auto Layout Guide.

Auto Layout Not So Auto

I have the most basic set up possible. See pic 1:
Believe it or not this is my first project using AutoLayout, I have created everything prior programatically. This basic set up is literally a UIWebView with 1 custom UIView positioned at the bottom. Previously I was using a tool bar that handled everything for me and had no issues with constraints whatsoever. However, the tool bar created discrepancies for event handling when adding a UILongPressGesture to the subview of the UIBarButtonItem so I decided to convert the tool bar to a UIView (Even inserting a UIView into a tool bar, it naturally converts to a button item) for easier handling. But run-time, the view gets pushed off screen by half of the UIView size (48px) See Pic 2. Then when I add buttons, it just gets worse:
I have reviewed the documents and the support HERE with no results. I've spent about 24 hours in total researching it and learned a lot, so my efforts aren't in vein. I KNOW by 'Adding Missing Constraints', the constraints are just recommendations based on the current set up, they aren't concrete in all cases, so I did try to create my own with control drag after reviewing the documents but my set up concluded with no different results, and exponentially more sloppy. So I decided to include the populated constraints by Xcode below :
UIWebView Constraints
Custom UIView (toolBar) Constraints
Any solid starting point recommendations? Does Intrinsic Size have anything to do with it?
EDIT : WORKING CONSTRAINTS I didn't realize you could simply omit a constraint. It seems the culprit was adding one to the top layout guide.
Just for answerer #Matt :
Constant 0 result : there are small gaps at edges
-16 for leading space/trailing space results as a true toolbar emulation in my case with no outstanding warnings or issues. Thanks
Let's talk about the view at the bottom of your interface and how you would use auto layout to position and size it the way a toolbar would be positioned and sized.
To use auto layout, you need to supply sufficient info to determine both position and size. This view is a subview of the view controller's main view. The main view will be resized depending on the screen, so we want to use auto layout to resize the subview ("toolbar") as well. This is what auto layout is for!
So constrain subview leading edge to the leading edge of the superview, and constrain subview trailing edge to the trailing edge of the superview, both with a constant of 0. Now the right and left edges match the superview!
That takes care of horizontal position and size.
Now let's talk about vertical position. The position should be the bottom. So constrain subview bottom edge to the bottom layout guide of the view controller, again with a constant of 0. Now the bottom of the view is at the bottom!
The only thing we don't know is the top of the subview. This, in our situation, is the same as knowing its height. So give the subview a height constraint, set its constant to a reasonable value like 40, and you're done.

UIScrollView behaves different depending on fullscreen or not?

When applying auto layout constraints to a UIScrollView, i´m given an error message if the scroll view is laid out to be full screen, but not if it has "margins". I´ll illustrate with 2 examples. In both examples i apply leading and trailing space to the superview and add top and bottom space to layout guide.
Example 1 (behaves as i expect.)
The auto layout constraints are blue, everything is in order
Example 2 (weird behaviour)
I stretch out the scroll view and apply the same rules as in example 1, but now 1 of the constraints ends up different. The "top space to top layout guide" is added as "Vertical space - (-548) - Top layout guide - Scroll View".
And then Xcode complains that i need "constraints for y position or height" for my scroll view.
Can anyone explain why this is?
Unfortunately, I think this is a bug in Interface Builder in Xcode 5. When you try creating a constraint from the top of a view to the top layout guide, IB usually (incorrectly) adds a constraint from the bottom of the view to the top layout guide.
To get around this, try first resizing the scrollView so the top edge of it is much lower down in your viewController:
Then try ctrl dragging from the scrollView to the viewController, and adding a constraint to the top layout guide. You can then select this constraint and adjust the constant in the inspector so that the scrollView aligns with the top of your viewController:
Alternatively, create your constraints in code :)
Just select the scroll view and manually create constraints for the top, left, bottom, and right edges.
What is happening here is that the automatically created constraints don't make sense, in that they do not fully define the size of the scroll view, only its position.
Following your original example with the scrollview smaller than the containing view I noticed that XCode was actually adding one of the constraints wrong in my case, when I selected "Top space to top layout guide" it was adding a "Bottom space to top layout guide" constraint with a large negative value. I can see something similar from your second screenshot.
My solution was to follow your example with the UIScrollView smaller than its container. I then manually dragged out the constraints, resized the UIScrollView to fit the container. The final step was to take the now correct constraints with the wrong constants and set them all to be 0.
Hope that helps, I'm still working on fixing the problem I had but I noticed this stage was similar to what you posted.

Resources