Reputation: 13
I have ViewController
with button. When I press on button I move to TableViewController
with images in cells. I have array with 300 images.
But when I press on button my app paused for 12-14 seconds to load an array of images. After 14 second I move to TableViewController
. How to fix it?
class TableViewController: UITableViewController {
var imageArray: [UIImage] =
[UIImage(named: "1.jpg")!,
...
UIImage(named: "300.jpg")!]
var textArray: [String] = ["text1", ... "text300"]
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return textArray
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
cell.cardImageView.image = imageArray[indexPath.row]
cell.label.text = "\(textArray[indexPath.row])"
}
}
Upvotes: 1
Views: 843
Reputation: 8011
Don't store the object of UIImage
in the array, as your code suggested, your images are locally stored as asset, just load the image names.
instead of this
var imageArray: [UIImage] =
[UIImage(named: "1.jpg")!,
...
UIImage(named: "300.jpg")!]
Do this
var imageNameArray: [String] =
["1.jpg",
...
"300.jpg"]
In the cellForRowAtIndexPath
do following
cell.cardImageView.image = UIImage(named: imageArray[indexPath.row])
You could load the image data in background thread, once available then set it. But this approach has several shortcoming. If you do this you have to manually manage the image caching mechanism, because you dont want to load same image twice while shown twice in the cell.
There is a good news for you that, Uiimage(named:) method is thread safe for iOS9+. So if you are targeting iOS9+ you can load the image using UIImage(named:) in background thread. It will automatically manage the caching mechanism
DispatchQueue.global(qos: .background).async {
// load image here
let image = UIImage(named......
DispatchQueue.main.async {
// set image here
cell.card........
}
}
one possible bug may arise in this approach, such that while scrolling fast you may see old images for a while. You could solve it easily by managing the indexpath for which this image was loaded and comparing with current one.
Upvotes: 1
Reputation: 1376
Rather than creating an array of images straight out why don't you try this:
For numberOfRowsInSection set:
return 299
Add this method to your Viewcontroller:
func loadImageAsync(imageName: String, completion: @escaping (UIImage) -> ()) {
DispatchQueue.global(qos: .userInteractive).async {
guard let image = UIImage(named: imageName) else {return}
DispatchQueue.main.async {
completion(image)
}
}
}
in your tableView cellForRowAt all you will have to call is:
loadImageAsync(imageName: "\(indexPath.row + 1).jpg") { (image) in
cell.cardImageView.image = image
}
cell.label.text = "text\\(indexPath.row + 1)"
It means the app will only have to load the images on demand instead of making a huge array of images that it may never use if the user doesn't scroll all the way down. A 300 image array would be very CPU and memory intensive to create.
Upvotes: 1
Reputation: 28
I believe you should perform some kind of lazy initialization here so you get the benefit of cell reusing.
your code must be like this:
var imageTitles: [String] =
"1.jpg",
...
"300.jpg"]
var textArray: [String] = ["text1", ... "text300"]
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return textArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
cell.cardImageView.image = UIImage(named: imageTitles[indexPath.row])
cell.label.text = textArray[indexPath.row]
}
Upvotes: 0