bitmusher
bitmusher

Reputation: 887

How do you make Interface Builder respect a custom view's intrinsic content size in constraint based layout?

Interface Builder in XCode 4.5 respects the intrinsicContentSize for some views, e.g. NSButton, but I can't convince it to respect it on my own custom subviews. This causes IB to add extra constraints trying to force the layout drawn in IB, which then causes the intrinsic sizes to not be used when the program is run.

For example, consider a button centered in a window, and a custom view centered in a window…

IB constraints for centered NSButton

IB constraints for centered NSView

You can see that the custom view gets four constraints, presumably because IB doesn't know the view's intrinsicContentSize. You can change which extra constraints are added, e.g. you can force it to be width and height instead, but you can't delete them.

I'm coping with this now by searching and deleting the extra constraints in my awakeFromNib, but there must be a better way to do this.

Upvotes: 26

Views: 11246

Answers (3)

Fattie
Fattie

Reputation: 12598

How to actually do this, 2019

import UIKit

@IBDesignable class TagPerson: ShadowRoundedImageView {

    override var intrinsicContentSize: CGSize {
        var s = super.intrinsicContentSize
        s.height = 40
        s.width = 40
        return s
    }

    override func prepareForInterfaceBuilder() {
        invalidateIntrinsicContentSize()
    }

}

However, there is a problem. Xcode is buggy. You can sometimes reset it by:

The above will of course work flawlessly at runtime. But it randomly fails to work in interface builder (even with 11+).

To make it cycle, try

  1. The usual 'Refresh all views'

  2. Attach and delete a pointless constraint to one of your intrinsic size views. (I've noticed if you have a number of them, doing this to one is usually enough to make Xcode cycle, then they all work.)

  3. And finally:

Xcode has an "intrinsic size placeholder" feature.

enter image description here

Select one or more of your intrinsic-size elements. Toggle the bizarre placeholder thing back and fore a few times. Often that makes it cycle and the view will then work correctly.

At worst, restarting Xcode with the usual clean-everything will, sometimes, get it working.

Upvotes: 15

ravron
ravron

Reputation: 11211

Set a placeholder intrinsic content size — a "guess," if you will — in Interface Builder.

  1. Select your custom view.
  2. Show the size inspector (Shift5).
  3. Change the "Intrinsic Size" drop-down from "Default (System Defined)" to "Placeholder."
  4. Enter reasonable guesses at your view's runtime width and height.

These constraints are removed at compile-time, meaning they will have no effect on your running app, and the layout engine will add constraints as appropriate at runtime to respect your view's intrinsicContentSize.

Upvotes: 35

Mariam K.
Mariam K.

Reputation: 620

Ok, the point here is to make Xcode use the intrinsicContentSize of your custom view in IB.

This can be achieved by adding a placeholder view inside your custom view in IB with a fixed width and height (you can center it horizontally and vertically as well)

enter image description here

Then select your custom view and tap "Size To Fit Content" form the Edit Menu in IB. At this point all extra size defining constraints will be deletable leaving only positioning ones.

enter image description here

That way IB will size your custom view to fit the placeholder view and and Autolayout would depend on your view's override of - (CGSize)intrinsicContentSize in run time to determine your custom view's size.

Last step is to delete the placeholder view to allow your view to display its content and size correctly:

   - (void)viewDidLoad
    {
        [super viewDidLoad];
        [_placeholderView removeFromSuperview];
    }

I know this is a hack but hopefully it helps you.

Upvotes: 2

Related Questions