0

I have the following layout for UITableViewCell:

enter image description here

The layout consists of two subviews:

  • TopView (containing a Show button)
  • BottomView (which is expanded or collapsed when the Show button is pressed).

BottomView consists of 3 subviews. The constraints of these subviews are:

  • Each of the three subviews contains a UILabel that is pinned to the leading,top and trailing edges with a constant == 8.

  • The UILabel is pinned to the bottom of the UIView using a constraint that is >= 8. This forces the UILabel to be aligned to the top of the UIView.

  • The left-most of the three UIViews is pinned to the leading edge of BottomView.

  • The right-most of the three UIViews is pinned to the trailing edge of BottomView
  • Each of the three UIViews is pinned to the top of BottomView
  • Each of the three UIViews is pinned to the bottom of BottomView
  • The three views have equal widths and equal heights.
  • the bottom of BottomView is pinned to the the bottom of the UITableViewCell's Content View

This gives me my desired layout:

enter image description here

What I'd like to accomplish is the following:

  1. Initially, BottomView should be collapsed.
  2. Clicking on the Show button should expand or collapse the BottomView as appropriate.

I managed to do this by creating a bottomViewHeightConstraint that is initially uninstalled. Tapping on the show button activates/deactivates the constraint.

AnotherTableViewCell.m

-(IBAction) show
{
    self.bottomViewHeightConstraint.active = !self.bottomViewHeightConstraint.active;
     [UIView animateWithDuration:0.3 animations:^{
        [self layoutIfNeeded];
        [self.delegate cellRequiresUpdates:self];
    } completion:^(BOOL finished) {

    }];
}

UIViewController.m

-(void) cellRequiresUpdates:(AnotherTableViewCell *)cell
{
    [self.tableView beginUpdates];
    [self.tableView endUpdates];
}

This worked but produced a lot of warnings for unsatisfiable constraints. I'd like help in understanding which of my constraints are causing the warnings. The unsatisfiable constraints are:

  1. bottomViewHeightConstraint== 0

(needed because I want to collapse bottom view)

  1. Equal heights between the left-most and right most UIView in UIBottomView

(tried deactivating, but warning didn't go away)

  1. 8-pixel distance between the bottom of the leftmost UIView and the bottom of BottomView

(tried deactivating, but warning didn't go away)

  1. 8-pixel distance between the bottom of the leftmost UIView and the bottom of BottomView

(tried deactivating, but warning didn't go away)

W.K.S
  • 9,787
  • 15
  • 75
  • 122
  • Can you tell me the height of the expanded view is fixed or it will grow as per the size of the contents/text? – iUser Jun 23 '17 at 12:18
  • 1
    Take a look at my answer to this post - might get you on your way... https://stackoverflow.com/questions/43096231/expand-uilabel-inside-uitableview-with-more-button-like-instagram – DonMag Jun 23 '17 at 12:18
  • @iUser : Good Point: the height of the expanded view is not fixed. It is based on the intrinsicContentSize of the labels – W.K.S Jun 23 '17 at 12:28
  • Check out @DonMag answer. The perfect one for your question. – iUser Jun 23 '17 at 12:30
  • @DonMag thank you, yes, that is the approach i am using to update the height of the cells but my issue is how to resolve the warnings when I change the height of the BottomView. I am currently AFK, but I'll post some code to make the question clearer. – W.K.S Jun 23 '17 at 12:31
  • @W.K.S - ah, ok.. I've been able to resolve most "unsatisfiable constraint" warnings when working with different features like this. Do you have a stripped-down version of your project you could post? – DonMag Jun 23 '17 at 13:00
  • @DonMag. I'd really appreciate that. I hope you won't mind download the files: https://file.io/gwCQa4 – W.K.S Jun 23 '17 at 13:14
  • @W.K.S - whoops, that link returns `{"success":false,"error":404,"message":"Not Found"}` – DonMag Jun 23 '17 at 13:26
  • Sorry about that, try https://www.dropbox.com/s/4k6ec8gikxwb6ss/Experimental.zip?dl=0 – W.K.S Jun 23 '17 at 13:30
  • Taking a look... – DonMag Jun 23 '17 at 13:53
  • @W.K.S - I *think* I've got it... see my answer. – DonMag Jun 23 '17 at 14:31

2 Answers2

1

OK - this could solve your issue. Working on a similar recent question, I came across this in Apple's docs:

NOTE Don’t feel obligated to use all 1000 priority values. In fact, priorities should general cluster around the system-defined low (250), medium (500), high (750), and required (1000) priorities. You may need to make constraints that are one or two points higher or lower than these values, to help prevent ties. If you’re going much beyond that, you probably want to reexamine your layout’s logic.

Of course, when creating constraints in IB, they appear to use 1000 by default.

So, for your particular layout...

Select all the constraints on the Bottom View, except the Bottom View Height Constraint:

enter image description here

and change the Priority to 998

Then, select only the Bottom View Height Constraint and set its Priority to 999

Run your app and show/hide the bottom view to your heart's content :)

Ref: https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/AnatomyofaConstraint.html#//apple_ref/doc/uid/TP40010853-CH9-SW19

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Priorities! I hadn't even considered them! Thank you so much for taking the time to help; this is precisely the simple, elegant solution I knew I was missing – W.K.S Jun 23 '17 at 16:15
0

Some rough & ready code does what you're after -

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    var expanded: [Bool] = [false, false, false, false]


    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.delegate = self
        self.tableView.dataSource = self

        self.tableView.rowHeight = UITableViewAutomaticDimension;
        self.tableView.estimatedRowHeight = 64.0
    }



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

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

        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! ExpandingCell

        cell.isExpanded = self.expanded[indexPath.row]

        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        if let cell = tableView.cellForRow(at: indexPath) as? ExpandingCell {

            let isExpanded = !self.expanded[indexPath.row]

            self.expanded[indexPath.row] = isExpanded
            cell.isExpanded = isExpanded

            tableView.beginUpdates()
            tableView.endUpdates()
        }
    }
}


class ExpandingCell: UITableViewCell {

    @IBOutlet weak var label: UILabel!

    public var isExpanded = false {
        didSet {
            if self.isExpanded {
                self.label.text = "Lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots & lots of text"
            } else {
                self.label.text = "Not a lot of text"
            }
        }
    }
}

Just remember that your view in the cell has to be constrained to the top & bottom of the contentView, autolayout done right means that when you change the content of the label in this case, the cell will resize correctly.

SomaMan
  • 4,127
  • 1
  • 34
  • 45