2

I created a Swift UITableView of posts, each post including some text and a chart image. The image is loaded asynchronously using SDWebImage and Firebase. Images have different heights, but a fixed width.

Here is a short video showing the display issue : https://youtu.be/QzQFT2z0GjA

Some cells are not displayed correctly the first time, but look perfect after some scrolling. I read about using layoutIfNeeded and setNeedsLayout as suggested in this post or iOS 11 UITableViewAutomaticDimension but it does not seem to work in my case.

Here is my code :

var postArray : [Post] = [Post]()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    postTableView.delegate = self
    postTableView.dataSource = self
    aLaUneWidthConstraint.constant = view.frame.size.width/2

    etatFranceWidthConstraint.constant = view.frame.size.width/2
    postTableView.register(UINib(nibName:"TableViewCell", bundle: nil), forCellReuseIdentifier: "postCell")



    activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
    activityIndicator.startAnimating()

    retrievePosts()

}

override func viewWillAppear(_ animated: Bool) {
    self.navigationController?.isNavigationBarHidden = true
}

func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! TableViewCell

    tableView.separatorStyle = UITableViewCellSeparatorStyle.none
    cell.selectionStyle = UITableViewCellSelectionStyle.none

    cell.postTitle.text = postArray[indexPath.row].title
    cell.postSource.text = postArray[indexPath.row].source
    cell.postChart.sd_setImage(with: URL(string: postArray[indexPath.row].chartURL!), placeholderImage: UIImage(named: "placeholder.png")) { (image, error, cache, url) in
        cell.chartHeightConstraint.constant = ((cell.postChart.image?.size.height)!/2)
        cell.setNeedsLayout()
        cell.layoutIfNeeded()
    }

    return cell

}

func retrievePosts() {

    let postDB = Database.database().reference().child("Posts")

    postDB.observe(.childAdded, with: { (snapshot) in

        let snapshotValue = snapshot.value as! NSDictionary

        let title = snapshotValue["title"] as! String
        let source = snapshotValue["source"] as! String
        let chartURL = snapshotValue["chartURL"] as! String
        let category = snapshotValue["category"] as! String

        let post = Post(data: snapshotValue)
        post.title = title
        post.source = source
        post.chartURL = chartURL
        post.category = category

        self.postArray.append(post)

        self.activityIndicator.stopAnimating()
        self.activityView.isHidden = true
        self.activityView.frame.size.height = 0
        self.postTableView.reloadData()

    })

}

Any idea? Thanks in advance.

  • Possible duplicate of [Dynamic image height in tableview with using sdwebimage](https://stackoverflow.com/questions/45483639/dynamic-image-height-in-tableview-with-using-sdwebimage) – t4nhpt Jan 02 '18 at 09:21
  • Problem is your autolayout. Just teamviewer, I can help you fix this – motbantuangiauten Jan 22 '18 at 09:53

2 Answers2

0

The solution consists in adding chartWidth and chartHeight params in the Post object and adding their values for each post in the Firebase database, and then setting some constraints to precalculate the cell height before the image is downloaded.

In TableViewCell.swift add :

func setChartSize(size: CGSize) {
    postChart.removeConstraint(postChartRatioConstraint)

    postChartRatioConstraint = NSLayoutConstraint(
        item: postChart,
        attribute: .height,
        relatedBy: .equal,
        toItem: postChart,
        attribute: .width,
        multiplier: (size.height / size.width),
        constant: 0)

    postChart.addConstraint(postChartRatioConstraint)
}

In ViewController use the setChartSize function :

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! TableViewCell

    tableView.separatorStyle = UITableViewCellSeparatorStyle.none
    cell.selectionStyle = UITableViewCellSelectionStyle.none

    let post = postArray[indexPath.row]

    cell.postTitle.text = post.title
    cell.postSource.text = post.source

    cell.postChart.sd_setShowActivityIndicatorView(true)
    cell.postChart.sd_setIndicatorStyle(.white)

    cell.setChartSize(size: CGSize(width: post.chartWidth!, height: post.chartHeight!))
    cell.postChart.sd_setImage(
        with: URL(string: post.chartURL!),
        placeholderImage: UIImage(named: "placeholder.png"),
        options: [.continueInBackground],
        completed: nil)

    return cell
}

Any other option like resizing the cell after the chart is downloaded generated scrolling jumps.

-1

Swift 5+ IOS 13 Xcode 11.2.1 + Amazing Answer https://stackoverflow.com/a/58832555/6881070

override func viewDidAppear(_ animated: Bool) {
    //  self.tablev.frame.origin.y = vnavigation.frame.maxY + 5
    //   tablevheight = self.tablev.frame.size.height
    self.bgview.backgroundColor = UIColor.init(patternImage: UIImage(named: "chat_bg.png")!)
    self.registerForKeyboardNotifications()
    UIView.performWithoutAnimation {
        tablev.beginUpdates()
        tablev.endUpdates()
    }
}
Shakeel Ahmed
  • 5,361
  • 1
  • 43
  • 34