Home > Mobile >  NSWindow - scale all content when resizing
NSWindow - scale all content when resizing

Time:11-27

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:

  • With no additional properties set I can resize the window, but the subiew does not scale at all, it sticks to its position in the superview.
  • I set subView.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor).isActive = true and the same for the trailingAnchor, additionally I set subView.autoresizingMask = [.width] Now the width of the rect drawn in the subview gets resized, but the rect's edges stay away 20 points from the border. I want this distance to be scaled as well.
  • The rect's heigth stays fixed. When I add -heigth to the subview's autoresizing mask the heigth gets distorted in a riculously disproportianal way (the distance to top and bottom margins are kept constant, so although the subview's heigth is only about 30 at start the rect gets blown up on resizing.
  • The emitter does not get repositioned. Its horizontal position gets calculated on user interaction and is relative to the view's borders, after resizing it sticks to its absloute position. I solved that by calling the calculating function from the draw() method, but that does not seem right to me

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()
    } 
}

CodePudding user response:

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)
  • Related