Intrinsiccontentsize() - Method Does Not Override Any Method from Its Superclass

intrinsicContentSize() - Method does not override any method from its superclass

Please check the latest reference.
(You can easily find it just putting the word "intrinsicContentSize" in the searchbar of Apple's developer site.)

Declaration

var intrinsicContentSize: CGSize { get }

intrinsicContentSize has become a computed property, so you need to override it in this way:

override open var intrinsicContentSize: CGSize {
get {
//...
return someCGSize
}
}

Or simply:

override open var intrinsicContentSize: CGSize {
//...
return someCGSize
}

Method does not override any method from its superclass for Set NSObject

This method in Swift 3 has change to:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
}

Reference from Apple

Method intrinsicContentSize() conflicts with getter from superclass

This is a property, not a method, so in Swift it's overridden using the property syntax rather than a getter method as it would be in Objective-C:

override var intrinsicContentSize: CGSize {
return CGSize(width: 0.0, height: 55.0)
}

How to override intrinsictContentSize for a view with flexible height and fixed width?

The documentation suggests that overriding intrinsicContentSize may not be the best way to achieve what you want to achieve. As you've observed, from the UIView class reference:

Setting this property allows a custom view to communicate to the layout system what size it would like to be based on its content. This intrinsic size must be independent of the content frame, because there’s no way to dynamically communicate a changed width to the layout system based on a changed height, for example.

MyView's bounds are not set until after its intrinsicContentSize has been requested, and in an auto layout context are not guaranteed to have been set at any time before layoutSubviews() is called. There is a way to work around that and pass a width to your custom view for use in intrinsicContentSize, however, which I've included as the second option in this answer.

Option 1: Manual layout by overriding sizeThatFits(_:)

First, in the absence of auto layout, configure MyView and its label subview to size and resize themselves appropriately:

//  MyView.swift

func setupView() {
label = UILabel()
label.numberOfLines = 0
label.autoresizingMask = [.flexibleWidth] // New
addSubview(label)
}

override func layoutSubviews() {
super.layoutSubviews()
label.sizeToFit() // New
}

override func sizeThatFits(_ size: CGSize) -> CGSize {
return label.sizeThatFits(size) // New
}

MyView will now size itself according to the size of its label subview, and the label will be sized according to the width of its superview (because of its .flexibleWidth autoresizing mask) and its text content (because numberOfLines is set to 0). If MyView had other subviews, you would need to compute and return the total size in sizeThatFits(_:).

Secondly, we want UITableView to be able to compute the height of its cells and your custom subviews according to the manual layout above. When self-sizing cells, UITableView calls systemLayoutSizeFitting(_:withHorizontalFittingPriority:verticalFittingPriority) on each cell, which in turn calls sizeThatFits(_:) on the cell. (See WWDC 2014 Session 226.)

The target size or fitting size passed to those methods is the width of the table view with a zero height. The horizontal fitting priority is UILayoutPriorityRequired. You take control of the self-sizing cell process by overriding one of those methods in your custom cell subclass (the former for auto layout, the latter for manual layout) and using the width of the size passed in to compute and return the height of the cell.

In this case, the size of the cell is the size of myView:

//  MyCell.swift

override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
return myView.sizeThatFits(targetSize)
}

And you're done.

Option 2: Auto layout by overriding intrinsicContentSize

As the size of MyView depends on the size of its label, the first step is to ensure that the label is positioned and sized correctly. You've already done this:

//  MyView.swift

override func layoutSubviews() {
super.layoutSubviews()
label.frame = bounds
}

The second step is to define MyView's intrinsic content size. In this case, there is no intrinsic width (because that dimension will be determined entirely by the cell or other superview), and the intrinsic height is the intrinsic height of the label:

//  MyView.swift

override var intrinsicContentSize: CGSize {
return CGSize(width: UIViewNoIntrinsicMetric, height: label.intrinsicContentSize.height)
}

Because your label is a multiline label, its intrinsic height cannot be determined in the absence of a maximum width. By default, in the absence of a maximum width, UILabel will return an intrinsic content size appropriate for a single line label, which is not what we want.

The only documented way to provide a maximum width to a multiline label for the purpose of computing its intrinsic content size is the preferredMaxLayoutWidth property. The header file for UILabel provides the following comment for that property:

// If nonzero, this is used when determining -intrinsicContentSize for multiline labels

So the third step is to ensure preferredMaxLayoutWidth is set to an appropriate width. As the intrinsicContentSize property forms part of, and is relevant only to, the auto layout process, and your custom cell subclass is the only part of your code performing auto layout, the appropriate place to set layout preferences is in that class:

//  MyCell.swift

override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
myView.label.preferredMaxLayoutWidth = targetSize.width
myView.invalidateIntrinsicContentSize()

return super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority)
}

If MyView had more than one multiline label, or if you preferred to keep the subview hierarchy of MyView hidden from its superview, you could equally create your own preferredMaxLayoutWidth property on MyView and follow through.

The above code will ensure MyView computes and return an appropriate intrinsicContentSize based on the content of its multiline label, and that the size is invalidated on rotation.

How to set a custom view's intrinsic content size in Swift?

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

  • Swift 3 update: Easier Auto Layout: Coding Constraints in iOS 9
  • Just because you have the intrinsic content size set up in your custom view doesn't mean it will work as you expect. Read the documentation for how to use it, paying special attention to Content-Hugging and Compression-Resistance.
  • Thanks also to this Q&A for putting me on the right track: How can I add padding to the intrinsic content size of UILabel?
  • Thanks also to this article and the documentation for help with invalidateIntrinsicContentSize().


Related Topics



Leave a reply



Submit