1

I'm writing an application in Swift. I've been searching for how to start an indeterminate progress bar when an action is being done, but there doesn't seem to be any results. I want the indeterminate progress bar to run when an action is running instead the system says "Applications not responding", here is my progress bar function:

@IBOutlet weak var progressBar: NSProgressIndicator!
  • [`NSProgressIndicator#isIndeterminate`](https://developer.apple.com/documentation/appkit/nsprogressindicator/1501146-isindeterminate); [`NSProgressIndicator#style`](https://developer.apple.com/documentation/appkit/nsprogressindicator/1501158-style); [`NSProgressBar#startAnimation`](https://developer.apple.com/documentation/appkit/nsprogressindicator/1501167-startanimation) - if it's not animating then you're probably executing your functionality from within the main thread, so https://stackoverflow.com/questions/14861373/indeterminate-nsprogressindicator-will-not-animate might help – MadProgrammer Oct 12 '21 at 03:13
  • Unless you change the indiciator status, it will remain indeterminate, if I remember correctly. Plus, use the right tags. – El Tomato Oct 12 '21 at 03:29
  • How do I change the indicator status? – user17078449 Oct 12 '21 at 03:44

1 Answers1

1

This is a simple playground test (make sure you create use Blank from the macOS templates)

import AppKit
import PlaygroundSupport

let view = NSView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))

let progressBar = NSProgressIndicator(frame: CGRect(x: view.bounds.midX - 60, y: 0, width: 120, height: 32))

view.addSubview(progressBar)
progressBar.isIndeterminate = true
progressBar.style = .bar

// Present the view in Playground
PlaygroundPage.current.liveView = view

progressBar.startAnimation(nil)

DispatchQueue.global().async {
    Thread.sleep(forTimeInterval: 5)
    DispatchQueue.main.async {
        progressBar.stopAnimation(nil)
    }
}

This will present a indeterminate playground and after 5 seconds it will stop animating.

The point here is, you are likely blocking the "main" thread, which is why you're getting "Applications not responding", but you also need to ensure that any updates made to the UI are done only on the "main" thread.

The use of DispatchQueue.global().async and Thread.sleep(forTimeInterval: 5) is for demonstration purposes only, instead, what ever long running/blocking work your doing would be executed within the DispatchQueue.global() context

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks for your help! Finally I've managed to run an indeterminate progress bar. Btw can you help me with "Application not responding"? Please give me a more detail explanation – user17078449 Oct 12 '21 at 03:51
  • This is basic UI development. Most UI frameworks are single threaded and not thread safe, this means that anything that takes a long time to run or is blocks the main thread will stop the thread from executing, so the user can't interact with the App or do something and can't process any new events. But on the other hand, you shouldn't update the UI from outside the context of the main thread, hence the use of `DispatchQueue.main.async` in the example – MadProgrammer Oct 12 '21 at 03:56
  • The fact is I'm executing a few numbers of terminal command when the button is clicked – user17078449 Oct 12 '21 at 04:01
  • So another solution is just execute them and leave them run, not to wait until they completed – user17078449 Oct 12 '21 at 04:06
  • @user17078449 Depends if you want to know the result or not and if you want to know when it's completed – MadProgrammer Oct 12 '21 at 04:11
  • I am just expecting it will stop the progress bar when completed – user17078449 Oct 12 '21 at 06:35
  • @user17078449 So, you will need to wait for it to finish, off the main thread – MadProgrammer Oct 12 '21 at 07:16