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