Search code examples
iosswiftgrand-central-dispatchdispatch-async

dispatch_get_main_queue() not update UI when move from oneViewController to another ViewController and back again


In my application i perform long task in background. So i used Grand Central Dispatch (GCD) and update the UI in main Queue.

Here is my code

dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0), {
    // perform long task
    //update UI   
     dispatch_async(dispatch_get_main_queue(), {
           self.Label.text = "value changed"  // not work
           self.collectionView.reloadData() // work properly
        })    
})  

It will work fine. However when i move from my current ViewController to NextViewController and again back toViewController background task is executed but my Label will not change.

QUESTION: Please help me so i can update my UI when back from another ViewController

NOTE: for moving one ViewController to another the task is perform in main queue.

EDIT : here is a output of @Yuri code

2015-06-08 14:34:19.565 myApp[7726:1481939]  before: self:Logic() `self.label:<myApp.ViewController: 0x15947e00>

2015-06-08 14:34:50.925 myApp[7726:1481925]  after: self:Logic() self.label:<myApp.ViewController: 0x15947e00>`  

Edit2 here is output of @Oyeoj

before: <UILabel: 0x16565c60; frame = (20 56; 104 18); text = 'Back up'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x16565eb0>>
after: <UILabel: 0x16565c60; frame = (20 56; 104 18); text = 'value changed'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x16565eb0>>

Solution

  • Updates of UI must be made in main thread, it is likely that your updates in UI will not work..

    try this:

    dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0), {
    
        dispatch_async(dispatch_get_main_queue(), {
    
             println("before: \(self.Label)")
    
             self.collectionView.reloadData() 
    
             self.Label.text = "value changed"
    
             self.Label.setNeedsDisplay() // don't forget this line..
    
             println("after: \(self.Label)")
        })    
    })  
    

    Edit 1

    Okay, i'm down voted can i at least know why? just for my edification, thank you..

    From your @edit2 log, it seems that the values of self.Label.text was updated but your UIViewController was not in control of the main thread..

    and i will recommend you to use NSNotificationCenter to trigger the updates in you ViewController

    it'll look like this now:

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "methodReceiver:", name:"NotificationIdentifier", object: nil)
    
    dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0), {
        //long task
        dispatch_async(dispatch_get_main_queue(), {
    
             // sends notification to `methodReceiver` 
            NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)
        })    
    })
    
    @objc func methodReceiver(notification: NSNotification){
    
        dispatch_async(dispatch_get_main_queue(), {
    
            self.collectionView.reloadData() 
    
            self.Label.text = "value changed"
    
            self.Label.setNeedsDisplay() // don't forget this line..
    
        })
    
    
        // in case you want to remove the Observer after notification was delivered 
        NSNotificationCenter.defaultCenter().removeObserver(self, name: "NotificationIdentifier", object: nil)
    }
    

    Hope this have helped you for some reason..

    Edit 2

    from comment: this approach is work , but i want to know why my UIViewController is not in controll of main thread

    Inside your

    dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0)

    your are processing in background (long task) and will not block any main thread activity...

    then you changed to AnotherUIViewController which has UI updates, now your main thread is in AnotherUIViewController's control while dispatch_async(dispatch_get_global_queue( is still in progress, then let's say you go back to UIViewController then dispatch_async(dispatch_get_main_queue(), { was fired, BUT tasks inside AnotherUIViewController was not yet done,

    like for example:

    [self.navigationController popViewControllerAnimated:YES]; // -> (in objective-C code)

    or any other UI updates that is currently in progress will be handled first before UIViewController takes control of main thread again since all dispatch queues are FIFO..

    that's how it works i believe..

    for more information about that heres Grand Central Dispatch (GCD) Reference from apple..