Autolayout and NSScrollView

I hit an annoying auto-layout issue when working on a client project recently. I was attempting to upgrade the software from the 10.11 to the 10.12 SDK. We had a few compiler issues which were quickly resolved but when I ran the software the document view completely missing from its windows.

The view uses intrinsicContentSize to dynamically size itself inside an NSScrollView based on the document contents and zoom level. We also use a custom NSClipView subclass to centre the view when the scrollview is larger than it. This has worked fine for the last few years, however now my intrinsicContentSize override was not being called and the view was being positioned outside of the clipview bounds.

I’ve never felt completely comfortable with auto-layout, every time I think I’ve got my head round it I find another use case that I can’t solve. Anyway, after some googling I noticed people mentioning on StackOverflow that translatesAutoresizingMaskIntoConstraints should be set to NO. Various solutions were given usually involving removing the view, setting the flag and then re-adding it at runtime, or overriding the method to just return NO. I tried the latter an my issue was fixed.

There was a new problem. Now the following was output to the log.

[Layout] Detected missing constraints for <DocumentView: 0x6080001206e0>.  
It cannot be placed because there are not enough constraints to fully define the size and origin. 
Add the missing constraints, or set translatesAutoresizingMaskIntoConstraints=YES and constraints 
will be generated for you. If this view is laid out manually on macOS 10.12 and later, you may 
choose to not call [super layout] from your override. Set a breakpoint on 
DETECTED_MISSING_CONSTRAINTS to debug. This error will only be logged once.

Clearly something was wrong. Looking at the constraints at runtime showed that they were correctly set for the width and height based on the content size, but missing for the X, Y position. Removing the mask translation hack, I could see that constraints were then being applied incorrectly to lock the position and size at the state in the original xib file. That was the Aha! moment.

So, what are the correct constraints? Since we’re using intrinsicContentSize we need to have variable dimensions, and we need to provide a position, but the scrollview and clipview will need to change that. The solution in the end was annoyingly simple.

First I pinned my view to the top left of the superview to set my position constraints. That left warnings about missing constraints on the width and height. To fix these I went into the view’s Size inspector and set “Intrinsic Size” to “Placeholder”. At that point my warnings vanished.

When I ran the application everything worked properly and there was no more warning about missing constraints. I couldn’t find any easily reachable documentation on solving this problem, although I’m sure if I read the auto-layout documentation from start to end it would be buried in there somewhere.