0

Why does the function heightForRowAt always find the array containing row heights which is initially set to nil to sill be nil even after reloading the rows.

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Reuse", for: indexPath) as! TableViewCell

    let downloadURL = URL(string: self.imageURLS[indexPath.row])


    if cell.cellImageView.image == nil{
        URLSession.shared.dataTask(with: downloadURL!) { (data, _, _) in
            if let data = data {
                let image = UIImage(data: data)
                DispatchQueue.main.async {
                    cell.setCellImage(image:image!)
                    self.rowHeights?.insert((cell.imageView?.frame.height)!, at: indexPath.row)
                    tableView.reloadRows(at: [indexPath], with: .top)
                }
            }
            }.resume()
    }
    return cell
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    guard let rowHeights = self.rowHeights else{
        print("returned nil")
        return 500.0
    }
    print("the array exists at \(indexPath.row) with value: \(rowHeights[indexPath.row])")
    return rowHeights[indexPath.row]
}
}

UPDATED TableViewController

import UIKit
import Firebase
import FirebaseStorageUI

class TableViewController: UITableViewController {
var images:[UIImage]! = [#imageLiteral(resourceName: "rininger_2.jpg")]
var imageURLS:[String] = [String]()
var rowHeights:[CGFloat] = [CGFloat]()
var listener:ListenerRegistration?

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.dataSource = self
    tableView.delegate = self
    listener = Firestore.firestore().collection("Posts").addSnapshotListener{
        querySnapshot, error in
        guard let snapshot = querySnapshot else {
            print("Error fetching snapshots: \(error!)")
            return
        }
        snapshot.documentChanges.forEach { diff in
            if (diff.type == .added) {
                print("New data: \(diff.document.data())")
            }
            if (diff.type == .modified) {
                print("Modified data: \(diff.document.data())")
            }
            if (diff.type == .removed) {
                print("Removed data: \(diff.document.data())")
            }

            guard let newImageURL = diff.document.data()["imageDownloadURL"] as? String else{
                print("Failed to get image download URL")
                return
            }
            print("downloadURL: \(newImageURL)")
            self.imageURLS.insert(newImageURL, at: 0)
            let indexPath = IndexPath(row: 0, section: 0)
            self.tableView.insertRows(at: [indexPath], with: .top)


        }

    }
    tableView.estimatedRowHeight = 100.0
    tableView.rowHeight = UITableViewAutomaticDimension
}


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

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


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Reuse", for: indexPath) as! TableViewCell

    let downloadURL = URL(string: self.imageURLS[indexPath.row])


    if cell.cellImageView.image == nil{
        URLSession.shared.dataTask(with: downloadURL!) { (data, _, _) in
            if let data = data {
                let image = UIImage(data: data)
                self.images.insert(image, at: indexPath.row)
                DispatchQueue.main.async {
                    cell.setCellImage(image:image!)
                   // self.rowHeights.insert((cell.imageView?.frame.height)!, at: indexPath.row)
                   //tableView.reloadData()
                   tableView.reloadRows(at: [indexPath], with: .top)
                }
            }
            }.resume()
    }
    return cell
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    print("UITableViewAutomaticDimension: \(UITableViewAutomaticDimension)")
    return UITableViewAutomaticDimension
}

}

user372382
  • 183
  • 9
  • How do you declare it ? – Shehata Gamal Jan 15 '18 at 18:13
  • @Sh_Khan It is declared at the top of the `UITableViewController` class as `var rowHeights:[CGFloat]? = nil` and is not used outside of `cellForRowAt` or `heightForRowAt` – user372382 Jan 15 '18 at 18:15
  • must b like that var rowHeights = [CGFloat]() – Shehata Gamal Jan 15 '18 at 18:17
  • Why post yet another question (8 now in two days) instead of updating your [previous question](https://stackoverflow.com/questions/48257139/what-is-the-best-approach-to-resize-rows-in-a-table-view)? – rmaddy Jan 15 '18 at 18:17
  • Note that if the second image load first, you may have a crash... (see explaination there: https://stackoverflow.com/questions/48170210/how-to-avoid-mistakes-index-out-of-range/48170612#48170612 – Larme Jan 15 '18 at 18:17
  • @larme I am getting that error, how do you work around it, do I need to index the array differently – user372382 Jan 15 '18 at 18:26
  • You can do `var rowHeight:[IndexPath:CGFloat]()` and `if let height = self.rowHeight[indexPath]{ return height }else {return 500.0}` – Larme Jan 15 '18 at 18:38
  • I don't know your code for `prepareReuse()`, but `if cell.cellImageView.image == nil` might fail because of the reuse... – Larme Jan 15 '18 at 18:40
  • @Larme I haven't implemented the `prepareReuse()` function. What the advantage to doing an `if let` statement over a `guard let` statement inside of `heightForRowAt` – user372382 Jan 15 '18 at 18:45

2 Answers2

0

You have massive problems in your code

1- first you must download , save image and then reload the table

2- second you should use automatic table view height to accomplish that instead of array of heights like what you're currently do

solve 1

   NSString *getImagePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.jpeg",n1.Id]];

    if([[NSFileManager defaultManager] fileExistsAtPath:getImagePath])
    {
        UIImage *img = [UIImage imageWithContentsOfFile:getImagePath];
        cell.leftImageV.image =img;
        [cell.activity stopAnimating];
        cell.activity.hidden=YES;
    }
    else
    {

        [cell.activity startAnimating];
         cell.activity.hidden=NO;

        cell.leftImageV.image = nil;

        NSLog(@"xdasdxsadxa %@",n1.mainImgStr);

        dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

        dispatch_async(concurrentQueue, ^{
            __block NSData * imageData  = nil;
            dispatch_sync(concurrentQueue, ^{ 

                imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:n1.mainImgStr]];

                //Add the file name
                [imageData writeToFile:getImagePath atomically:YES]; //Write the file

            });

            dispatch_sync(dispatch_get_main_queue(), ^{

                if(imageData)
                { 
                    [self.tableView reloadData];

                } 
            });
        }); 

Solve 2

To set automatic dimension for row height & estimated row height, ensure following steps to make, auto dimension effective for cell/row height layout. I just tested following steps and code and works fine.

  • Assign and implement tableview dataSource and delegate
  • Assign UITableViewAutomaticDimension to rowHeight & estimatedRowHeight
  • Implement delegate/dataSource methods (i.e. heightForRowAt and return a value UITableViewAutomaticDimension to it)

-

@IBOutlet weak var table: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Don't forget to set dataSource and delegate for table
    table.dataSource = self
    table.delegate = self

    // Set automatic dimensions for row height
    table.rowHeight = UITableViewAutomaticDimension
    table.estimatedRowHeight = UITableViewAutomaticDimension
}



// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
  • After I download the image and configure the cell how should I save the cell. Do I use another array. Also where should I put the UIAutomaticDimension, when I tried that originally I wound up with all the rowHeights being the same size as the `estimatedRowHeight` – user372382 Jan 15 '18 at 18:23
  • you must save the image not the cell , hook aspect ratio constraint of the cell image and change it according the retrieved image size (width/height) in cellForRow – Shehata Gamal Jan 15 '18 at 18:28
  • Ive added the updated TableViewController with some of the edits, I added an array to save the images and used UITableViewAutomaticDimension to set the rowHeights but its still loading images with spaces around them and loading the same row multiple times. Did I implement the edits correctly? – user372382 Jan 15 '18 at 18:42
  • cache images in files as when you out of that viewcontroller not be have to download them again – Shehata Gamal Jan 15 '18 at 18:47
0

Make sure that the rowHeights array has been initialized before you add values to it For example:

cell.setCellImage(image:image!)

if self.rowHeights == nil{ rowHeights = [CGFloat]() }

self.rowHeights?.insert((cell.imageView?.frame.height)!, at: indexPath.row)
osebas15
  • 61
  • 6