U
U
uzolenta2019-10-31 07:38:03
Swift
uzolenta, 2019-10-31 07:38:03

How to update interface on button click (threading issue)?

Hello.
Help me figure out the flows, please.
What you want to implement: when you click on the button, the activity indicator starts, the code executes, the indicator stops. It seems to be simple, but I'm stuck.

@IBAction func button(_ sender: UIButton) {
acrivityIndicator.startAnimating()

//Код со множественными обращениями к серверу (URLSession.shared.dataTask)

acrivityIndicator.stopAnimating()
}

Accordingly, acrivityIndicator.startAnimating() only fires at the very end, after the entire code has been executed.
When you click on the button - the entire interface is blocked. I read that updating the interface can only be done in the main thread:
DispatchQueue.main.async {
                self.acrivityIndicator.startAnimating()
            }

But it doesn't work.
The only scheme in which everything works:
@IBAction func button(_ sender: UIButton) {
DispatchQueue.global().async {
 DispatchQueue.main.async {
acrivityIndicator.startAnimating()
}
//Код со множественными обращениями к серверу (URLSession.shared.dataTask)

 DispatchQueue.main.async {
acrivityIndicator.stopAnimating()
}
}
}

There is no understanding yet how to stop the code inside Global if, for example, an error occurs inside URLSession.shared.dataTask.
How to implement the plan correctly: so that the user can no longer do anything, but an indicator appears at the beginning when the button is pressed (before the code is executed)?
After all, when you click on the button, it already shows that in the main thread the execution is:
print(Thread.isMainThread)
        print(Thread.current)

answer:
true
<NSThread: 0x60000203e280>{number = 1, name = main}

Answer the question

In order to leave comments, you need to log in

1 answer(s)
D
doublench21, 2019-10-31
@doublench21

Stopping the indicator should be done at the end of the work. Working with URLSession is asynchronous, so you need to stop the indicator in the completion task that you start.
All examples shown by you can be simply thrown out. This is sheer horror.
You can't prevent a user from doing something. At least he should be able to switch between tabs, if any.
If you want to show the user that the download is in progress, then make an overlay that will be shown while the task is running and hidden when it is finished. There are a million examples. First one from google:
https://stackoverflow.com/questions/27960556/loadi...

text
class ViewController: UIViewController {
  
  @IBOutlet var button: UIButton!
  
  
  var alert: UIAlertController?
  
  func displayActivityIndicatorAlert() {
    alert = UIAlertController(title: "Deleting from black list...", message: nil, preferredStyle: .alert)
    let activityIndicator = UIActivityIndicatorView(style: .medium)
    activityIndicator.translatesAutoresizingMaskIntoConstraints = false
    activityIndicator.isUserInteractionEnabled = false
    activityIndicator.startAnimating()
    
    alert!.view.addSubview(activityIndicator)
    alert!.view.heightAnchor.constraint(equalToConstant: 95).isActive = true
    
    activityIndicator.centerXAnchor.constraint(equalTo: alert!.view.centerXAnchor, constant: 0).isActive = true
    activityIndicator.bottomAnchor.constraint(equalTo: alert!.view.bottomAnchor, constant: -20).isActive = true
    
    present(alert!, animated: true)
  }
  
  func dismissActivityIndicatorAlert() {
    alert?.dismiss(animated: true)
    alert = nil
  }
  
  var blackList = [Int](0...9)
  let lock = NSLock()
  
  func asyncDeleteBlackList() {
    var tasks = [URLSessionTask]()
    tasks.reserveCapacity(10)
    
    var results = [Int: String]()
    results.reserveCapacity(10)
    
    let group = DispatchGroup()
    
    displayActivityIndicatorAlert()
    
    for item in blackList {
      group.enter()
      
      let url = URL(string: "https://.../api/v1/today/batch")!
      let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
        if let error = error {
          // Что-то делаем с ошибкой на клиенте...
          return
        }
        
        guard let httpResponse = response as? HTTPURLResponse,
          (200...299).contains(httpResponse.statusCode) else
        {
          // Что-то делаем с ошибкой на сервере...
          return
        }
        
        if let mimeType = httpResponse.mimeType,
          mimeType == "application/json",
          let data = data,
          let string = String(data: data, encoding: .utf8)
        {
          print("Запрос №\(item) завершён.")
          self.lock.lock()
          results[item] = string
          self.lock.unlock()
          group.leave()
        }
      }
      
      tasks.append(task)
      task.resume()
    }
    
    group.notify(queue: .main) {
      print("\nЗадача завершена.")
      self.dismissActivityIndicatorAlert()
    }
  }
  
  override func viewDidLoad() {
    button.addTarget(self, action: #selector(pressed(sender:)), for: .touchUpInside)
  }
  
  @objc
  func pressed(sender: UIButton) {
    asyncDeleteBlackList()
  }
}

Result: (Video)
https://disk.yandex.ru/i/S5yG9YQsRRJSZg

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question