Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
340 views
in Technique[技术] by (71.8m points)

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 Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

enter image description here

Then you had to create clear UIViews and add them as your subviews and then add your constraints to them like below:

enter image description here


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:

  1. Instantiate a new layout guide.
  2. Add the layout guide to a view by calling the view’s addLayoutGuide(_:) method.
  3. 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:

  1. If you are using the 'view debug hierarchy' then you would be seeing more instances of UILayoutGuide
  2. Just like a UIView, a UILayoutGuide instance has all kinds of anchors
  3. 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."

enter image description here

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)

enter image description here 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) 

enter image description here

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: enter image description here

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:

enter image description here

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...