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)
}
ios swift dispatch-queue
add a comment |
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
You must update UI elements on the main queue, so at the very least youraddSubview
andremoveFromSuperview
calls need to be made in aDispatchQueue.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 abackground
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
add a comment |
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
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
ios swift dispatch-queue
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 youraddSubview
andremoveFromSuperview
calls need to be made in aDispatchQueue.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 abackground
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
add a comment |
You must update UI elements on the main queue, so at the very least youraddSubview
andremoveFromSuperview
calls need to be made in aDispatchQueue.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 abackground
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
add a comment |
1 Answer
1
active
oldest
votes
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)
}
}
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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)
}
}
add a comment |
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)
}
}
add a comment |
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)
}
}
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)
}
}
answered Nov 20 '18 at 6:25
iOS_MaccusiOS_Maccus
30710
30710
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
You must update UI elements on the main queue, so at the very least your
addSubview
andremoveFromSuperview
calls need to be made in aDispatchQueue.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 abackground
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