How to change the width of an NSView in a transparent window

Viewed 99

(Swift, macOS, storyboard)

I have an NSView in a transparent window

I have this in the viewDidLoad. To make the window transparent and the NSView blue:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.2){
    self.view.window?.isOpaque = false
    self.view.window?.backgroundColor = NSColor.clear
}
view1.wantsLayer = true
view1.layer?.backgroundColor = NSColor.green.cgColor

enter image description here

I want to change the width with code when I click a button. If it has constraints:

@IBAction func button1(_ sender: NSButton) {
    view1Width.constant = 74
}

I tried without constraints and different ways to change the width. They all give the same results:

view1.frame = NSRect(x:50, y:120, width:74, height:100)

enter image description here

But there is still a border and a shadow where the old shape was. Why does it happen and how to solve it?

It only happens in specific circumstances:

  • If the window is transparent (and macOS)
  • I change the width and do not change the position y
  • The window must be active. If it is not (If I click to anywhere else) it looks as it should: the shadow around the changed NSView green.

(I have simplified the case to try to find a solution. I have created a new document and there is only this code and I am sure there is no other element)

1 Answers

Since the window is transparent you need to invalidate the shadows.

Apple states about invalidateShadow()

Invalidates the window shadow so that it is recomputed based on the current window shape.

Complete Self-Contained Test Program

It sets up the UI pogrammatically instead of using a storyboard. Other than that, the code is very close to your example.

Note the line:

view.window?.invalidateShadow()

in the onChange method.

import Cocoa

class ViewController: NSViewController {
    private let view1 = NSView()
    private let changeButton = NSButton()
    private var view1Width: NSLayoutConstraint?
        
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2){
            self.view.window?.isOpaque = false
            self.view.window?.backgroundColor = NSColor.clear
        }
        view1.wantsLayer = true
        view1.layer?.backgroundColor = NSColor.green.cgColor
    }
    
    @objc private func onChange() {
        view1Width?.constant += 32
        view.window?.invalidateShadow()
    }
    
    private func setupUI() {
        changeButton.title = "change"
        changeButton.bezelStyle = .rounded
        changeButton.setButtonType(.momentaryPushIn)
        changeButton.target = self
        changeButton.action = #selector(onChange)
        
        self.view.addSubview(view1)
        self.view.addSubview(changeButton)
        
        self.view1.translatesAutoresizingMaskIntoConstraints = false
        self.changeButton.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            view1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            view1.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            view1.heightAnchor.constraint(equalToConstant: 128),
            
            changeButton.topAnchor.constraint(equalTo: view1.bottomAnchor, constant:16),
            changeButton.centerXAnchor.constraint(equalTo: view1.centerXAnchor)
        ])
        
        view1Width = view1.widthAnchor.constraint(equalToConstant: 128)
        view1Width?.isActive = true
    }

}

Result

The desired result with an update of the shadows is accomplished: shadow demo

Related