I can't find a clear answer on Apple documentation regarding Cocoa Autolayout about the difference between content hugging and compression resistance.
Can somebody explain their usages and difference ?
I can't find a clear answer on Apple documentation regarding Cocoa Autolayout about the difference between content hugging and compression resistance.
Can somebody explain their usages and difference ?
A quick summary of the concepts:
Example:
Say you've got a button like this:
[ Click Me ]
and you've pinned the edges to a larger superview with priority 500.
Then, if Hugging priority > 500 it'll look like this:
[Click Me]
If Hugging priority < 500 it'll look like this:
[ Click Me ]
If the superview now shrinks then, if the Compression Resistance priority > 500, it'll look like this
[Click Me]
Else if Compression Resistance priority < 500, it could look like this:
[Cli..]
If it doesn't work like this then you've probably got some other constraints going on that are messing up your good work!
E.g. you could have it pinned to the superview with priority 1000. Or you could have a width priority. If so, this can be helpful:
Editor > Size to Fit Content
Take a look at this video tutorial about Autolayout, they explain it carefully
source: @mokagio
Intrinsic Content Size - Pretty self-explanatory, but views with variable content are aware of how big their content is and describe their content's size through this property. Some obvious examples of views that have intrinsic content sizes are UIImageViews, UILabels, UIButtons.
Content Hugging Priority - The higher this priority is, the more a view resists growing larger than its intrinsic content size.
Content Compression Resistance Priority - The higher this priority is, the more a view resists shrinking smaller than its intrinsic content size.
Check here for more explanation: AUTO LAYOUT MAGIC: CONTENT SIZING PRIORITIES
Let's say you have a button with the text, "Click Me". What width should that button be?
First, you definitely don't want the button to be smaller than the text. Otherwise, the text would be clipped. This is the horizontal compression resistance priority.
Second, you don't want the button to be bigger than it needs to be. A button that looked like this, [ Click Me ], is obviously too big. You want the button to "hug" its contents without too much padding. This is the horizontal content hugging priority. For a button, it isn't as strong as the horizontal compression resistance priority.
If view.intrinsicContentSize.width != NSViewNoIntrinsicMetric
, then auto layout creates a special constraint of type NSContentSizeLayoutConstraint
. This constraint acts like two normal constraints:
view.width <= view.intrinsicContentSize.width
with the horizontal hugging priority, andview.width >= view.intrinsicContentSize.width
with the horizontal compression resistance priority.In Swift, with iOS 9's new layout anchors, you could set up equivalent constraints like this:
let horizontalHugging = view.widthAnchor.constraint(
lessThanOrEqualToConstant: view.intrinsicContentSize.width)
horizontalHugging.priority = view.contentHuggingPriority(for: .horizontal)
let horizontalCompression = view.widthAnchor.constraint(
greaterThanOrEqualToConstant: view.intrinsicContentSize.width)
horizontalCompression.priority = view.contentCompressionResistancePriority(for: .horizontal)
Similarly, if view.intrinsicContentSize.height != NSViewNoIntrinsicMetric
, then auto layout creates an NSContentSizeLayoutConstraint
that acts like two constraints on the view's height. In code, they would look like this:
let verticalHugging = view.heightAnchor.constraint(
lessThanOrEqualToConstant: view.intrinsicContentSize.height)
verticalHugging.priority = view.contentHuggingPriority(for: .vertical)
let verticalCompression = view.heightAnchor.constraint(
greaterThanOrEqualToConstant: view.intrinsicContentSize.height)
verticalCompression.priority = view.contentCompressionResistancePriority(for: .vertical)
You can see these special NSContentSizeLayoutConstraint
instances (if they exist) by printing view.constraints
after layout has run. Example:
label.constraints.forEach { print($0) }
// Output:
<NSContentSizeLayoutConstraint:0x7fd82982af90 H:[UILabel:0x7fd82980e5e0'Hello'(39)] Hug:250 CompressionResistance:750>
<NSContentSizeLayoutConstraint:0x7fd82982b4f0 V:[UILabel:0x7fd82980e5e0'Hello'(21)] Hug:250 CompressionResistance:750>
Content Hugging and Content Compression Resistence Priorities work for elements which can calculate their size intrinsically depending upon the contents which are coming in.
From Apple docs:
The Content hugging priority
is like a Rubber band that is placed around a view.
The higher the priority value, the stronger the rubber band and the more it wants to hug to its content size.
The priority value can be imagined like the "strength" of the rubber band
And the Content Compression Resistance
is, how much a view "resists" getting smaller
The View with higher resistance priority value is the one that will resist compression.
contentCompressionResistancePriority – The view with the lowest value gets truncated when there is not enough space to fit everything’s intrinsicContentSize
contentHuggingPriority – The view with the lowest value gets expanded beyond its intrinsicContentSize
when there is leftover space to fill
When we thinking about these two priorities, we need to consider the conditions.
When the space is enough for two or more widgets, we consider hugging priority
, the priority is less, it will take the surplus space, and other widgets only take the Intrinsic Content Size.
When the space is not enough for widgets, we consider the Content Compression Resistance
as well.
iOS AutoLayout - Intrinsic Content Size And Content Hugging & Content Compression Resistence Priority(CHCR)
Intrinsic Content Size
Intrinsic Content Size -> Constraint -> AutoLayout engine -> draw
AutoLayout
uses Intrinsic Content Size
to create implicit constraint(width and/or height)
Intrinsic Content Size - tells how much space(width and/or height) is needed to show a full content.
Not all views have an intrinsic content size
UIView and NSView - No intrinsic content size
UIView
object doesn't have it and UIView
class has intrinsicContentSize
variable which is/can be overrode by subclass. For example UILabel
has it's own realization which is based on text and font attributesUIView
subclass uses default realisation with UIView.noIntrinsicMetric (-1)pseudocode:
import UIKit
class UIView {
override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric)
}
}
when you need to recalculate intrinsicContentSize
and redraw it - call view.invalidateIntrinsicContentSize()
intrinsicContentSize
vs sizeToFit()
. intrinsicContentSize
is for AutoLayout, sizeToFit()
is for Frame-based.
view.sizeToFit()
view.sizeThatFits(_ size: CGSize)
. size usually is view.bounds.sizeContent Hugging & Content Compression Resistence Priority(CHCR)
Every view has a CHCR priority for each dimension(horizontal, vertical) but it is applied only view with intrinsicContentSize
. Also it has an impact on constraint priority
Content Hugging Priority(CHP)(default value is 251)
Prevent expanding. Higher priority - prevent view to be bigger then Intrinsic Content Size.
Content Compression Resistence Priority(CRP)(default value is 750)
Prevent collapsing. Higher priority - prevent view to be smaller then Intrinsic Content Size.
Experiments:
Two UILabels(Label 1, Label 2), which is layout one by one horizontally
Content Priority Ambiguity
Decrease horizontal hugging...
Content Priority Ambiguity
Decrease horizontal compression resistance...