UITableView with images & performance issues

Bhuman Soni
Bhuman Soni October 8, 2018
Updated 2021/01/10 at 2:35 PM
I encountered some performance issues while working with a UITableView when I was building our first AI powered iOS app, I was there.  I was building a solution where a UITableView would have a series of rows where the data includes image. The issue with this was, as more rows were added to the table, the app became really slow (on my test device i.e. my old iPhone 5). Initially I was quite confused and then I found a solution to the problem after which was even more confused…by the simplicity of the solution. Anyway this blog post is on UITableView with images & performance with code samples to improve it in native iOS apps. A breakdown of the post is as follows,
  1. my solution, which was little more than simple image scaling
  2. providing some code samples – including a full iOS project you can download from GitHub
  3. asking you if there’s a better way to solve this problem and whether or not this was a problem at all?
So without further a due let’s get into it by looking at my motivations for this.

UITableView with images & performance

If you read my previous post you will know a little about what our new app, I was there is i.e. it is an AI powered photo, where the user captures image with the camera, which the app tries to analyse using image processing and machine learning. The captured image is then added to a row of a UITableView which the user can tap to examine the original image! One of the first places I learned iOS development from was from Apple i.e. their Start developing iOS apps (Swift) article. That’s a great tutorial and it lays a good foundation of getting started with iOS UI controls such as UITableView, Segue and such. Anyway I used that tutorial as a foundation for building my iOS app and I soon realised that, the tutorial works is great when dealing with a small number rows with. As you add more rows with images, the device gets progressively slower as the number of rows increases. This was a high priority problem since the devices that I used for my tests got progressively hot to the point it was uncomfortable as more and more images were captured, analysed and added to the table. To solve this, I came up with a rather straight forward solution which to me seemed very obvious and this made me think? given that I am relatively new to iOS development, was this a problem at all? Did I solve a problem, was this even a problem or is there some iOS info that I completely ignored? Anyway you can let me know all that in the comments on this post but first let’s see what my solution is.


The solution is quite simple: every time an image is captured save two versions of the captured image i.e. the full-size image and a thumbnail. To better understand this solution, it’s best we examine what the target app is,

Target App

This is an app where you store data in a UITableView, where each cell contains an image and some text next to it that describes the image. Now, every time I am learning something new programmatically, looking at some code samples helps me understand it better. I get the general idea after reading about it and then code samples really drive home the point. So for anyone who’s in a similar situation, I have created a GitHub repository (UITableViewWithImage) with some sample code for this.

Sample code

The app in the sample code has some very basic code,
  1. The main view of the app is embedded in a navigation view and it has a UITableView
  2. On the navigation bar  there’s bar button (UIBarButtonItem) ‘+’  that you can tap to open the DetailView
  3. The DetailView can be accessed to either create a new record or to view an existing TableEntry
I am not going to go into the details of iOS UI creation in this blogpost, if you want that then I suggest you have a look at this tutorial! Here I will talk about aspects of the sample code that aren’t in that tutorial e.g. the code invoked on tapping “+”
@IBAction func capture(_ sender: Any) {
    let imagePicker = UIImagePickerController()
    imagePicker.delegate = self
    imagePicker.allowsEditing = false
    if UIImagePickerController.isSourceTypeAvailable(.camera) {
        imagePicker.sourceType = .camera
    } else {
        imagePicker.sourceType = .photoLibrary
    self.present(imagePicker, animated: true)
The UIImagePickerController is first defined and it’s all fairly standard the only thing that’s a little different is we check whether we have access to the device camera to capture an image. The idea is that you add a record (row) to your table view by either capturing an image or selecting an image from your photo library. hmm, what else? I think the rest of the code is fairly standard. If you run the app both on your mac or on your iDevice, you will realise the device gets progressively hotter as you keep adding data. This is because you need to add the following image resizing code

Subscribe & never miss a post

Image resize code

Onto the main focus of this post, as a general rule of thumb, I tend to identify a group of functions that solve problems in a certain domain and bundle them into “one” logically named class. You know because I like “separating my concerns”… ahh haha see what I did there? In I was there code, we have a class called ImageHelper which has all the code that deals with things such as, resizing, adding text overlays and various other things that help when working with images. For this example, however I have only included the one function that’s relevant to this post i.e. resizeImage
static func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
    let size = image.size
    let widthRatio = targetSize.width / size.width
    let heightRatio = targetSize.height / size.height

    var newSize: CGSize
    if (widthRatio > heightRatio) {
        newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
    } else {
        newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
    let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)

    UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
    image.draw(in: rect)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()!
    return newImage
That’s to resize an image, the next interesting bit is the FileIO class. Yes you guessed it right, this class has all the functions that deal with the file operations i.e. saving, loading, retrieving etc etc. Anyway in this class we have a few functions of interest
func getFilename(name: String) -> (name:String, thumbnail: String) {
    let now = Date()
    let filename = "\(now.timeIntervalSince1970)_\(name).png"
    let thumbnail = "\(filename)_thumbnail.png"
    return (name:filename, thumbnail: thumbnail)
This is fairly straightforward, when we save a file we want two things, a string representing the name and the thumbnail of the file. To ensure a filename is unique we append a time stamp to it. We could parametrise the file extension but for simplicity’s sake let’s just leave it to .png.
func saveImage(saveFileName:String, image: UIImage) {
    let data = UIImagePNGRepresentation(image)
    FileManager.default.createFile(atPath: saveFileName, contents: data, attributes: nil)
static func getFileURL(fileName:String) -> URL? {
    let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let dirContents = try? FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil, options: [])
    if let dc = dirContents {
        return dc.first{$0.absoluteString.contains(fileName)}
    return nil
The the important thing to know in the saveImage post is UIImagePNGRepresentation and then there’s the getFileURL function that retrieves the right URL based on the filename. Now to know more about how to save a file, refer to this tutorial here.

Subscribe & never miss a post

Putting it all together

Now that we have looked at all the relevant functions, let’s look at how it all fits together. It all starts with a UIImagePickerControllerDelegate method
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

    guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else {
        print("Error getting the image")
    //save two copies of the image
    let imageName = "Image_\(data.count)"

    //Step 1: save original image
    let fullImgFilename = FileIO.fileIO.getNameWithDirPath(filename: "\(imageName)\(Constants.ROW_DATA_IMG_EXTENSION)")
    FileIO.fileIO.saveImage(saveFileName: fullImgFilename, image: image)
    //Step 2: resize image
    let thumbnailImage = UIImage.resizeImage(image: image, targetSize: CGSize(width: 75, height: 75))
    //Step 3: save thumbnail
    let thumbnailname = FileIO.fileIO.getNameWithDirPath(filename: "\(imageName)\(Constants.THUMBNAIL_NAME)\(Constants.ROW_DATA_IMG_EXTENSION)")
    FileIO.fileIO.saveImage(saveFileName: thumbnailname, image: thumbnailImage)

    let newRow = TableData(name: imageName)
    picker.dismiss(animated: true, completion: nil)
Most of the code above is self-explanatory but let me try to summarise a few key points of it,
  1. Ensure we have a valid image and give it a name
  2. Save the original image, as well as a smaller sized thumbnail for the image
  3. Make sure it is saved to the user’s documents directory
  4. Append a new record to the array of data that constitutes to the rows of the UITableView
  5. Reload the table
Ok so once we have added the data, it’s time to see how the table reloading works but wait before that, I think we should look at the custom data structure in our project TableData
class TableData: NSObject {
    var name: String        
    init(name: String) {
        self.name = name
    func getRowThumbnail() -> UIImage {
        let thumbnailname = FileIO.fileIO.getNameWithDirPath(filename: "\(name)\(Constants.THUMBNAIL_NAME)\(Constants.ROW_DATA_IMG_EXTENSION)")
        let thumbnail = UIImage(contentsOfFile: thumbnailname)!
        return thumbnail
    func getRowImage() -> UIImage {
        let imageName = FileIO.fileIO.getNameWithDirPath(filename: "\(name)\(Constants.ROW_DATA_IMG_EXTENSION)")
        let image = UIImage(contentsOfFile: imageName)!
        return image
It’s a simple Swift class that extends NSObject with 2 extra methods for our needs. getRowImage() and getRowThumbnail(). These methods with the help of our FileIO helper class get the name or path of our saved image and initialise and return a UIImage object. All right, now that we know all that, we can look at how our UITableView loads it’s data
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableViewCell else {
        fatalError("Cannot cast table cell")
    let rowData = data[indexPath.row]
    cell.rowLabel.text = rowData.name
    cell.rowImage.image = rowData.getRowThumbnail()
    cell.rowData = rowData
    return cell
This is actually pretty straight forward i.e. our TableViewCell has two properties, a UILabel for rowLabel and a UIImage for rowImage, so if we are loading contents to our table cell we just call the TableData.getRowThumbnail() method. So where do we use the getImage() method? In the DetailViewController, remember a user taps on a tableView cell to examine it in more detail.  Let’s have a look at the DetailViewController method 
class DetailViewController: UIViewController {
    var rowData: TableData?
    //MARK: IBOutlet
    @IBOutlet var nameLbl: UILabel!
    @IBOutlet var rowImage: UIImageView!

    override func viewDidLoad() {

        if let rd = rowData {
            nameLbl.text = rd.name
            rowImage.image = rd.getRowImage()
In here we use the getRowImage() method to show the original higher resolution image…and that’s a wrap I think? Did I miss anything? ok maybe one more thing that I can think of? hmm, for each image captured, we are storing an additional item, however small the space maybe, what if you delete a row from the table? We would need to delete the image right and probably the thumbnail as well, so how do we do that? Well there’s code for that too, have a look at the deleteImageFile() method of the FileIO class.


As we wrap it up, let me repeat I am not sure if this was a real problem or not? I was building an iOS app that showed data in a UITableView where each row of the table has an image captured by the user’s camera. In that solution when the user taps on a row, the user can see the full image. Now when I first started building it, I was testing it on my 4 year old iPhone 5 and I noticed significant slow down and device over heating as the number of rows in the table kept increasing. To save time I came up with a quick solution that involved storing 2 copies of an image, one the original image that the user copied and another a smaller sized thumbnail that can be displayed in the UITableView. In this blogpost I presented my solution and provided some code samples via a GitHub repo for such a solution that can help you paint a picture of my simple image scaling solution.

Subscribe & never miss a post

As usual, if you find any of my posts useful support me by  buying or even trying one of my apps on the App Store.  https://mydaytodo.com/apps/ Also, if you can leave a review on the App Store or Google Play Store, that would help too.

Share this Article
  • Hi i am kavin, its my first occasion to commenting anywhere, when i read this piece of writing i thought i could also make comment due to this sensible

  • Hey there! This post could not be writtdn any better!

    Reading through this post reminds me of my previous room mate!
    He always kept talking about this. I wilol forward this page to him.
    Pretty sure he will have a good read. Thanks foor sharing!

  • What you said made a great deal of sense. But, what about this?
    suppose you typed a catchier post title? I mean, I don’t
    want to tell you how to run your blog, but what if you added something that grabbed people’s attention? I mean UITableView with
    images & performance issues – My Day To-Do is a little plain. You could glance at Yahoo’s front page
    and see how they write news titles to get viewers to click.

    You might try adding a video or a picture or two to grab
    readers interested about what you’ve written. Just
    my opinion, it could bring your posts a little bit more interesting.

Leave a Reply

Your email address will not be published.

Ads Blocker Image Powered by Code Help Pro

Ads Blocker Detected!!!

We have detected that you are using extensions to block ads. Please support us by disabling these ads blocker.

Powered By
Best Wordpress Adblock Detecting Plugin | CHP Adblock