Search code examples
iosswiftdispatch-queue

Dispatch for loading UIImages producing unexpected results


I am building an app that has several images(Plans) that are loaded and setup on a scrollview with a page content. I am trying to load the first image on the main thread and then load the surrounding images from it's index on a background thread. All the UIImageViews are in an array (planImages) to reference when I need to set their images with the loaded data.

The problem is the images aren't always updating with the recently loaded images. This is my first time using Dispatch, so is there something I am doing wrong? Here is the section of code that is giving me the issue.

    func setupPagesInScrollView(firstImage: Int)
{
    let numberOfPages: CGFloat = CGFloat(selectedDetails!.numberOfPlans)

    scrollView!.contentSize = CGSize(width: scrollView!.frame.width * numberOfPages, height: scrollView!.frame.height)
    pageControl!.numberOfPages = selectedDetails!.numberOfPlans

    if(planImages.count < selectedDetails!.numberOfPlans)
    {
        for index in planImages.count...selectedDetails!.numberOfPlans - 1
        {
            let iView = UIImageView()
            iView.frame = CGRect(x: 0, y: 0, width: scrollView!.frame.width, height: scrollView!.frame.height)
            planImages.append(iView)
            scrollView!.addSubview(iView)

            iView.translatesAutoresizingMaskIntoConstraints = false
            iView.topAnchor.constraint(equalTo: scrollView!.topAnchor).isActive = true
            iView.leftAnchor.constraint(equalTo: scrollView!.leftAnchor, constant: scrollView!.frame.width * CGFloat(index)).isActive = true
            iView.heightAnchor.constraint(equalToConstant: scrollView!.contentSize.height).isActive = true
            iView.widthAnchor.constraint(equalToConstant: scrollView!.frame.width).isActive = true
        }
    }

    DispatchQueue.global(qos: .background).async
        {
            //Load first selected image
            let path = self.selectedDetails!.dirPath + self.selectedDetails!.plansName
            self.setupImageAtIndex(path: path, index: firstImage)

            //Build Image data around first image on background threads
            var mask = 1
            let max = self.selectedDetails!.numberOfPlans

            while firstImage + mask < max || firstImage - mask > 0
            {
                if(firstImage + mask < max)
                {
                    self.setupImageAtIndex(path: path, index: firstImage + mask)
                }

                if(firstImage - mask >= 0)
                {
                    self.setupImageAtIndex(path: path, index: firstImage - mask)
                }

                mask = mask + 1
            }

            //Remove extra images from memory if needed
            if(self.planImages.count > self.selectedDetails!.numberOfPlans)
            {
                let dif = self.planImages.count - self.selectedDetails!.numberOfPlans

                for _ in 1...dif
                {
                    let index = self.planImages.count - 1
                    let plan = self.planImages[index]
                    self.planImages.remove(at: index)
                    plan.removeFromSuperview()
                    plan.image = nil
                }
            }
    }
}

private func setupImageAtIndex(path: String, index: Int)
{
    let imageName = index + 1 > 9 ? path + String(index + 1) : path + "0" + String(index + 1)

    planImages[index].image = UIImage(contentsOfFile: imageName)
}

Solution

  • As per apple documentation You must update UI elements on the main queue.

    Update your code like this

    private func setupImageAtIndex(path: String, index: Int)
        {
            let imageName = index + 1 > 9 ? path + String(index + 1) : path + "0" + String(index + 1)
            DispatchQueue.main.async {
                planImages[index].image = UIImage(contentsOfFile: imageName)
    
            }
        }