Search code examples
objective-cswiftcocoaconstraintsnsprogressindicator

Pinning a NSProgressIndicator to the bottom


Simple question, but somehow I cannot get it to work. So I have a NSProgressIndicator that I want to pin to the bottom of the window, however when I add the bottom constraints for the NSProgressIndicator, the indicator does not get pinned to the bottom rather the distance between the indicator and the bottom slowly increases as I increase the size of the window.

Here's a couple images for reference:

Picture of a fresh project with nothing but a progress indicator and a constraint in its mainmenu.xib

When the view is small the constraint is held

But why does the constraint change as I increase the size of the window?

I have tried the same thing with a button and a text field and it works for the other two control views, it only does not work with the NSProgressIndicator.

Any ideas why this is happening and any solutions (even workarounds would be fine) would be great!


Solution

  • If you don't properly constrain a view, Auto Layout will add its own constraints to ensure it is properly constrained. In your example you can see what Auto Layout has added by logging the constraints belonging to the window's contentView (the progress indicator's superview):

    var contentView = window.contentView as! NSView
    for constraint in contentView.constraints {
        println(constraint)
    }
    
    /*
    <NSLayoutConstraint:0x6... V:[NSProgressIndicator:0x6...]-(20)-|   (Names: '|':NSView:0x6... )>
    <NSLayoutConstraint:0x6... H:|-(20)-[NSProgressIndicator:0x6...]   (Names: '|':NSView:0x6... )>
    <NSIBPrototypingLayoutConstraint:0x6... 'IB auto generated at build time for view with ambiguity' H:|-(20@251)-[NSProgressIndicator:0x6...](LTR) priority:251   (Names: '|':NSView:0x6... )>
    <NSIBPrototypingLayoutConstraint:0x6... 'IB auto generated at build time for view with ambiguity' V:|-(322@251)-[NSProgressIndicator:0x6...] priority:251   (Names: '|':NSView:0x6... )>
    <NSIBPrototypingLayoutConstraint:0x6... 'IB auto generated at build time for view with ambiguity' H:[NSProgressIndicator:0x6...(96@251)] priority:251>
    */
    

    Note the constraints whose identifier includes the phrase IB auto generated at build time for view with ambiguity - these are constraints Auto Layout has had to create to prevent an ambiguous layout. In particular, note the second of these constraints:

    V:|-(322@251)-[NSProgressIndicator:0x6...] priority:251
    

    This is saying, where possible, ensure the top edge of the progress indicator is always 322 pixels from the top edge of its superview (the exact value in your program will be different - it'll be the distance between the two edges as they appear in your xib). When you vertically enlarge your view, Auto Layout will try to find a way to honor both constraints, and it can do that by adjusting the height of the progress indicator. This is hard to see because the progress indicator has no border, but in your last screenshot the height of the indicator is about 400 pixels, whereas the distance of its top and bottom edges to the corresponding superview edges is constant.

    To get the behavior you're after you need to prevent Auto Layout from using the constraints it adds in the case of an ambiguous layout. If you give your progress indicator a height constraint, Auto Layout won't be able to satisfy both vertical constraints, so it'll just satisfy the one with the higher priority - that's your constraint - the one pinning the bottom edges (1000 vs 251). (Of course you should also fully constrain its horizontal position.)