New features of iOS Xcode8 Auto layout

Source: Internet
Author: User
Tags uikit

Directory
    • 1.Incrementally adopting Auto Layout
    • 2.Design and Runtime Constraints
    • 3.NSGridView
    • 4.Layout Feedback Loop Debugging
I. Incrementally adopting Auto Layout

What does incrementally adopting Auto layout mean? When we layout our view in IB, we don't need to add all the constraints at once. We can increase the constraints step-by-step, simplify our steps, and allow us to set up more flexibly.

Before we talk about new features, let's start by introducing the background source for this feature.

There is a scenario where, imagine, we put a view on the parent view, this time does not set the constraints, when we run the program, it will appear.


It looks like everything is fine. But once we rotate the device 90°, it will appear.


This time you can see that the length, width, and top and left margins of the view have not changed. At this time we did not set the constraints, how is this done?

During the program's compilation period, auto layout's engine automatically implicitly gives view some constraints constraints to ensure that the view size does not change. In this example, view is added to the 4 constraints of top,left,width,height.

If we need more dynamic resize behavior, we need to customize the constraints within IB. Now that's the problem, is there a better way to do it? It is better to have a method without restraint, but also to achieve a simple resize effect.

Now there is a solution to the problem. In Xcode8, we can assign a autoresizing masks to the view without having to set the constraints. This means that we can do without restraint, and we can do simple resize effects.

Before the AutoLayout era, some people might recognize this UI way. This is a UI for springs & struts. We can set the edge constraint (Note: The constraint here does not refer to the AutoLayout inside the constraints, is the autoresizing masks inside the rules), regardless of the length of the view of how the width changes, These view changes are followed by a constraint-set view.


In the above example, the view margin does not change after you rotate the screen without adding constraint in Xcode 8. How did this happen? In fact, Xcode 8 's approach is to first remove the autoresizing masks and then convert it to the corresponding constraints, the timing of this conversion occurs during runtime. Generating the corresponding constraints occurs at run time, not at compile time because it gives us developers a more convenient way to add more granular constraints to the view.

On the view, we can set the Translatesautoresizingmaskintoconstraints property.

true

Assuming that the view has been added to Interface builder constraints, the Show the Size Inspector panel will still be the same as before. Click View to see all the constraints that are added to it, this time autoresizing masks is ignored, and the Translatesautoresizingmask property becomes false. For example, we don't see the Autoresizingmask Setup Panel on the "Show The Size Inspector" panel at this time.




Just before the AutoLayout era, we had been using autoresizing masks, but after the advent of the AutoLayout era, once the AutoLayout was ticked off, the previous autoresizingmask would have lapsed.

Back to our most primitive question, Xcode 8 is now applicable AutoLayout for view to support increments. This means that we can start with autoresizingmask, do simple resize work, and then, if there are more complex requirements, we add the appropriate constraint constraints to fit. For simplicity, Xcode 8 autolayout≈autoresizingmask + AutoLayout.

Next, use a demo example to illustrate the new features of Xcode 8 AutoLayout.
Before we say the example, let's talk about what the Xcode 8 adds to the storyboard. As we can see, at the bottom of the new Add a column, you can switch different screen sizes, you can see that the IPhone has now differentiated into 6 screen sizes we need to fit, from big to small, in order: ipad Pro 12.9, ipad 9.7, IPhone 6s plus/ iphone 6 Plus, iphone 6s/iphone 6, iphone se/iphone5s/iphone5, Iphone4s/iphone4. Below you can also choose the screen, and not the percentage of the screen adaptability.


Back to the example, we are now doing a simple autoresizingmask of the view on the page. The preview screen on the right is the effect we have after adding these masks.

First the pink parent view, we add the following autoresizingmask to it.


Add the following to the "Rainy Day" ImageView autoresizingmask


To the "cloudy" ImageView plus the following autoresizingmask


Finally, add autoresizingmask to our middle label.


This time we rotate the screen, everything is fine, and the layout of the view is as we wish.


This time we choose again, 3:2 sub-screen, this time there is a wrong situation. The width of the label is squeezed.


The reason is because autoresizing masks does not take into account the content of the view like AutoLayout, so it is squeezed.

To fix this label, we can easily add a constraints to fix it. But here we are going to talk about another approach.

Go to the Attributes Inspector panel, locate the AutoShrink property, and switch "fixed font size" to "Minimum font size"


This is the time to fix the above problem.


At this point is back to landscape, split screen case, has been able to display normal.


Then we'll deal with the middle temperature of the label. We have more complex needs at this time. This time we need to use the constraint.

This time we control the key, and then drag to the parent view, release, will pop up the menu. Let's hold shift again so we can select multiple constraints at once.



We select "Center horizontally in Container" and "center vertically in Container" at once. Note that this time the right side is still autoresizingmask panel, because this time the label has not any constraint. When we click "Add Constraints", the label is added to the constraint, the right panel has become the Constraints panel.

Let's add 2 more constraints to this label. "Horizontal Spacing" and "Baseline".


Similarly, drag the ImageView from the label to the "Sun" and add the "Horizontal Spacing" and "Baseline" constraints.

This time we update frame. As shown, select "Update Frames" and all frames are complete at this time.



This time we update the middle temperature of the label font size, when the calculation becomes larger, because our constraints are correct, both sides of the view will be larger with the label font becomes larger.


XOCDE 8 becomes smarter at this time and automatically updates the frame immediately.

We are continuing to add a background map to the sunny days of Shanghai. Add a ImageView, then size the entire parent view, and select Mode as "Aspect Fill"


The following general practice is to add constraints to this imageview to make this view the same size as the parent view. But this simple resize behavior in XOCDE 8 does not need to add constraint, here we use autoresizing masks to achieve. Add these mask to ImageView.


We put the ImageView in the background. At this point, all of our interfaces have been completed in the layout.


Test the effect of the horizontal screen


Even the split screen can complete the task!


Demo's github address, this demo is not difficult, is to see the effect.

This is the Xcode 8 incrementally adopting auto layout,autoresizing masks + auto Layout Constraint work together!

Two. Design and Runtime Constraints

In our development process there is a situation where the constraints of the view is added based on the data you have loaded. So before the app runs, we can't know all the constraints.

There are 3 ways to respond to the above situation.

1.Placeholder Constraints

Suppose now we need to put a picture in the middle of the view's vertical and horizontal, and there is a leading margin on the left edge. And it also needs to maintain its length-to-width ratio. And the final appearance of this kind of picture, we do not know. Only when we run, can we know what the picture looks like.

In order to see our images in Interface Builder, we have to estimate the length-to-width ratio first. Suppose we estimate to be 4:3. At this point, add constraints to the image and tick "Place order constraint", which will be removed at build time.


When we get the picture at run time, this is the time to add the appropriate constraint and length-to-width ratio.

2.Intrinsic Content Size

or similar to the one above, we sometimes customize some uiview or NSView, and the content in these view is dynamic. Interface Builder doesn't run our code, so we don't know the size of the app when it's running. We can set an intrinsic content size for it.


Setting a design time intrinsic content size only affects a view and editing in Interface builder.the view won't have This intrinsic content size at runtime.

Note the above description intrinsic content size is just equivalent to a placeholder at the time of layout, and this size is not at runtime. So if you really need to use this intrinsic content size in the development process, then we need the overriding content size

override var intrinsicContentSize: CGSize
3.Turn Off ambiguity Per View

This is a new feature of Xcode 8. When the above 2 methods are unable to solve our needs. This is the time to use this method. Xcode 8 gives us the ability to dynamically adjust the warning level when constraints creates ambiguity.


In this scenario, we only know that we need to put this imageview in the middle of the horizontal position, but the size of the ImageView and its horizontal position we do not know. If we only add this constraint, Interface Builder will report red because IB is not uniquely positioned to determine the current view based on the constraints we give.

If we get the full information of the picture after we run it, we know how to add constraints, we know how to make the layout to ensure that ImageView can only determine the location, then we can turn off the IB red warning. Find "Ambiguous", here is the level of warning, we choose "Never Verify" here, there is no red warning and error reminder. But the premise of this option is that we can guarantee that we will be able to add enough constraints to ensure that the location of the view is complete after the operation.

The above 3 methods are the solution that we add constraints to view at runtime.

Three. Nsgridview

This is a new layout container that MacOS brings to us.

Sometimes we are more troublesome to maintain the correctness of constraints, for example, even if we are a group of simple checkboxes, it is not easy to maintain constraints. This time we will choose to use Stack view to make our development easier.

is a set of checkboxes that MacOS apps often see.


At this time we use Ns/uistackview to achieve, because it has the following advantages, it can arrange a set of items, it is important that it can handle the content size and can control the spacing between each item.

But stack view still has some scenes that can't be handled easily. For example, a scenario.


This can still be done with stack view, but it doesn't help us to align rows and columns based on content.

This is why the new Nsgridview is being introduced.

With Nsgridview, we can easily align the content on the x-axis and the y-axis. Just by putting the content in a pre-defined grid, Nsgridview will help us manage everything that comes next.

Let's take a look at the following example.


Nsgridview has 2 subclasses, Nsgridrow and Nsgridcolumn, and they both automatically manage the content size. Of course we can specify size, padding and spacing size when needed. We can also dynamically hide some rows rows and Colunms columns.

Nsgridcell's job is to manage the layout of content view within each cell. If the contents of a cell go beyond the bounds of the cell, the cell is merged, just like the normal spreadsheet app does.


Let's build a simple interface. The design diagram is as follows:


We do not need to care about the sizing of the grid, we only care about how many content needs to be displayed in each row.

empty = NSGridCell.emptyContentViewlet gridView = NSGridView(views: [ [brailleTranslationLabel, brailleTranslationPopup],  [empty, showContractedCheckbox],  [empty, showEightDotCheckbox],  [statusCellsLabel, showGeneralDisplayCB], [empty, textStyleCB],  [showAlertCB] ])

The interface that runs out of the above code is this:


Although we call constructors right, there are some gaps in the interface and design that come out. The most obvious problem is that the UI has been pulled open and there are a lot of gaps.

The problem arises because the grid is constrained to the edge of the window. Our intent should be window to match our grid size, but now the problem becomes that the mesh is stretched to match the size of the window.

Our solution to this problem is to change the hugging priority of the grid view content. Although the constraints on the page already has a high priority, we can now continue to increase the priority so that constraints pushes the content away from the edge of the window. We raise some priority levels:

gridView.setContentHuggingPriority(600, for: .horizontal)gridView.setContentHuggingPriority(600, for: .vertical)

We will find that the content in the window is more aggregated, and the large gap in the middle is gone.

Let's deal with the gap in the middle of the window, and the label on the left is too far away from the content on the right. According to the design, we should put the label in the right order. This is easy, as long as we adjust the cell location information to complete. The position information that is arranged affects the cell, row, column, and grid views.

If you do not specify the placement property value for the cell, the column will be determined based on the placement property value of the GridView. This rule allows us to set the placement in one place, which can change the layout of a large number of cells in an instant.

//first column needs to be right-justified:gridView.column(at: 0).xPlacement = .trailing

We find the first column of the GridView and change its Xplacement property value so that the cell in the column becomes the right permutation.


After the right, we will have new problems, baseline is not aligned.


The alignment principle of the row is the same for the alignment column, so we only need to set one place, which will affect the entire grid view.

// all cells use firstBaseline alignmentgridView.rowAlignment = .firstBaseline

After the setup is complete, the entire grid view is aligned.

Next we'll change the margin of the pop-up button.


let row = gridView.cell(for: brailleTranslationPopup)!.row!row.topPadding = 5row.bottomPadding = 5

The first line of the practice here can be the same as before the first column, the direct removal of the row labeled 0. Here's a better way to do it. Locate the cell containing the pop-up button in the GridView, and locate the corresponding row row according to the cell. The advantage of this approach is that if someone adds another line at index 0 in the future, then the code goes wrong, and our code is never going to go wrong, because the cell containing the pop-up button is guaranteed to be removed. So the code inside try not to write dead fixed index, so later maintenance is more difficult.

In the same vein, we also add padding to the "status cells"

ridView.cell(for:statusCellsLabel)!.row!.topPadding = 6

Here we need to compare the difference between padding and spacing.

Padding is for the spacing between each row or column, we can add padding to change the spacing between 22.
Spacing is for the entire GridView, changing it will affect the layout of the entire grid view.

Take a look at our plans:


If there is no padding then it is like:


If there is no spacing then the appearance will appear:


If spacing and padding are not, then they are all squeezed together:


Finally, let's take care of the cell with the checkbox in the bottom line.


Here we need to use the previously mentioned, merging 2 cells.

// Special treatment for centered checkbox:let cell = gridView.cell(for: showAlertCB)!cell.row!.topPadding = 4cell.row!.mergeCells(in: NSMakeRange(0, 2))

Here we point directly to merging the first 2 cells.

After executing the code, it will look like this.


The last row of cells spans 2 cell locations. Although it occupies 2 cell locations, it still inherits the right-hand arrangement of the first column.

Now our demand is neither want it to be right, nor want it to reside on the left.
The checkbox actually supports arranging between 2 columns, but because the widths of the adjacent 2 columns are not equal, the GridView does not know how to arrange them. At this point we need to manually change the layout.

There may be people here who would like to direct the

cell.xPlacement = .none

The xplacement of the cell directly into none, this will suddenly disrupt the entire GridView constraints layout, we can not do so. We need to continue to add additional constraints to the cell to maintain the constraints balance of the entire GridView.

cell.xPlacement = .nonelet centering = showAlertCB.centerXAnchor.constraint(equalTo: textStyleCB.leadingAnchor)cell.customPlacementConstraints = [centering]

We just need to give the checkbox an anchor point in the x-axis. This time the checkbox will be arranged in the way we want it to be.


So far, we have completed the demand. To summarize, Nsgridview is a new control that helps us to do a grid-like layout well. It can quickly and conveniently arrange the content we need to show. Then we just need to adjust the padding and spacing information.

Four. Layout Feedback Loop Debugging

Sometimes after we set up constraint, we don't report any errors, but there are cases when we run up with a bunch of constraint conflicts in the Debug window, which can also crash the app directly. The crash was encountered with layout feedback loop.

In this case, it often occurs in the "transition period", beginning or end of time. If you click on a Button,button corresponding to your click, but then the button does not play up, has maintained the status of being pressed.


Then the CPU usage is observed, the memory doubles, and then the app crashes, returning a stack of layout-backtracking information.


The reason for this is that the layout of a view has been executed, executed, and trapped in a dead loop. Runloop will not stop and CPU usage will always peak. All messages are collected into objects that are automatically freed, and messages are sent all the time. So memory doubles as well.

One of the causes of this is the Setneedslayout method.


When one of the views has finished calling Setneedslayout, it is passed to the parent view to continue calling Setneedslayout, and the parent view's setneedslayout may call layout information to the other view. If we can call the caller after each other, that is, the view called the method, then we can analyze where these setneedslayout come from and where they are, and we can find the place of the dead circle.

This information is really difficult to collect, which is why Apple has developed such a tool specifically for us to debug and find the cause of the problem.

The switch to turn on the tool is in the "Arguments" option. Such as.


-UIViewLayoutFeedbackLoopDebuggingThreshold 100 // 50...1000-NSViewLayoutFeedbackLoopDebuggingThreshold 100 // 50...1000// Logs to com.apple.UIKit:LayoutLoop or com.apple.AppKit:LayoutLoop

UIView is used in iOS, and NSView is used in MacOS. Once we have this switch turned on, the layout feedback loop debugger will begin to record each message that calls Setneedslayout.

Here I set the threshold for it to be 100.

If found in a runloop, layout in a view above the number of calls above the threshold, here is set to 100, that is, the number of times more than 100, the cycle will be running a small segment, because this time to give debugger a record of information time. When the record is complete, an exception is thrown immediately. And the information is displayed in the logs. Log is recorded in Com.apple.UIKit:LayoutLoop (IOS)/com.apple.appkit:layoutloop (MacOS)

We can also hit the global exception breakpoint exception break point.
In the Debug window you can also use the LLDB command PO to debug some debugging information.


Let's take a look at 2 practical examples.

1.Upstream Geometry Change

There are so many view levels, such as.

Now the top 10 sub-view of the right substring is removed in a single level change.


Then the top 3 view will be affected. so the bounds of the 3 view changed. The setneedslayout is then implicitly invoked to obtain the new bounds information. (here after @kuailejim @ Wax gourd for the full stack melon and the Great God Experiment, setneedslayout is required by our developers to call manually, the system will not be bounds change when the implicit call Setneedslayout method). The bounds of the current view changes, but the parent view will continue to receive the Setneedslayout message if the parent view does not have layout complete. The message will be passed up until it reaches the topmost view, and after the top view layout is completed, the bounds of the associated view will be reset, calling the Layoutsubview () method. At this time, the cycle of death is produced.



These 3 view is the above 3 view, the following view needs setneedslayout, need to obtain the latest bounds information, the middle Blue view also needs setneedslayout, The upper view is then called the Setneedslayout () method, at which time the cycle of death is generated. There are 2 rings on top and bottom, and a common view is the middle Blue view. The view inside the loop is Setneedslayout () on each other's request, and resets the associated view's bounds after the layout is completed. This forms the triggers layout.

There was a great deal of curiosity about the 2 rings here, and there was a heated discussion of the situation where the rings would be produced. At present, it can be thought that the scene is like this: in the above 3 sub-tree, when a certain scenario, suddenly deleted the right subtree, assuming that the user's screen is now full screen, because suddenly deleted a bunch of view, then the original there will become blank, At this point the developer wants to tile the other view onto the screen. This time need to change the bounds of the parent view above, the bottom view will manually call the above Blue View,setneedslayout () method, and the blue view bounds set to full screen, due to the blue view of the bounds change, This time the developer code also manually called the Blue view of the parent view, to execute the Setneedslayout () method. The top view Code also writes bounds = Origrect, which triggers the layout of the blue view and updates the bounds. This creates a loop. In the same vein, a cycle is formed. This creates 2 cycles of death. These summaries need to thank @kuailejim @ Wax gourd To do the full stack melon give guidance.


Here is the log that we collected with the tool, the first line is top-level view, and the next is the process of recursion. Looking down, we'll see some numbers, which are the number of times the view is connected to layout, and these numbers are ordered. In a single dead loop, these numbers are the order of the loops. Of course, in a loop, each view can be a starting point or an end point. Here we set the top view as the starting point by default. This allows us to show how many of the view is involved in the dead loop.

From the log, there are 3 view, the following 10 view, add up is not equal to 23, this is why? Let's go down to log and see "views receiving layout in order".


Here we can clearly see that the view received the layout of the order, a total of exactly 23. You can also see that in a loop, a view receives layout more than once.


As indicated, there are 2 paragraphs in the loop, 10 view received layout, then 2 view, followed by 10 view, then 1 view.

Back to the initial use of this tool, we initially used this tool to see where the top-level view received the Setneedslayout message. Continue looking down and find the stack information that was called there.



From the top down, the first few lines are definitely uiviewlayoutfeedbackloopdebugging information. Look down, see the 6th line, you can see Dropshadowview received information, ready to setbounds. Looking back at the level information, we will find that Dropshadowview is Transitionview's child view.

The only way to cause Dropshadowview to trigger setbounds is that its parent view,transitionview triggers the Setneedslayout () method. Because this time Transitionview has not yet layout.



Back to "Geometry Change Records", this time we can see the selected 3 lines of information in the loop over and over again. Looking at lines 2nd and 3rd, we can see the layout from Transitionview. It is reasonable at this time. Looking at the first line, you will find that there is a Transitionview child view called Viewlayoutsubviews.

This time we are positioned to the root of the bug, as long as the way in layout, do not change the superview bounds that can be removed from the dead loop.

2.Ambiguous Layout from Constraints

When we set constraints constraints, we often produce some ambiguous constraints. Ambiguous constraints are usually not scary, we just need to make a little bit of tweaking and update all frame.

However, the following scenarios are exported to form a ring:

When your view is rotated, the constraints changes as well, and some of the constraint after the rotation of the view collide with each other. Because of the Times some constraint formed a ring.

The problem is that when you don't have this debugger tool, thinking is burning, without any clue, which is why log put top-level view on the first line, and give us hints about why we started looking for bugs here.



At log, we will see a lot of "ambiguous Layout". Note: Tamic is the abbreviation for translates Auto resizing Mask into constraints.

Let's take a look at the detailed log. Before we look at log, we should know that constraint although there is a lot of conflict, but there is only one constraint that can cause conflict, that is, when we correct one of the constraint, it is possible that all conflicts are resolved.



As shown in log, we have set up 2 conflicting constraint here in Minx, one 60 and one 120. We can check the constraints one after the other, but this list is very long, and it's more troublesome to check.

Let's draw a picture to analyze the problem.


, label has leading and trailing Padding,label is container child view,container is the child of action, action is the child view of representation. There is a center centering constraint between the container and the action view. Action View has a autoresizing mask constraints on representation view.

Then each representation view is alignment aligned. From this perspective, these view does not have enough constraints to allow these view to determine the location information. For example, on the x-axis, this string of view can exist in any position, so the ambiguous constraint is produced.

To resolve the ambiguity above

-UIViewLayoutFeedbackLoopDebuggingThreshold 100 // 50...1000-NSViewLayoutFeedbackLoopDebuggingThreshold 100 // 50...1000 //Logs to com.apple.UIKit:LayoutLoop or com.apple.AppKit:LayoutLoop

The above problems can be solved with debugger.

Summarize

This Xcode 8 gives us the AutoLayout fusion of the previous autoresizing masks usage, so that two are combined to use, so that different scenarios we can have more choices, more flexibility to deal with the layout of the problem. It also allows us to manually adjust the constraints warning priority level.

The problem with the layout of MacOS has brought us new controls Nsgridview

Finally brought us a new debug layout Feedback Loop Debugging tools, can let us debug more headaches in peacetime problems, there are tools can be based on, quickly locate problems, find problems.



Text/A wisp of the stream of hidden Half Frost (Jane book author)
Original link: http://www.jianshu.com/p/2521c610fac3
Copyright belongs to the author, please contact the author to obtain authorization, and Mark "book author".

New features of iOS Xcode8 Auto layout

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.