DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • A Better Web3 Experience: Account Abstraction From Flow (Part 2)
  • Training a Handwritten Digits Classifier in Pytorch With Apache Cassandra Database
  • Mastering Persistence: Why the Persistence Layer Is Crucial for Modern Java Applications
  • .NET Performance Optimization Techniques for Expert Developers

Trending

  • How To Simplify Multi-Cluster Istio Service Mesh Using Admiral
  • No Spark Streaming, No Problem
  • Parallel Sort
  • A Better Web3 Experience: Account Abstraction From Flow (Part 1)
  1. DZone
  2. Data Engineering
  3. Databases
  4. Dynamically Loading Table View Images in iOS

Dynamically Loading Table View Images in iOS

Learn how thumbnail images in the table views of iOS mobile apps can be loaded dynamically as needed to populate table view cells.

Greg Brown user avatar by
Greg Brown
·
Jul. 10, 17 · Tutorial
Like (4)
Save
Tweet
Share
15.87K Views

Join the DZone community and get the full member experience.

Join For Free

ios applications often display thumbnail images in table views alongside other text-based content such as contact names or product descriptions. however, these images are not usually delivered with the initial request, but must instead be retrieved separately afterward. they are typically downloaded in the background as needed to avoid blocking the main thread, which would temporarily render the user interface unresponsive.

for example, consider this rest service , which returns a list of simulated photo data:

[
  {
    "albumid": 1,
    "id": 1,
    "title": "accusamus beatae ad facilis cum similique qui sunt",
    "url": "http://placehold.it/600/92c952",
    "thumbnailurl": "http://placehold.it/150/92c952"
  },
  {
    "albumid": 1,
    "id": 2,
    "title": "reprehenderit est deserunt velit ipsam",
    "url": "http://placehold.it/600/771796",
    "thumbnailurl": "http://placehold.it/150/771796"
  },
  {
    "albumid": 1,
    "id": 3,
    "title": "officia porro iure quia iusto qui ipsa ut modi",
    "url": "http://placehold.it/600/24f355",
    "thumbnailurl": "http://placehold.it/150/24f355"
  },
  ...
]

each record contains a photo id, album id, and title, as well as urls for both thumbnail and full-size images; for example:

photo class

a class representing this data might be defined as follows:

class photo: nsobject {
    var id: int = 0
    var albumid: int = 0
    var title: string?
    var url: url?
    var thumbnailurl: url?

    init(dictionary: [string: any]) {
        super.init()

        setvaluesforkeys(dictionary)
    }

    override func setvalue(_ value: any?, forkey key: string) {
        switch key {
        case #keypath(url):
            url = url(string: value as! string)

        case #keypath(thumbnailurl):
            thumbnailurl = url(string: value as! string)

        default:
            super.setvalue(value, forkey: key)
        }
    }
}

since instances of this class will be populated using dictionary values returned by the web service, an initializer that takes a dictionary argument is provided. the setvaluesforkeys(_:) method of nsobject is used to map dictionary entries to property values. the id and title properties are handled automatically by this method; the class overrides setvalue(_:forkey:) to convert the urls from strings into actual url instances.

view controller class

a basic user interface for displaying service results in a table view is shown below:

row data is stored in an array of photo instances. previously loaded thumbnail images are stored in a dictionary that associates uiimage instances with photo ids:

class viewcontroller: uitableviewcontroller {
    // row data
    var photos: [photo]!

    // image cache
    var thumbnailimages: [int: uiimage] = [:]

    ...    
}

the photo list is loaded the first time the view appears. the wswebserviceproxy class of the open-source http-rpc library is used to retrieve the data. if the call succeeds, the response data (an array of dictionary objects) is transformed into an array of photo objects and the table view is refreshed. otherwise, an error message is displayed:

override func viewwillappear(_ animated: bool) {
    super.viewwillappear(animated)

    // load photo data
    if (photos == nil) {
        let serviceproxy = wswebserviceproxy(session: urlsession.shared, serverurl: url(string: "https://jsonplaceholder.typicode.com")!)

        serviceproxy.invoke("get", path: "/photos") { result, error in
            if (error == nil) {
                let photos = result as! [[string: any]]

                self.photos = photos.map({
                    return photo(dictionary: $0)
                })

                self.tableview.reloaddata()
            } else {
                let alertcontroller = uialertcontroller(title: "error", message: error!.localizeddescription, preferredstyle: .alert)

                alertcontroller.addaction(uialertaction(title: "ok", style: .default))

                self.present(alertcontroller, animated: true)
            }
        }
    }
}

cell content is generated as follows. the corresponding photo instance is retrieved from the photos array and used to configure the cell:

override func tableview(_ tableview: uitableview, numberofrowsinsection section: int) -> int {
    return (photos == nil) ? 0 : photos.count
}

override func tableview(_ tableview: uitableview, cellforrowat indexpath: indexpath) -> uitableviewcell {
    var cell = tableview.dequeuereusablecell(withidentifier: cellidentifier)

    if (cell == nil) {
        cell = uitableviewcell(style: .default, reuseidentifier: cellidentifier)
    }

    let photo = photos[indexpath.row];

    cell!.textlabel!.text = photo.title

    // attempt to load image from cache
    cell!.imageview!.image = thumbnailimages[photo.id]

    if (cell!.imageview!.image == nil && photo.thumbnailurl != nil) {
        // image was not found in cache; load it from the server

        ...
    }

    return cell!
}

if the thumbnail image is already available in the cache, it is used to populate the cell's image view. otherwise, it is loaded from the server and added to the cache as shown below. if the cell is still visible when the image request returns, it is updated immediately and reloaded:

let serverurl = url(string: string(format: "%@://%@", photo.thumbnailurl!.scheme!, photo.thumbnailurl!.host!))!

let serviceproxy = wswebserviceproxy(session: urlsession.shared, serverurl: serverurl)

serviceproxy.invoke("get", path: photo.thumbnailurl!.path) { result, error in
    if (error == nil) {
        // if cell is still visible, update image and reload row
        let cell = tableview.cellforrow(at: indexpath)

        if (cell != nil) {
            if let thumbnailimage = result as? uiimage {
                self.thumbnailimages[photo.id] = thumbnailimage

                cell!.imageview!.image = thumbnailimage

                tableview.reloadrows(at: [indexpath], with: .none)
            }
        }
    } else {
        print(error!.localizeddescription)
    }
}

finally, if the system is running low on memory, the image cache is cleared:

override func didreceivememorywarning() {
    super.didreceivememorywarning()

    // clear image cache
    thumbnailimages.removeall()
}

summary

this article provided an overview of how images can be dynamically loaded as needed to populate table view cells in ios. for more ways to simplify ios app development, please see my projects on github:

  • markupkit
  • http-rpc
Database

Published at DZone with permission of Greg Brown, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • A Better Web3 Experience: Account Abstraction From Flow (Part 2)
  • Training a Handwritten Digits Classifier in Pytorch With Apache Cassandra Database
  • Mastering Persistence: Why the Persistence Layer Is Crucial for Modern Java Applications
  • .NET Performance Optimization Techniques for Expert Developers

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: