Search code examples
iosuitableviewautolayoutuitextview

UITextView in a UITableViewCell - Height resizes correctly but cell is cut off


The Problem

I have a UITextView inside of a custom UITableViewCell subclass that is producing some odd behavior.

Here is a gif of the problem I'm seeing (relevant items are colored):

Self Sizing Bug

The height resizes correctly, but the full contents of the cell are not shown. But when I pause execution and print out frames of the following:

  • Cell
  • Cell's content view
  • Text View

the frames are all correct!

Further, when I inspect the view using the view hierarchy debugger, the frames are all correct there too. There is one difference when using the view debugger though, and that is that I'm able to view the contents of the text view.

Here is what I see on the simulator vs in the debugger:

Side by side

There seems to be an extraneous cell separator at the point where the yellow stops. Other than that, I can't find any sort of indicator of why the cell is not expanding past its original height.

My Code

In the storyboard, the UITextView has top, bottom and trailing constraints to the cell's content view, and a vertical spacing constraint to the UIImageView. The UIImageView has a fixed width and has a leading constraint to the cell's content view. I believe my constraints are set up correctly. Oh yeah, and scrolling is disabled on the UITextView.

As for code, I have a custom protocol that informs the table view controller when the notes field has changed:

protocol AddContactCellDelegate {
    func notesViewDidChange(textView: UITextView)
}

Here is the relevant code from my UITableViewCell subclass:

class AddContactCell: UITableViewCell, UITextViewDelegate {

    var delegate: AddContactCellDelegate?

    func textViewDidChange(textView: UITextView) {
        delegate?.notesViewDidChange(textView)
    }
}

and from my UITableViewController subclass:

class AddContactViewController: UITableViewController, AddContactCellDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.estimatedRowHeight = 60.0
        tableView.rowHeight = UITableViewAutomaticDimension
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let row = rows[indexPath.row]
        let cell = tableView.dequeueReusableCellWithIdentifier(row.cellIdentifier, forIndexPath: indexPath) as! AddContactCell
        cell.configureForRow(row)
        cell.delegate = self

        return cell
    }

    func notesViewDidChange(textView: UITextView) {
        tableView.beginUpdates()
        tableView.endUpdates()
    }
}

Discussion

  • I've tried adding setNeedsLayout(), in to either the cell, the cell's content view, the tableview, or the textview, in just about every place, to no avail.

  • I rebuilt the entire view in the storyboard from scratch, same thing.

  • I tried creating a bare-bones project that has basically only the code above, and the sizing works correctly.

  • I've ruled out tons of other variables as being the culprit (for example, the tableview is in a container view, but the behavior persists without that).

One final weird point is that sometimes if I scroll the notes cell off the screen, leave the page, and come back again, everything looks as it should. The cell is fully resized and everything is visible. However, the problem resumes as soon as the text view goes to the next line, this time with all the previous text visible but none of the additional text.

If anyone has any ideas on what I might try next, it would be extremely helpful. Thanks!


Solution

  • Thanks to Steve asking for an example project that exhibits the behavior, I was able to figure out what is causing this issue.

    To get the rounded corner effect, I had been using a layer mask on my cells. The mask uses the bounds of the cell to calculate its path, so when the cell updated itself to reflect the height of the text view, the mask was still in place and covered up the rest of the cell.

    I didn't catch it because the rounding implementation was abstracted away in an extension. Whoops!