Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using AutoLayout to stack within two Columns of varying Heights

Targetting iOS 8.1

I am using AutoLayout to lay out a number of Labels in a TableCell. Some of those Labels are optional and some can wrap their Text. They are split across two "Columns", these columns are simply two UIViews in the TableCell's ContentView. My constraints are applied programatically.

SECOND UPDATE

Without SwiftArchitect's answer below I would not have solved this and have accepted his answer. However because mine is all in code, in a custom tablecell, I have also added a separate answer below

UPDATE

In an attempt to stop the labels from stretching to a size larger than they needed to be I had previously set the SetContentHuggingPriority and SetContentCompressionResistancePriority to 1000 as I belived this was the equivalent of saying "I want the Label to hug its content to its exact height and I do not want it to ever be compressed vertically" This request was clearly not being complied with by AutoLayout as you can see in the Red and Pink examples below.

this.notesLabel.SetContentHuggingPriority(1000, UILayoutConstraintAxis.Vertical);
this.notesLabel.SetContentCompressionResistancePriority(1000, UILayoutConstraintAxis.Vertical);

I removed the setting of these priorities and the labels are no longer being squashed which was my original issue. Of course now certain labels are stretched beyond the height they need to be.

  1. Why does removing the Hugging and Compression priorities fix my issue?
  2. How can I get the text in the red box (red box not part of the cell added later) to not expand without going back to my previous issue?

enter image description here

Here are a couple of screenshots of what it did look like when the Compression and Hugging priorities where set. The background colours are for debugging

enter image description here

The general problem was that the Containing View's (colored purple and red) were sizing themselves to the smaller of the two. As you can see in the top one "Priority 3" is being cut because the left column container doesn't need to be any higher.

In this next example there is no Priority label but the EventDate is being squashed.

enter image description here

like image 548
Pat Long - Munkii Yebee Avatar asked Jul 27 '15 16:07

Pat Long - Munkii Yebee


People also ask

How do I change the stack view height?

'Fill proportionally' distribution type works with intrinsic content size. So if our vertical stack(height say 600) view has 2 views, ViewA (intrinsic content height 200) and ViewB(intrinsic content height 100), the stack view will size them to ViewA(height 400) and ViewB(height 200).

What are two properties that auto layout constraints control on a Uiview?

Auto Layout defines margins for each view. These margins describe the preferred spacing between the edge of the view and its subviews. You can access the view's margins using either the layoutMargins or layoutMarginsGuide property. The layoutMargins property lets you get and set the margins as a UIEdgeInsets structure.

How does Uistackview layout elements in a stack?

The stack view aligns the first and last arranged view with its edges along the stack's axis. In a horizontal stack, this means the first arranged view's leading edge is pinned to the stack's leading edge, and the last arranged view's trailing edge is pinned to the stack's trailing edge.


1 Answers

The following answer has been written and tested. It works properly on iPhone & iPad, portrait & landscape. The tallest column wins, and the other one just takes the space it needs. It can even be modified to vertically center objects if need be. It addresses the vertically clipped label concerns, as well as dynamic scaling.

enter image description here

Preliminary advice

  • Use a Storyboard if you can. You can test all your constraints visually with a state-of-the-art GUI.
  • Do not tinker with hugging, compression, or even UILabel height: let each label take the space it needs vertically, and only add top and side anchors
  • Use extra views as containers to define the width of each column. Use multiplier to get, say 2 thirds and 1 third.
  • Let these views calculate their ideal height by adding a single height constraint to the bottom of the lowest label (leftColumn.bottom Equal lowestLeftLabel.bottom)
  • Do not add or remove views dynamically ; rather, hide them so that they preserve the associated constraints.

Solution Description

For simplicity, I have created 2 subviews, 1 for each column, and positioned them side by side. They are anchored on top/left and top/right, their width is calculated, and their height is derived from their respective content(*).

The left and right subviews have a 1/2 multiplier, to which I added a constant of 2 pixels for margin. The labels inside these two columns are anchored left and right (leading space to container and trailing space to container), with an 8 pixels margin. This ensure no label ever bleeds beyond its column.

  1. Consider that the height of your UITableViewCell is the largest of the 2 inner columns. In other words, containerView.height >= left.height and containerView.height >= right.height.
  2. Ensure you are not deleting any of the invisible labels. view.hidden will not disrupt your constraints, and that is what you want.
  3. Anchor each UILabel left and right to container, the uppermost to the container as well, but every subsequent label.top should be anchored to the .bottom of one above it. This way, your content will flow. You can add margins if you want.

(*) The final key is to tie the height of each column with a constraint on the column to equal the .bottom of the lowest label for that column. In the example above, you can clearly see that the right column, with a blue background, is shorter than the left one.

enter image description here

While I see you wanted a solution in code, I created my example using a Storyboard in less than 15 minutes. It is not merely a concept, it is an actual implementation. It has exactly 0 lines of code, and works on all iOS devices. Incidentally, it has also 0 bugs.

List of All Constraints

Notice the >= sprinkled here and there. They are the key to making your columns independently tall.

The NSLayoutContraint are virtually identical for L and R.

enter image description here

enter image description here

Get the Storyboard here, and a detailed article there.

like image 78
SwiftArchitect Avatar answered Oct 13 '22 15:10

SwiftArchitect