I would like to create a custom UIView
which uses/offers a IntrinsicContentSize, since its height depends on its content (just like a label where the height depends on the text).
While I found a lot of information on how to work with IntrinsicContentSize offered by existing Views, I found just a few bits on how to use IntrinsicContentSize on a custom UIView
:
@IBDesignable class MyIntrinsicView: UIView {
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(UIColor.gray.cgColor)
context?.fill(CGRect(x: 0, y: 0, width: frame.width, height: 25))
height = 300
invalidateIntrinsicContentSize()
}
@IBInspectable var height: CGFloat = 50
override var intrinsicContentSize: CGSize {
return CGSize(width: super.intrinsicContentSize.width, height: height)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
invalidateIntrinsicContentSize()
}
}
The initial high is set to
50
The
draw
methods draws a gray rect with a size25
height is changed to
300
andinvalidateIntrinsicContentSize()
is calledPlacing a
MyView
instance in InterfaceBuilder works without any problem. The view does not need a height constraint
However, in IB the initial height of 50
is used. Why? IB draws the gray rect, thus the draw
method is called. So why is the height not changed to 300?
What is also strange: When setting a background color it is drawn as well, also super.draw(...)
is not called. Is this intended?
I would expect a view with height of 300
and a gray rect at the top with a height of 25
. However, when running the project in simulator the result is different:
- height of the view =
300
- OK - height of content (= gray rect) = 150 (half view height) - NOT OK
It seems that the content was stretched from its original height of 25
to keep its relative height to the view. Why is this?
Trying to change the view's height from inside
draw()
is probably a really bad idea.First, as you've seen, changing the intrinsic content size does not trigger a redraw. Second, if it did, your code would go into an infinite recursion loop.
Take a look at this edit to your class:
Now, when you change the intrinsic height in IB via the
IBDesignable
property, it will update in your Storyboard properly.Here's a quick look at using it at run-time. Each tap (anywhere) will increase the
height
property by 50 (until we get over 300, when it will be reset to 50), which then invalidates the intrinsic content size and forces a call todraw()
: