Simo Virokannas

Writings and ramblings

Adding resizing and resizable subviews to NSView

If you’re adding a subview programmatically, you may run into having to constantly set all the resizing behaviors and/or anchors manually when all you really want to do is fill the whole available area based on two options: “stretch me to fit this box” or “stretch this box so I can fit”.

This happens especially often if you’re in the middle of a project that’s slowly being migrated over to SwiftUI.

Here’s a little NSView extension that may help:

import Cocoa
import SwiftUI

enum ViewAnchor {
    case top
    case left
    case bottom
    case right
    static let all: [ViewAnchor] = [.top, .left, .bottom, .right]
}

extension NSView {
    func constraintEdge(_ name: ViewAnchor, to: NSView) {
        switch(name) {
        case .top: self.topAnchor.constraint(equalTo: to.topAnchor).isActive = true
        case .left: self.leftAnchor.constraint(equalTo: to.leftAnchor).isActive = true
        case .bottom: self.bottomAnchor.constraint(equalTo: to.bottomAnchor).isActive = true
        case .right: self.rightAnchor.constraint(equalTo: to.rightAnchor).isActive = true
        }
    }
    
    public func addResizableSubview(_ other: NSView) {
        other.autoresizingMask = [.width, .height]
        self.autoresizesSubviews = true
        other.frame = NSRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
        self.addSubview(other)
    }

    public func addResizingSubview(_ other: NSView) {
        addSubview(other)
        other.translatesAutoresizingMaskIntoConstraints = false
        for anchor in ViewAnchor.all {
            self.constraintEdge(anchor, to: other)
        }
    }
}

class ViewController: NSViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let host = NSHostingController(rootView: YourSwiftUIView())
        self.view.addResizableSubview(host.view)
    }
}

Using this extension, you have two options to add the subview:
addResizingSubview (this will stretch the parent view to satisfy the size constraints of the subview)
addResizableSubview (this will make your subview respect the main view size)

As an added bonus, there’s a couple of little helpers for gluing view edges, using an enum for the edges instead of calling four separate constraints.

Hope this is as useful to others as it has been to me.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.