Atualizando a interface do usuário usando Dispatch_Async no Swift

No meu código, eu tenho um loop for simples que faz um loop de 100 vezes com loops nesteds para criar um atraso. Após o atraso, estou atualizando um elemento de visualização de progresso na interface do usuário por meio de um dispatch_async. No entanto, não consigo atualizar a interface do usuário. Alguém sabe por que a interface do usuário não está atualizando? Nota: A instrução de impressão abaixo é usada para verificar se o loop for está executando corretamente.

for i in 0..<100 { //Used to create a delay for var x = 0; x<100000; x++ { for var z = 0; z<1000; z++ { } } println(i) dispatch_async(dispatch_get_main_queue()) { // update some UI self.progressView.setProgress(Float(i), animated: true) } } 

Três observações, duas básicas, uma um pouco mais avançadas:

  1. Seu loop não poderá atualizar a interface do usuário nesse thread principal, a menos que o próprio loop esteja sendo executado em outro thread. Então, você pode despachá-lo para alguma fila em segundo plano. Em Swift 3:

     DispatchQueue.global(qos: .utility).async { for i in 0 ..< kNumberOfIterations { // do something time consuming here DispatchQueue.main.async { // now update UI on main thread self.progressView.setProgress(Float(i) / Float(kNumberOfIterations), animated: true) } } } 

    Em Swift 2:

     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { for i in 0 ..< kNumberOfIterations { // do something time consuming here dispatch_async(dispatch_get_main_queue()) { // now update UI on main thread self.progressView.setProgress(Float(i) / Float(kNumberOfIterations), animated: true) } } } 
  2. Observe também que o progresso é um número de 0,0 a 1,0, então você provavelmente quer dividir pelo número máximo de iterações para o loop.

  3. Se as atualizações da interface do usuário vierem mais rapidamente do thread em segundo plano do que a interface do usuário pode manipulá-las, o thread principal pode ser backlogged com solicitações de atualização (fazendo com que pareça muito mais lento do que realmente é). Para resolver isso, pode-se considerar o uso da fonte de dispatch para desacoplar a tarefa "atualizar a interface do usuário" do processo real de atualização em segundo plano.

    É possível usar um DispatchSourceUserDataAdd (no Swift 2, é um dispatch_source_t de DISPATCH_SOURCE_TYPE_DATA_ADD ), postar chamadas de add ( dispatch_source_merge_data no Swift 2) do thread em segundo plano com a frequência desejada e a interface do usuário as processará o mais rápido possível, mas se unirá eles juntos quando ele chama data ( dispatch_source_get_data no Swift 2) se as atualizações de segundo plano entrarem mais rapidamente do que a interface do usuário pode processá-las. Isso alcança o máximo desempenho em segundo plano com ótimas atualizações de interface do usuário, mas o mais importante, isso garante que a interface do usuário não se torne um gargalo.

    Então, primeiro declare alguma variável para acompanhar o progresso:

     var progressCounter: UInt = 0 

    E agora seu loop pode criar uma origem, definir o que fazer quando a fonte é atualizada e, em seguida, iniciar o loop asynchronous que atualiza a fonte. No Swift 3 isto é:

     progressCounter = 0 // create dispatch source that will handle events on main queue let source = DispatchSource.makeUserDataAddSource(queue: .main) // tell it what to do when source events take place source.setEventHandler() { [unowned self] in self.progressCounter += source.data self.progressView.setProgress(Float(self.progressCounter) / Float(kNumberOfIterations), animated: true) } // start the source source.resume() // now start loop in the background DispatchQueue.global(qos: .utility).async { for i in 0 ..< kNumberOfIterations { // do something time consuming here // now update the dispatch source source.add(data: 1) } } 

    Em Swift 2:

     progressCounter = 0 // create dispatch source that will handle events on main queue let source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue()); // tell it what to do when source events take place dispatch_source_set_event_handler(source) { [unowned self] in self.progressCounter += dispatch_source_get_data(source) self.progressView.setProgress(Float(self.progressCounter) / Float(kNumberOfIterations), animated: true) } // start the source dispatch_resume(source) // now start loop in the background dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { for i in 0 ..< kNumberOfIterations { // do something time consuming here // now update the dispatch source dispatch_source_merge_data(source, 1); } }