Dispatch for loading UIImages producing unexpected results












0















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)
}









share|improve this question























  • You must update UI elements on the main queue, so at the very least your addSubview and removeFromSuperview calls need to be made in a DispatchQueue.main.async closure. Honestly you probably won't see much performance difference between just running everything on the main queue and dispatching work onto another queue, and you almost definitely don't want to use a background QoS. userInitiaited is probably a better choice.

    – Paulw11
    Nov 20 '18 at 6:18













  • So I have made the changes and to my surprise this doesn't even preload the images into memory. The images don't appear to be loaded into memory until it is assigned to a view. Is there any way to force these images into memory so I don't get the load time delay when finally assigning it to the view?

    – Dane Caro
    Nov 20 '18 at 22:21











  • You should show your revised code. The typical approach with a scrollview is to maintain 3 "pages"; the page to the left, the visible page and the page to the right. When a left or right scroll completes you move the left/right/center images (and load a new image) and adjust the scrollview offset so that the visible page is back in the center. Don't create a "page" per image; that isn't efficient

    – Paulw11
    Nov 20 '18 at 22:36


















0















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)
}









share|improve this question























  • You must update UI elements on the main queue, so at the very least your addSubview and removeFromSuperview calls need to be made in a DispatchQueue.main.async closure. Honestly you probably won't see much performance difference between just running everything on the main queue and dispatching work onto another queue, and you almost definitely don't want to use a background QoS. userInitiaited is probably a better choice.

    – Paulw11
    Nov 20 '18 at 6:18













  • So I have made the changes and to my surprise this doesn't even preload the images into memory. The images don't appear to be loaded into memory until it is assigned to a view. Is there any way to force these images into memory so I don't get the load time delay when finally assigning it to the view?

    – Dane Caro
    Nov 20 '18 at 22:21











  • You should show your revised code. The typical approach with a scrollview is to maintain 3 "pages"; the page to the left, the visible page and the page to the right. When a left or right scroll completes you move the left/right/center images (and load a new image) and adjust the scrollview offset so that the visible page is back in the center. Don't create a "page" per image; that isn't efficient

    – Paulw11
    Nov 20 '18 at 22:36
















0












0








0








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)
}









share|improve this question














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)
}






ios swift dispatch-queue






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 20 '18 at 6:14









Dane CaroDane Caro

4517




4517













  • You must update UI elements on the main queue, so at the very least your addSubview and removeFromSuperview calls need to be made in a DispatchQueue.main.async closure. Honestly you probably won't see much performance difference between just running everything on the main queue and dispatching work onto another queue, and you almost definitely don't want to use a background QoS. userInitiaited is probably a better choice.

    – Paulw11
    Nov 20 '18 at 6:18













  • So I have made the changes and to my surprise this doesn't even preload the images into memory. The images don't appear to be loaded into memory until it is assigned to a view. Is there any way to force these images into memory so I don't get the load time delay when finally assigning it to the view?

    – Dane Caro
    Nov 20 '18 at 22:21











  • You should show your revised code. The typical approach with a scrollview is to maintain 3 "pages"; the page to the left, the visible page and the page to the right. When a left or right scroll completes you move the left/right/center images (and load a new image) and adjust the scrollview offset so that the visible page is back in the center. Don't create a "page" per image; that isn't efficient

    – Paulw11
    Nov 20 '18 at 22:36





















  • You must update UI elements on the main queue, so at the very least your addSubview and removeFromSuperview calls need to be made in a DispatchQueue.main.async closure. Honestly you probably won't see much performance difference between just running everything on the main queue and dispatching work onto another queue, and you almost definitely don't want to use a background QoS. userInitiaited is probably a better choice.

    – Paulw11
    Nov 20 '18 at 6:18













  • So I have made the changes and to my surprise this doesn't even preload the images into memory. The images don't appear to be loaded into memory until it is assigned to a view. Is there any way to force these images into memory so I don't get the load time delay when finally assigning it to the view?

    – Dane Caro
    Nov 20 '18 at 22:21











  • You should show your revised code. The typical approach with a scrollview is to maintain 3 "pages"; the page to the left, the visible page and the page to the right. When a left or right scroll completes you move the left/right/center images (and load a new image) and adjust the scrollview offset so that the visible page is back in the center. Don't create a "page" per image; that isn't efficient

    – Paulw11
    Nov 20 '18 at 22:36



















You must update UI elements on the main queue, so at the very least your addSubview and removeFromSuperview calls need to be made in a DispatchQueue.main.async closure. Honestly you probably won't see much performance difference between just running everything on the main queue and dispatching work onto another queue, and you almost definitely don't want to use a background QoS. userInitiaited is probably a better choice.

– Paulw11
Nov 20 '18 at 6:18







You must update UI elements on the main queue, so at the very least your addSubview and removeFromSuperview calls need to be made in a DispatchQueue.main.async closure. Honestly you probably won't see much performance difference between just running everything on the main queue and dispatching work onto another queue, and you almost definitely don't want to use a background QoS. userInitiaited is probably a better choice.

– Paulw11
Nov 20 '18 at 6:18















So I have made the changes and to my surprise this doesn't even preload the images into memory. The images don't appear to be loaded into memory until it is assigned to a view. Is there any way to force these images into memory so I don't get the load time delay when finally assigning it to the view?

– Dane Caro
Nov 20 '18 at 22:21





So I have made the changes and to my surprise this doesn't even preload the images into memory. The images don't appear to be loaded into memory until it is assigned to a view. Is there any way to force these images into memory so I don't get the load time delay when finally assigning it to the view?

– Dane Caro
Nov 20 '18 at 22:21













You should show your revised code. The typical approach with a scrollview is to maintain 3 "pages"; the page to the left, the visible page and the page to the right. When a left or right scroll completes you move the left/right/center images (and load a new image) and adjust the scrollview offset so that the visible page is back in the center. Don't create a "page" per image; that isn't efficient

– Paulw11
Nov 20 '18 at 22:36







You should show your revised code. The typical approach with a scrollview is to maintain 3 "pages"; the page to the left, the visible page and the page to the right. When a left or right scroll completes you move the left/right/center images (and load a new image) and adjust the scrollview offset so that the visible page is back in the center. Don't create a "page" per image; that isn't efficient

– Paulw11
Nov 20 '18 at 22:36














1 Answer
1






active

oldest

votes


















0














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)

}
}





share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53387250%2fdispatch-for-loading-uiimages-producing-unexpected-results%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    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)

    }
    }





    share|improve this answer




























      0














      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)

      }
      }





      share|improve this answer


























        0












        0








        0







        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)

        }
        }





        share|improve this answer













        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)

        }
        }






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 20 '18 at 6:25









        iOS_MaccusiOS_Maccus

        30710




        30710
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53387250%2fdispatch-for-loading-uiimages-producing-unexpected-results%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            這個網誌中的熱門文章

            Xamarin.form Move up view when keyboard appear

            Post-Redirect-Get with Spring WebFlux and Thymeleaf

            Anylogic : not able to use stopDelay()