swiftcocoaxibnslayoutconstraintappkit

NSLayoutConstraint for setting Min and Max view width


What I want to achieve in AppKit (not in SwiftUI): [GIF] (example in SwiftUI)

I had several attempts to implement this behavior in AppKit. I've been trying to do this for a few days now, but it doesn't work. [PIC] [GIF]

I tried to set the low priority on Leading / Trailing constraint.
This partially fixed the situation. I was able to change the size of the window normally, but the window size was not limited to the minimum size of NSTextField. [GIF]


Solution

  • The important thing to notice here is that you only want the low priority constraints to be one way. That is, you don't want something like this:

    // throughout the answer, I use "tf" for the text field, and "view" for its superview
    let weakLeadingConstraint = tf.leadingAnchor.constraint(equalTo: view.leadingAnchor)
    let weakTrailingConstraint = tf.trailingAnchor.constraint(equalTo: view.trailingAnchor)
    weakLeadingConstraint.priority = .dragThatCannotResizeWindow
    weakTrailingConstraint.priority = .dragThatCannotResizeWindow
    

    Because these constraints would break when the window is resized, allowing the window to be resizable to any width where the leading and trailing anchors are "not equal" to those of the text field.

    Instead, the low priority constraints should be >= or <= constraints. Think of the 2 equality constraints as the following 4 inequality constraints:

    It is the first 2 that you want to break, leaving the last 2 (which says that the text field should always be within the window) in tact, when you resize the window.

    The other constraints are quite straightforward, so I'll just present the whole code here:

    tf.translatesAutoresizingMaskIntoConstraints = false
    
    let weakLeadingConstraint = tf.leadingAnchor.constraint(lessThanOrEqualTo: view.leadingAnchor)
    let weakTrailingConstraint = tf.trailingAnchor.constraint(greaterThanOrEqualTo: view.trailingAnchor)
    weakLeadingConstraint.priority = .dragThatCannotResizeWindow
    weakTrailingConstraint.priority = .dragThatCannotResizeWindow
    
    NSLayoutConstraint.activate([
        tf.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        tf.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        tf.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor),
        tf.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor),
        weakLeadingConstraint,
        weakTrailingConstraint,
        tf.widthAnchor.constraint(greaterThanOrEqualToConstant: 200),
        tf.widthAnchor.constraint(lessThanOrEqualToConstant: 400),
    ])