0

Simple: I want the size of the picture with width of the screen and the height calculated to keep the aspect ratio, similar to 9gag app.

How can I make dynamic height of cell in PFQueryTableViewController, in my cell I have one label and PFImageView in a Custom cell and loading works fine, but the pictures are not changing the height of the cell and all of the cells have the same height. I am stuck on this problem for third day and it looks like error from the parse.com framework. I was able to change the cell height when I was working with UITableViewController but the parse framework ignores it.

I am using storyboard and tried that with autolayout constraint and without it. TableViewController.swift

import UIKit
import Parse
import ParseUI
import Bolts
class TableViewController: PFQueryTableViewController {

// Initialise the PFQueryTable tableview
override init(style: UITableViewStyle, className: String!) {
    super.init(style: style, className: className)        
}

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    // Configure the PFQueryTableView
    self.pullToRefreshEnabled = true
    self.paginationEnabled = false

}

// Define the query that will provide the data for the table view
override func queryForTable() -> PFQuery {

    // Start the query object
    var query = PFQuery(className: "Image")
    query.whereKey("deleted", equalTo: 0)
    query.orderByDescending("createdAt")
    return query
}

//override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
    var cell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as! CustomTableViewCell!
    if cell == nil {
        cell = CustomTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "CustomCell")
    }

    // Extract values from the PFObject to display in the table cell
    if let name = object?["caption"] as? String{
        cell.postHeadlineLabel.text = name
    }

    // display initial image
    var initialThumbnail = UIImage(named: "question")
    cell.postImageView.image = initialThumbnail
    // extract image
    if let thumbnail = object?["image"] as? PFFile {
        cell.postImageView.file = thumbnail
        cell.postImageView.loadInBackground()
    }
    cell.postImageView.contentMode = UIViewContentMode.ScaleAspectFit
    cell.postImageView.clipsToBounds = true

    cell.actualWidth = cell.postImageView.image!.size.width
    cell.actualHeight = cell.postImageView.image!.size.height

    return cell
}


    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        var cell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as! CustomTableViewCell!
        let aspect = cell.actualWidth / cell.actualHeight
        var height: CGFloat = cell.postImageView!.frame.size.width * aspect
        return height
    }
}

CustomTableViewCell.swift

import UIKit
import Parse
import ParseUI
class CustomTableViewCell: PFTableViewCell {
 var actualHeight: CGFloat = 10
 var actualWidth: CGFloat = 10
 @IBOutlet weak var postHeadlineLabel: UILabel!
 @IBOutlet weak var postImageView: PFImageView!
}

I found some advices online but it doesnt work and seems like error of the parse framework with iOS8 I tried

cell.postImageView.contentMode = UIViewContentMode.ScaleAspectFit
cell.postImageView.clipsToBounds = true
cell.sendSubviewToBack(cell.postImageView)

or

override func awakeFromNib() {
    self.setTranslatesAutoresizingMaskIntoConstraints(false)
}

Update - parse DB: https://www.anony.ws/image/D6tp

Mazel Tov
  • 2,064
  • 14
  • 26

2 Answers2

0

PFQueryTableViewController inherits from UITableViewController, which means it conforms to the UITableViewDelegate protocol. Inside here there is a method that you can override called:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat

In here you should return a specific CGFloat (for example 50.0f for 50px or something else) depending on the size of that specific cell given by the indexPath.


UPDATE 1:

OP provided more code to elaborate the issue


Since you are calling tableView.dequeueReusableCellWithIdentifier in the tableView:heightForRowAtIndexPath, then it means that once you've got one cell with an image ready, then it'll keep using this aspect ratio over and over. There is no logic in your tableView.dequeueReusableCellWithIdentifier which is tied to the unique object at the provided indexPath.

One way to overcome this could be by calling objectAtIndexPath(indexPath), which is a method from PFQueryTableViewController. This will give you access to the PFObject and PFFile at the specified cell. The downside to this is that you'll have to download the image again, thus using a lot of band-width. It will also slow down your app since you'll have to wait for it to download.

You could also make use of tableView.cellForRowAtIndexPath(indexPath) to get something unique for that cell, but unfortunately tableView:heightForRowAtIndexPath is called before tableView:cellForRowAtIndexPath in the PFQueryTableViewController which will cause the app to crash.


My best suggestion is to add another field to the database for your Image class which stores the aspect ratio of the image.

If this is not possible since you already have a large database or some other good reason, then I would recommend for you to make a cloud function which can compute the aspect ratio.


UPDATE 2

Updated solution to answer


After sitting and playing around with it for a while, then I managed to come up with a solution for your issue. Unfortunately it is not pretty and I will still suggest you to store the aspect ratio in your DB schema as the most optimal solution.

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return calculateHeightForRowAtIndexPath(indexPath);
}

func calculateHeightForRowAtIndexPath(indexPath: NSIndexPath) -> CGFloat {
    if let file = objectAtIndexPath(indexPath)?["image"] as? PFFile {
        let image = UIImage(data: file.getData()!)
        let imageToScreenRatio = tableView.bounds.size.width / (image?.size.width)!
        return imageToScreenRatio * (image?.size.height)!
    }
    else {
        return 50.0
    }
}

The reason why this is really bad is because the image is being downloaded in the main UI thread using file.getData(). Instead file.getDataInBackgroundWithBlock(block) should be used, but this would cause problems since calculateHeightForRowAtIndexPath would then return before having downloaded the image.


UPDATE 3

OP provided more info on DB schema


Since you already have the ratio stored in the database, then you can calculate the cell height the following way:

func calculateHeightForRowAtIndexPath(indexPath: NSIndexPath) -> CGFloat {
    if let ratio = objectAtIndexPath(indexPath)?["aspect"] as? Float {
        return tableView.bounds.size.width / CGFloat(ratio)
    }
    else {
        return 50.0
    }
}
Kumuluzz
  • 2,012
  • 1
  • 13
  • 17
  • I tried that but it changes the cell height for every cell... I also tried to calculate the height: `override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { var cell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as! CustomTableViewCell! let aspect = cell.actualWidth / cell.actualHeight var height: CGFloat = cell.postImageView!.frame.size.width * aspect return height }` actualWidth var I did in cellForRowAtInd `cell.actualWidth = cell.postImageView.image!.size.width` – Mazel Tov Jun 07 '15 at 17:34
  • Could you elaborate more on this by editing your question and adding the code samples there? – Kumuluzz Jun 07 '15 at 17:55
  • update 2 looks good but unfortunately it crashes my app `Warning: A long-running operation is being executed on the main thread.` I also tried to do this and without parse framework it works perfect [Get UIImageView height to calculate cell height correctly](http://stackoverflow.com/questions/26041820/auto-layout-get-uiimageview-height-to-calculate-cell-height-correctly) – Mazel Tov Jun 08 '15 at 08:31
  • Yeah, update 2 most likely crashes the app since a network request is done in the main thread, which I also mentioned was very bad practice. Maybe I just got it working because I had a good internet connection. You are not able to store the aspect ratio inside of the `PFOject`? – Kumuluzz Jun 08 '15 at 08:47
  • I am able to store the aspect ration in the DB but I am still having problem getting that value and calculate the height. I am new to iOS programming and the parse.com looked very good but when it doesnt have the ability to resize the cell by its content it seems as useless crap. **BTW do you know if there is any other way how to resize the cells without downloading additional data from DB?** – Mazel Tov Jun 08 '15 at 09:52
  • 1
    Resizing the cell depending on its content has nothing to do with Parse, that's all iOS stuff, so take your anger out over Apple xD Still, once you've gotten the hang of it, then it'll be second nature. **Regarding downloading extra stuff**: If you already have the ratio stored in the `Image` schema, then it's automatically downloaded with the other data. Could you please update your question with a print screen of your database from Parse? – Kumuluzz Jun 08 '15 at 11:47
  • OK I updated the picture. Actually my app was working as I wanted without parse.com but I was using PHP and MySQL a lot and using parse seems that it will save a lot of time in future changes in DB etc... It seems that **PFQueryTableViewController** doesnt work with autolayout from storyboard... – Mazel Tov Jun 08 '15 at 13:52
  • 1
    This has nothing to do with auto layout, this is purely about the `UITableViewController`. In total it has around 40-50 delegate methods so it can be quite overwhelming to know which ones to use, but hopefully you have it working by now :) – Kumuluzz Jun 08 '15 at 15:09
  • I just found out that this solution doesnt work with pagination. I set `self.paginationEnabled = true` `self.objectsPerPage = 5` if the database has less than 5 records it works fine but with more occurences it keeps crashing... Do you have any idea why or how? – Mazel Tov Jun 08 '15 at 22:18
  • 1
    This is starting to become off topic from your original question `Dynamic cell height in PFQueryTableViewController with images` and more towards something like `PFQueryTableViewController with pagination enabled`. I will suggest you to create a new question, with code snippets of the relevant parts for this and then you can link to the question in these comments if you want me to take a look at it :) – Kumuluzz Jun 09 '15 at 05:43
  • 1
    I posted [new question](http://stackoverflow.com/questions/30730844/pfquerytableviewcontroller-pagination-doesnt-work-with-heightforrowatindexpath), any help will be appreciated – Mazel Tov Jun 09 '15 at 11:35
0

What you're implementing is a wrong approach. heightForRowAtIndexPath is called before cellForRowAtIndexPath. So, it will return wrong values in your method. Try to use Device width instead. I've made changes below as per my understanding of your requirement. Try it and make some changes accordingly and let me know if it worked..

In your heightForRowAtIndexPath, try changing

#define ASPECT_RATIO 0.5f
var height: CGFloat = UIScreen.mainScreen().bounds.size.width * ASPECT_RATIO

This one would make the height of the cell as the half of width of device. You can change the value of the aspect ratio according to your requirement. If you have label, you can add height of it in the height variable.

Tejvansh
  • 676
  • 4
  • 9
  • I tried that but was not able to compile. What I want is width of the image inside cell be the same width as the cell and the height calculated by aspect ration of the image - **every image will have the same width and the height will be dynamic** – Mazel Tov Jun 08 '15 at 08:36
  • For that your image should be downloaded before calculating height, so you can calculate the size of image. I've an idea for that. If you can store the size of image (or aspect ratio of image) in your PFClass, you could retrieve it and calculate height before hand. – Tejvansh Jun 08 '15 at 09:27