Presentation single with code

(swift, macOS, storyboards)

I open a window with code. Every time the user clicks the button the same window opens again and again. How to avoid that?

This opens well the window, but multiple times:

    @IBAction func obrirAmbCodi(_ sender: NSButton) {

        let storyboard:NSStoryboard = NSStoryboard(name: "Main", bundle: nil)

        guard let controller:NSWindowController = storyboard.instantiateController(withIdentifier: "finestra3") as? NSWindowController else { return }

            controller.showWindow(self)

    }

If I open the window with a segue (storyboard control drag) I can accomplish that by selecting the window controller > Attributes > Presentation > Single. But it seems that it does not work if I open the window with code

Replies

Every time the user clicks the button the same window opens again and again. How to avoid that?

It is not clear what you want to do. But I guess you just want to activate the window if it is already opened.

Then, you should not instantiate a new instance at each time:

    private var controller: NSWindowController? = {
        let storyboard:NSStoryboard = NSStoryboard(name: "Main", bundle: nil)
        return storyboard.instantiateController(withIdentifier: "finestra3") as? NSWindowController
    }()
    
    @IBAction func obrirAmbCodi(_ sender: NSButton) {
        controller?.showWindow(self)
    }
  • It works very well. Thank you.

    I want to learn. I have been google this and there are some things I do not understand:

    Why the () at the end?Why do you make the var private?
  • the () is at the end of the closure, to tell the closure to execute and return result, which initialise the var. If you write only var controller: NSWindowController? = { … } the var is a closure, not NSWindowController. With () at the end, closure executes and returns the expected NSWindowController.

  • @narcisfromgirona Why do you make the var private? The keyword private is an access modifier which limits the access to the property (or method) only within the same class. It is a good habit not to show some members required only for internal processing for abstraction and may help Swift compiler optimize better.

Add a Comment

Could you explain better what you get and what you want ?

I try to formulate

  • You click button, calling obrirAmbCodi, which opens a window finestra3
  • when you click again on that button, a new, similar window opens (probably stacked over previous one ?)
  • in such a case, you would want just to display the already opened window, not create a new one.

Is that correct ?

If so, you could:

  • In the class where obrirAmbCodi is, declare a var

var controller: NSWindowController?

  • test if nil ; if so instantiate
    @IBAction func obrirAmbCodi(_ sender: NSButton) {
        if controller == nil {
            let storyboard:NSStoryboard = NSStoryboard(name: "Main", bundle: nil)
            controller = storyboard.instantiateController(withIdentifier: "finestra3") as? NSWindowController  // could be nil
            controller?.showWindow(self)
        }
    }
  • dismiss the controller when you close window and set to nil

Note: you could test if the window is open instead of testing controller

  • Thank you for reformulating my question more clearly. Your assumptions are right.

    Your solution works well. I have some doubts about the var controller. I understand what you say that I must put the var to nil when I close the window. But if the close button is in another view controller, that is in another class, should I make var controller global in the first place?. But if I understand global vars are not recommended? I am not sure if I explain well my problem.

  • I did not think of this case. So, just get          controller?.showWindow(self)     out of the if controller == nil { } ; then no need to reset to nil.

  • Clever solution!

Add a Comment