Home > Blockchain >  Closing a sheet programmatically
Closing a sheet programmatically

Time:04-25

I have a couple of processes that can take several seconds to complete and I wanted to have a sheet open in my main application window displaying an indeterminate ProgressWheel and a message saying that the process is underway. I want the sheet to close when the process has completed.

I was trying to implement that by performing a segue to the sheet and sending it two string values in my prepare(for segue) – a waitingMessage, so the sheet can display the name of the process that is underway, and a waitingState... that would be either "starting" or "ending". When my sheet ViewController receives a "starting" state it would just display the ProgressWheel and message, and when it receive the "ending" state it would dismiss itself.

Here's what I'm trying but the sheet won't dismiss, even though it is receiving the "ending" message – its just displays and the entire application won't continue.

import Cocoa

class WaitingViewController: NSViewController {
    @IBOutlet weak var waitingProgressWheel: NSProgressIndicator!
    @IBOutlet weak var waitingMessageLabel: NSTextField!
    
    var waitingMessage : String = ""
    var waitingState : String = ""

    
    override var representedObject: Any? {
        didSet {
            let incomming = representedObject as! (String, String)
            waitingMessage = incomming.0
            waitingState = incomming.1
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        waitingProgressWheel.startAnimation(self)
        waitingMessageLabel.stringValue = waitingMessage
        if waitingState == "ending" {
            waitingProgressWheel.stopAnimation(self)
            waitingMessageLabel.stringValue = "Done."
            dismiss(self)
        }
    }
}

Here's the relevant part of my prepare(for segue)

    override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
        if segue.identifier == "waiting" {
            var waitingInfo : (String, String) = ("","")
            waitingInfo.0 = waitingSegueMessage
            waitingInfo.1 = waitingSegueState
            (segue.destinationController as! WaitingViewController).representedObject = waitingInfo
        }
    }

Here is my updated sheet view controller:

import Cocoa

class WaitingViewController: NSViewController {
    @IBOutlet weak var waitingProgressWheel: NSProgressIndicator!
    @IBOutlet weak var waitingMessageLabel: NSTextField!
    
    var processWaitingMessage : String = ""
    var processWaitingState : ViewController.waitingState = .beginning
    var isDone : Bool? {
        didSet {
            if isDone! {
                dismiss(self)
            }
        }
    }
    
    override var representedObject: Any? {
        didSet {
            let incomming = representedObject as! (String, ViewController.waitingState)
            processWaitingMessage = incomming.0
            processWaitingState = incomming.1
            if processWaitingState == .ending {
                isDone = true
            }
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        waitingProgressWheel.startAnimation(self)
        waitingMessageLabel.stringValue = processWaitingMessage
        if processWaitingState == .ending {
            waitingProgressWheel.stopAnimation(self)
            waitingMessageLabel.stringValue = "Done."
        }
    }
}

CodePudding user response:

Your approach is wrong.

You have to update the state from the presenting view controller. To be able to do that you need a reference.

In ViewController add a property

weak var waitingSheet : WaitingViewController?

in WaitingViewController add a function

func updateState(_ state:  ViewController.waitingState) {
    switch state {
        case .beginning: 
           waitingProgressWheel.startAnimation(self)
           waitingMessageLabel.stringValue = "Beginning"

        case .ending: 
           waitingProgressWheel.stopAnimation(self)
           waitingMessageLabel.stringValue = "Ending"

        case .isDone:
           dismiss(self)
    } 
}

In viewDidLoad update the state to beginning

override func viewDidLoad() {
    super.viewDidLoad()
    updateState(.beginning)
}

Back in ViewController assign the reference to the sheet to waitingSheet in prepareForSegue (the predefined representedObject is not needed)

override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
    if segue.identifier == "waiting" {
        waitingSheet = segue.destinationController as? WaitingViewController
    }
}

and update the state from any place in ViewController

waitingSheet?.updateState(.ending)

and

waitingSheet?.updateState(.isDone)

The weak property sets itself to nil after the sheet has been deallocated

  • Related