MassMover
MassMover

Reputation: 557

NSWindow - scale all content when resizing

I cannot figure out how to accomplish what seems to be a very simple task. I have a macOS cocoa application which consists of one window with a custom view inside. The view is 600x500 points. Inside this view is a subwiew that spans the whole width of the main view, but only a small fraction of the view's heigth. Its vertical origin is at about y=200.

The subview draws a rect starting 20 points off the left and right border, about 30 points in heigth. It also uses its layer with a CAEmitter layer as sublayer. This emitter's position can be controlled by the user, so its position will be updated by a function called from outside the view.

What I want:

When the user resizes the window, it should only scale proportionally (I managed to to that), so that the window's content as a whole gets resized. No subviews should get rearranged in any way, they all should keep their size and positions relative to each other, just getting bigger/smaller.

I do not get this to work.

What I have tried so far:

There are so many entry points in the cocoa framework regarding sizes, anchors, constraints etc., I simply do not know where to start, so any help would be appreciated.

UPDATE: I added code stripped down to the min:

class MyViewController: NSViewController {

    public init () {
        super.init(nibName: nil, bundle: nil)
        self.view = MyView(frame: NSRect(x: 0, y: 0, width: 600, height: 500))
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class MyView: NSView {

    override init(frame frameRect: NSRect) {
    
        super.init(frame: frameRect)
    
        let subView = MySubView(frame: NSRect(x: 0, y: frame.height/3, width: frame.width, height: frame.height/10))
        self.addSubview(subView)
        subView.autoresizingMask = [.width, .height]
        subView.translatesAutoresizingMaskIntoConstraints = true
        subView.trailingAnchor.constraint(greaterThanOrEqualTo: self.trailingAnchor).isActive = true
        subView.leadingAnchor.constraint(greaterThanOrEqualTo: self.leadingAnchor).isActive = true
    }

    required init?(coder decoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}


class MySubView: NSView {

    override func draw(_ dirtyRect: NSRect) {
    
        let context = NSGraphicsContext.current!.cgContext
        context.setStrokeColor(.black)
        context.setFillColor(CGColor.init(red: 0.7, green: 0.7, blue: 0.7, alpha: 1))
        context.setLineWidth(2)
    
        let borderRect = CGRect(x: constant.sideMargin, y: 5, width: frame.width-2*constant.sideMargin, height: frame.height-10)
        let roundedRect = CGPath.init(roundedRect: borderRect, cornerWidth: 5, cornerHeight: 5, transform: nil)
    
        context.addPath(roundedRect)
        context.fillPath()
        context.strokePath()
    
        context.addPath(roundedRect)
        context.strokePath()
    } 
}

Upvotes: 0

Views: 1052

Answers (1)

Willeke
Willeke

Reputation: 15589

As explained in the View Programming Guide, NSView will scale and offset its contents if its bounds is different from its frame. Just set bounds to {600, 500} when frame changes. The subviews of the view don't need constraints because their coordinates don't change.

MyView setup code:

// frame changed notitication
self.postsFrameChangedNotifications = true
self.frameChangeObserver = NotificationCenter.default.addObserver(forName: NSView.frameDidChangeNotification,
    object: self, queue: OperationQueue.main) { (note) in
    self.setBoundsSize(NSSize(width:600, height: 500))
}

let subView = MySubView(frame: NSRect(x: 0, y: bounds.height/3, width: bounds.width, height: bounds.height/10))
// no constraints
subView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(subView)

Upvotes: 1

Related Questions