0

I am having a header named CollapsibleTableViewHeader and its height is dynamic based on the UILabels and UIViews contained inside it.

So, I have this function below -

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

        let header = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("header") as! CollapsibleTableViewHeader

        switch indexPath.row {
        case 0:
            if sections[indexPath.section].collapsed == true {
                return 0
            }
            else {

              return header.iconsViewHeightConstraint.constant + header.shortDescHeightConstraint.constant + header.validityDateHeightConstraint.constant + header.expiryAlertHeightConstraint.constant
            }

        default:
            return 0
        }

    }

In my else condition, I need to return the height of my header as the height of my row. So I added up all the elements inside my header and returning the value (CGFloat).

The height is returned as expected but the problem is the same height is applied to all the cells. For e.g. if the height value returned is 150.0, then the same 150.0 is being applied to all the cells out there irrespective of their individual heights.

How do I get the heights specific to each cell? indexPath is one critical thing to use, but I am not sure how to use it here.

Apologies, if the question is dumb! Please help

PS: I tried automaticDimension already but that doesn't help in anyway since I have these cells as collapse/expandable cells.

EDIT 1: Added viewForHeaderInSection code

// Header
    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

        let header = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("header") as! CollapsibleTableViewHeader

        if sections.count == 0 {
            self.tableView.userInteractionEnabled = false
            header.cornerRadiusView.layer.borderWidth = 0.0
            header.benefitAlertImage.hidden = true
            header.benefitAlertText.hidden = true
            header.amountLabel.hidden = true            
        }
        else {
            header.amountLabel.hidden = false
            header.cornerRadiusView.layer.borderWidth = 1.0
            self.tableView.userInteractionEnabled = true
            header.titleLabel.text = sections[section].name
            header.arrowImage.image = UIImage(named: "voucherDownArrow")
            header.setCollapsed(sections[section].collapsed)

            header.benefitDetailText2.text = sections[section].shortDesc
            header.benefitDetailText3.text = sections[section].untilDate

            header.section = section
            header.delegate = self

            if sections[section].collapsed == true {
                header.benefitAlertImage.hidden = true
                header.benefitAlertText.hidden = true
                header.commerceIconsContainerView.hidden = true

                for i in 0..<imagesArray.count {
                    imagesArray[i].image = UIImage(named: "")
                }

            }
            else {
                header.commerceIconsContainerView.hidden = false
                 if sections[section].items.count > 5 {
                    header.iconsViewHeightConstraint.constant = 74.0
                 }

                else {
                    header.iconsViewHeightConstraint.constant = 38.0
                }


                if sections[section].isNearExpiration == true {                 
                    header.benefitAlertImage.hidden = false
                    header.benefitAlertText.hidden = false
                }
                else {                  
                    header.benefitAlertImage.hidden = true
                    header.benefitAlertText.hidden = true
                }
            }
        }

        return header
    }
Amal T S
  • 3,327
  • 2
  • 24
  • 57
Lohith Korupolu
  • 1,066
  • 1
  • 18
  • 52
  • I actually don't understand why UITableViewAutomaticDimension didn't help you because it looks like a classic scenario. care to elaborate ? – OhadM Jun 29 '17 at 14:57
  • @OhadM: i am not aware why. The first thing I tried was automaticDimension but I always got a static height. – Lohith Korupolu Jun 29 '17 at 14:58
  • So you didn't implement that as it should be. Please tell me what have you tried ? Also, please provide a screen-shot of your autolayout view that you need it to be dynamic height. – OhadM Jun 29 '17 at 14:59
  • I stopped using heightForRowAtIndexPath, and heightForHeaderInSection. In my viewDidLoad, I added estimatedRowHeight and estimatedHeightForHeader – Lohith Korupolu Jun 29 '17 at 15:05
  • You don't use estimated height on constant height cells only dynamic and your header ISN'T dynamic. it is, constant. The only thing that is dynamic is your cells that would appear inside your header after a user will tap on that header, correct ? – OhadM Jun 30 '17 at 10:33
  • Also, before writing the answer you need, did you read that: https://stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights?rq=1 – OhadM Jun 30 '17 at 10:36

2 Answers2

1

You need to store heights for each cell that you calculated in cellForRow method into an array, and then load then in heightForRowAtIndexPath function

For example:

var heights = [Int]()

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as!  Cell
    heights.append(calculationResult)
    return cell
}

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return heights[indexPath.row]
}
Fangming
  • 24,551
  • 6
  • 100
  • 90
  • All the UI elements are in my section header, not the custom cell :| I had to do it this way due to a design problem where there needs to be border surrounding the whole section. – Lohith Korupolu Jun 29 '17 at 14:28
  • I just tried with your suggestion but appended heights in viewForHeaderInSection. Correct me if I am wrong. And I get an error index out of range – Lohith Korupolu Jun 29 '17 at 14:30
  • @LohithKorupolu You used custom header view? Can you please post that part of code so that I can have a look? – Fangming Jun 29 '17 at 14:35
  • I updated my question with code. The heights of UILabels differ with dynamic content. And so the need of calculating the heights – Lohith Korupolu Jun 29 '17 at 14:41
1

I'm not clear on what you're trying to do. You want to determine what the height of the header should be based on the elements inside it? If so, then you are using the wrong function. You should be using:

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 
    if (isSectionCollapsed) {
        return 0
    } else {
         let sum = subView1.height + subView2.height + subView3.height
         return sum
    }
 }

Thats why all your rows are getting their height set to 150. If you are trying to hide rows if a section is collapsed, then you would also need a similar check for the rows in the function you listed:

override func tableView(_ tableView: UITableView, heightforRowatIndexPath section: Int) -> CGFloat {
    if (isSectionCollapsed) {
        return 0
    } else {
         // return row height for this tableViewCell
    }
 }

EDIT: Just saw your edit, and still think it's the same issue. That function is creating a header view, and the height of that will be returned from the ViewController by a call to heightForHeaderInSection, not heightforRowatIndexPath.

angusc
  • 401
  • 3
  • 10
  • I think I understand the issue. Trying to make changes to the code. – Lohith Korupolu Jun 29 '17 at 14:49
  • Just read comment about borders around section. Does that include borders on the side or just the top & bottom of section? – angusc Jun 29 '17 at 14:54
  • So now, in heightForHeaderInSection, when I am checking collapsed status of each of my "sections", I get index out of range error. Is this because the sections count is not ready yet? I am confused – Lohith Korupolu Jun 29 '17 at 14:57
  • When are you setting the sections[].collapsed? I'm assuming it's a variable in the ViewController? If so, you should initialize it in viewDidLoad – angusc Jun 29 '17 at 15:00
  • Also, where is the UI element that is controlling if a section is collapsed or not? – angusc Jun 29 '17 at 15:02
  • I am doing that in viewWillAppear. These values are fetched from a call to REST service. – Lohith Korupolu Jun 29 '17 at 15:02
  • the UI element is an arrow, that is also within the same section header. Like mentioned earlier, I put everything in my section header for a border design :( – Lohith Korupolu Jun 29 '17 at 15:03
  • If the show/hide arrow is in the section header, then you don't want to hide the section header (as then you could never expand it) – angusc Jun 29 '17 at 15:05
  • Sorry, it's not just the arrow, but the section header itself. – Lohith Korupolu Jun 29 '17 at 15:06
  • Are there TableViewCells after the section header, or are you putting all rows of data into the section header? – angusc Jun 29 '17 at 15:08
  • My full code is in this question - https://stackoverflow.com/questions/44718598/swift-tableview-row-height-updates-only-after-scrolling-or-toggle-expand-colla with only modifications - that I already posted in the current question here – Lohith Korupolu Jun 29 '17 at 15:09
  • k, going to look at other post – angusc Jun 29 '17 at 15:13
  • K, what I think you're trying to do should work, but since you're only using headers you will need to use heightForHeaderInSection. Just need to make sure that the collapsed settings are initialized before you start displaying headers (viewWillAppear should work). – angusc Jun 29 '17 at 15:19
  • Another way to go completely would be use rows like they were intended, and have them grow/shrink when they are selected. Might be more changes than you want to do right now though – angusc Jun 29 '17 at 15:21
  • That gives me a lot of strength! I've been struggling a lot with this. I wasn't given a chance to compromise on design. – Lohith Korupolu Jun 29 '17 at 15:21
  • Well, now - I don't think I have time in my hands. I will only try with the current approach. – Lohith Korupolu Jun 29 '17 at 15:22
  • One last question, how would I get the row height when isCollapsed is false? Or the other way, when I expand my header, how can I set the row height with expanded height value? – Lohith Korupolu Jun 29 '17 at 15:52
  • Something just occurred to me, the one-time initialization you want for the view should be in viewDidLoad. That's loaded into memory, not loaded on screen. It happens before viewWillAppear, which happens every time the view will be displayed. – angusc Jun 29 '17 at 17:25
  • if isCollapsed is false, the row is full height, so you'd have to add up all the element heights in the view. If isCollapsed is true, this is probably a set number, like the default elements you always want to show. 44 is standard. – angusc Jun 29 '17 at 17:26
  • yes, I changed my intialization to viewDidLoad. and added a condition to check whether my array (sections) is not 0. I get the data populated now but still have the height issue – Lohith Korupolu Jun 30 '17 at 05:59
  • 1
    I posted a full working solution on the other question. Try and see if that is what you want. – angusc Jun 30 '17 at 07:15
  • I just downloaded the sample project from github, and that's what I am looking for. Great effort put in!. At this time, it's very difficult for me to make all those changes :( how Can I send you my latest privately? I can't add the screenshots/GIF recording since it has organization related info – Lohith Korupolu Jun 30 '17 at 07:31
  • One problem in the sample project I have though - the cell collapses when I tap on the expanded part also :( – Lohith Korupolu Jun 30 '17 at 07:33
  • 1
    Yes, it's set to the entire cell right now, but it would be easy to get the location of the click and only react if it was in the top x number of pixels. I've seen examples of that here in the past. – angusc Jun 30 '17 at 07:40
  • I got this working with the logic of summing up heights of UI elements and add them. Also I am calculating the heights with a String extension – Lohith Korupolu Jul 03 '17 at 06:30
  • Glad it's working. I think that's considered the harder way, and what AutoDimension was supposed to replace. – angusc Jul 04 '17 at 13:30