Shirish Hirekodi
Shirish Hirekodi

Reputation: 412

UICollectionView not refreshing from dispatch_async

I’m a few days old in iOS programming so please bear with me. I’m trying to show a UICollectionView with a matrix of some images.

Problem is getting the images to show up when initialisation of UICollectionView happens from inside a dispatch_async. Initialisation works fine from viewDidLoad(). Both the println output can be observed in Xcode console so background process and subsequent UI updates on main ought to happen.

I have left out other functions because apparently they are not the problem (init works from viewDidLoad)

Using Xcode 6.4. Target is iPhone 5, 8.4.1

Here is the relevant code:

class MyCollectionViewController: UICollectionViewController, UICollectionViewDataSource, UICollectionViewDelegate {

class MyClass {
        var x: Int
        var y: Int
    var thumbnail: String
        init(…) {
        }
    }

    var list = [MyClass]()

override func viewDidLoad() {
        super.viewDidLoad()

        // Register cell classes
        // self.collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)

        // Do any additional setup after loading the view.

    /* uncommenting this works fine
        list.append(MyClass(x: 1000, y: 10, thumbnail: “img1.jpg”))
        list.append(MyClass(x: 1001, y: 11, thumbnail: “img2.jpg”))
        list.append(MyClass(x: 1002, y: 12, thumbnail: “img3.jpg”))
        list.append(MyClass(x: 1003, y: 13, thumbnail: “img4.jpg”))
        list.append(MyClass(x: 1004, y: 14, thumbnail: “img5.jpg”))
        list.append(MyClass(x: 1005, y: 15, thumbnail: “img6.jpg”))
    */

        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), {
            println("This is run on the background queue")

            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                println("This is run on the main queue, after the previous code in outer block")

                // This code block below just shows a black screen
        self.list.append(MyClass(x: 1000, y: 10, thumbnail: “img1.jpg”))
                self.list.append(MyClass(x: 1001, y: 11, thumbnail: “img2.jpg”))
                self.list.append(MyClass(x: 1002, y: 12, thumbnail: “img3.jpg”))
                self.list.append(MyClass(x: 1003, y: 13, thumbnail: “img4.jpg”))
                self.list.append(MyClass(x: 1004, y: 14, thumbnail: “img5.jpg”))
                self.list.append(MyClass(x: 1005, y: 15, thumbnail: “img6.jpg”))
            })
        })
…
}

UPDATE: Here is the fixed code

class MyCollectionViewController: UICollectionViewController, UICollectionViewDataSource, UICollectionViewDelegate {

class MyClass {
        var x: Int
        var y: Int
    var thumbnail: String
        init(…) {
        }
    }

    var list = [MyClass]()

override func viewDidLoad() {
        super.viewDidLoad()

        // Register cell classes
        // self.collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)

        // Do any additional setup after loading the view.
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), {
            println("This is run on the background queue")

            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                println("This is run on the main queue, after the previous code in outer block")

                self.list.append(MyClass(x: 1000, y: 10, thumbnail: “img1.jpg”))
                self.list.append(MyClass(x: 1001, y: 11, thumbnail: “img2.jpg”))
                self.list.append(MyClass(x: 1002, y: 12, thumbnail: “img3.jpg”))
                self.list.append(MyClass(x: 1003, y: 13, thumbnail: “img4.jpg”))
                self.list.append(MyClass(x: 1004, y: 14, thumbnail: “img5.jpg”))
                self.list.append(MyClass(x: 1005, y: 15, thumbnail: “img6.jpg”))

        // This statement solved the issue
        self.collectionView?.reloadData()
            })
        })
…
}

Upvotes: 1

Views: 1363

Answers (1)

kandelvijaya
kandelvijaya

Reputation: 1585

UICollectionViewController or UITableViewController tries to call the datasource when the viewDidLoad() is called and hence is synchronous. If somehow the class needs to fetch data from server and does that in good async call, it means the data is going to arrive later and after the datasources methods to load the cells are called.

Now to reload the data after the arrival from the server or async task, call the collectionView's reloadData method like so.

 dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), {
        println("This is run on the background queue")

        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            println("This is run on the main queue, after the previous code in outer block")

            // This code block below just shows a black screen
    self.list.append(MyClass(x: 1000, y: 10, thumbnail: “img1.jpg”))
            self.list.append(MyClass(x: 1001, y: 11, thumbnail: “img2.jpg”))
            self.list.append(MyClass(x: 1002, y: 12, thumbnail: “img3.jpg”))
            self.list.append(MyClass(x: 1003, y: 13, thumbnail: “img4.jpg”))
            self.list.append(MyClass(x: 1004, y: 14, thumbnail: “img5.jpg”))
            self.list.append(MyClass(x: 1005, y: 15, thumbnail: “img6.jpg”))
        })
     self.collectionView.reloadData()    //telling to reload the data once again

    })

A subclass of UICollectionViewController gets a default hook to the collectionView outlet and we used it.

However sometimes what i do is, put a property observer in the data array and then call the reload there.

 var list = [MyClass](){
   didSet{
      //do some work like validation
      collectionView.reloadData()
   }
 }

Anyways, i hope it helps. Cheers!

Upvotes: 2

Related Questions