user
user

Reputation: 13

swift: loading array with 300 images

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

Answers (3)

Ratul Sharker
Ratul Sharker

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

paul_f
paul_f

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

Mouaz Alzahabi
Mouaz Alzahabi

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

Related Questions