53

Background

I am making a vertical label to use with traditional Mongolian script. Before I was just rotating a UILabel but there were some performance issues and other complications with this. Now I am working on making a label from scratch. However, I need the vertical label to tell auto layout when its height adjusts (based on string length).

What I have read

I read the Intrinsic Content Size and Views with Intrinsic Content Size documentation. These were more about how to use it, though, and not how to define it in a custom view.

Searching for "ios intrinsic content size for a custom view" only gives me

in Stack Overflow. This particular question didn't even need intrinsic content size because their view was just an assembly of standard views.

What I am trying

What I am trying is my answer below. I am adding this Q&A pair so that it won't take other people as long to find the answer as it took me with the search keywords that I used.

2 Answers 2

102

Setting the intrinsic content size of a custom view lets auto layout know how big that view would like to be. In order to set it, you need to override intrinsicContentSize.

override var intrinsicContentSize: CGSize { return CGSize(width: x, height: y) } 

Then call

invalidateIntrinsicContentSize() 

Whenever your custom view's intrinsic content size changes and the frame should be updated.

Notes

Sign up to request clarification or add additional context in comments.

1 Comment

For me adding @IBDesignable was needed to get Xcode to respect the content size.
17

Example of a "view with intrinsic height" ...

@IBDesignable class HView: UIView { @IBInspectable var height: CGFloat = 100.0 override var intrinsicContentSize: CGSize { return CGSize(width: 99, height: height) // if using in, say, a vertical stack view, the width is ignored } override func prepareForInterfaceBuilder() { invalidateIntrinsicContentSize() } } 

enter image description here

which you can set as an inspectable

enter image description here

Since it has an intrinsic height, it can (for example) be immediately inserted in a stack view in code:

stack?.insertArrangedSubview(HView(), at: 3) 

In contrast, if it was a normal view with no intrinsic height, you'd have to add a height anchor or it would crash:

let v:UIView = HView() v.heightAnchor.constraint(equalToConstant: 100).isActive = true stack?.insertArrangedSubview(v, at: 3) 

Note that in ...

the important special case of a stack view:

  • you set only ONE anchor (for vertical stack view, the height; for horizontal the width)

so, setting the intrinsic height works perfectly, since:

  • the intrinsic height indeed means that the height anchor specifically will be set automatically if needed.

Remembering that in all normal cases of a subview, many other anchors are needed.

2 Comments

I'd recommend returning super.intrinsicContentSize.width for the width in the override rather than returning a constant like you suggest. This way when the view is used outside of a stack view and has a width constraint that constraint is respected.
hi @VojtaBöhm - sure thing, but the pedagogy here is to explain that, in fact, the width value is ignored when used in a layout case where the width value is ignored (stack view in the example)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.