Search code examples
swiftuitableviewautolayout

Height UIImage asynchronously in UITableview with autolayout


I am showing an image in a UITableView, the image will have different sizes and it is downloaded via JSON, with the following code the image is shown correctly scaled but there are some large spaces up and down (green color). How could I eliminate those spaces?

The image has the constraints top, bottom, left and right to 0 to be able to use the autolayout

import UIKit

class ViewController: UIViewController, UITableViewDataSource {

    struct JsonSosTalleres : Codable {
        let titulo : String
        let image : String
    }

    private var datosOfertas = [JsonSosTalleres]()

    class ImageLoader {

        var cache = NSCache<AnyObject, AnyObject>()

        class var sharedInstance : ImageLoader {
            struct Static {
                static let instance : ImageLoader = ImageLoader()
            }
            return Static.instance
        }

        func imageForUrl(urlString: String, completionHandler:@escaping (_ image: UIImage?, _ url: String) -> ()) {
            let data: NSData? = self.cache.object(forKey: urlString as AnyObject) as? NSData

            if let imageData = data {
                let image = UIImage(data: imageData as Data)
                DispatchQueue.main.async {
                    completionHandler(image, urlString)
                }
                return
            }

            let downloadTask: URLSessionDataTask = URLSession.shared.dataTask(with: URL.init(string: urlString)!) { (data, response, error) in
                if error == nil {
                    if data != nil {
                        let image = UIImage.init(data: data!)
                        self.cache.setObject(data! as AnyObject, forKey: urlString as AnyObject)
                        DispatchQueue.main.async {
                            completionHandler(image, urlString)
                        }
                    }
                } else {
                    completionHandler(nil, urlString)
                }
            }
            downloadTask.resume()
        }
    }



    @IBOutlet weak var tabla: UITableView!
    override func viewDidLoad() {
        super.viewDidLoad()

        downloadJson()


        tabla.estimatedRowHeight = 40.0

        tabla.rowHeight = UITableViewAutomaticDimension

        tabla.tableFooterView = UIView()

    }








    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return datosOfertas.count
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tabla.dequeueReusableCell(withIdentifier: "ofertacell") as? TableViewCell else { return UITableViewCell() }


        let fimage = datosOfertas[indexPath.row].image
        ImageLoader.sharedInstance.imageForUrl(urlString: fimage, completionHandler: { (image, url) in
            if image != nil {


               tableView.beginUpdates()

                cell.imagen.image = image

                tableView.endUpdates()


            }
        })



        return cell
    }




    // download  json
    let url = URL(string: "http://sostalleres.com/test.json")

    func downloadJson() {
        guard let downloadURL = url else { return }
        URLSession.shared.dataTask(with: downloadURL) { data, urlResponse, error in
            guard let data = data, error == nil, urlResponse != nil else {
                print("something is wrong")
                return
            }
            print("downloaded")
            do
            {
                let decoder = JSONDecoder()
                self.datosOfertas = [try decoder.decode(JsonSosTalleres.self, from: data)]

                DispatchQueue.main.async {

                            self.tabla.reloadData()

                }

            } catch {
                print("something wrong after downloaded")
            }
            }.resume()
    }





}

and in TableViewCell.swift :

class TableViewCell: UITableViewCell {

    @IBOutlet weak var imagen: UIImageView!



    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

View image

I would appreciate if you can guide me in any solution.


Solution

  • You have to calculate the new height and width and with them apply the scale and use tableView.beginUpdates() and tableView.endUpdates(), with the following code you should work without problem.

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            guard let cell = tabla.dequeueReusableCell(withIdentifier: "ofertacell") as? TableViewCell else { return UITableViewCell() }
    
    
            let fimage = datosOfertas[indexPath.row].image
            ImageLoader.sharedInstance.imageForUrl(urlString: encoded!, completionHandler: { (image, url) in
                if image != nil {
    
    
                    let newWidth = self.self.imagenDetalle.frame.width
                    let scale = newWidth/(image?.size.width)!
                    let newHeight = (image?.size.height)! * scale
    
    
    
                            tableView.beginUpdates()
    
                            UIGraphicsBeginImageContext(CGSize(width:newWidth, height:newHeight))
                            image?.draw(in: CGRect(x:0, y:0, width:newWidth, height:newHeight))
                            let newImage = UIGraphicsGetImageFromCurrentImageContext()
                            UIGraphicsEndImageContext()
                            cell.imagen.image = newImage
    
                            tableView.endUpdates()
    
    
                }
            })
    
    
    
            return cell
        }
    

    happy coding!