4

I have a custom UITableViewCell and I'm trying to resize the UITextView inside it based on the content size. I'm on iOS7 and using Autolayout.

I've tried using the following:

[cell.question setScrollEnabled:YES];
[cell.question sizeToFit];
[cell.question setScrollEnabled:NO];

and

- (CGRect)textViewHeightForAttributedText: (NSAttributedString*)text andWidth: (CGFloat)width
{
    UITextView *calculationView = [[UITextView alloc] init];
    [calculationView setAttributedText:text];
    CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];
    CGRect finalFrame = CGRectMake(0, 0, width, size.height);
    return finalFrame;
}

from different SO posts. I'm able to resize the frame. But the issue is I'm not able to see the change visibly. In the sense, when I log it, I can see the change. But the UITextView doesnt resize. I cant find any autolayout dependencies either.

When I disabled AutoLayout, it seems to work. How do I do this, by enabling AutoLayout?

Thanks.

EDIT

Here's my UITextView constraints

Screenshot

Tobi Nary
  • 4,566
  • 4
  • 30
  • 50
Anil
  • 2,430
  • 3
  • 37
  • 55

6 Answers6

2

You have to do this calculation in

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

method and also resize the cell height accordingly.

If you got it, it's okay. Or If you need the code sample, just ask again. I think you got it !

Updated

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
            UITextView *tempTV = [[UITextView alloc] init];
            [tempTV setText:@"your text"];
            CGFloat width = self.tableView.frame.size.width - TEXT_ORIGIN_X - TEXT_END_X;
            CGSize size = [tempTV sizeThatFits:CGSizeMake(width, MAX_HEIGHT)];
            return (TEXT_ORIGIN_Y + size.height + TEXT_BOTTOM_SPACE);
    }
Ravi Sisodia
  • 776
  • 1
  • 5
  • 20
  • Yes. I faced the same problem a day in past. And the solution is you have to resize textview in the above mentioned method and also return the calculated height for the row. – Ravi Sisodia Dec 09 '13 at 09:24
  • How did you tackle that situation? – Anil Dec 09 '13 at 09:25
  • Your constraints are okay. I think you're resizing the textview in the '- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath' method !! – Ravi Sisodia Dec 09 '13 at 09:27
  • I understand that. But that's done to resize the cell's height. If I do the same, my cell's height will change. Got it. But the UITextView's height still remains the same. I want to change that. – Anil Dec 09 '13 at 09:30
  • No, No, No. Your cell's height won't resize just by changing the height of textview. You've to do that. The constraints don't work from child to parent, they work from child to parent. Means only child(inner view) can resize if you the parents(or outer view), but you can't resize the parent(outer view) by resizing child(inner view). – Ravi Sisodia Dec 09 '13 at 09:53
  • For TableViewCell, you've to set cell height, and that's why this callback method is provided. Only resizing textview won't work. – Ravi Sisodia Dec 09 '13 at 09:54
  • And actually there is no need to resize textview. If you resize the parent(the cell), the child(the textview) will automatically resize bound to the constrains; As I previously mentioned. – Ravi Sisodia Dec 09 '13 at 10:05
  • 1
    The text view will only resize when the cell does if you've set up appropriate constraints. It doesn't happen by magic. If the text view has a height constraint then it's not going to resize. – Luke Redpath Dec 09 '13 at 10:08
  • Yes Luke Redpath. But According to the constrained Anil mentioned in questions, it'll work. – Ravi Sisodia Dec 09 '13 at 10:14
  • But apparently its not. Trying to get the constraints in place – Anil Dec 09 '13 at 11:18
  • Have you implemented this solution ? – Ravi Sisodia Dec 09 '13 at 11:19
  • Yes I have. Its not helping as it only increases the cell's height leaving the UITextView untouched. – Anil Dec 09 '13 at 11:32
  • @LukeRedpath can you tell me how I can incorporate both vertical space and height constraints into the UITextView? – Anil Dec 09 '13 at 11:33
  • Let me check it. Have you fixed the height of UITextView ?, Have you applied 'size to fit content' in StoryBoard for the UITextView ? Please share the code for 'heightForRowAtIndexPath' method. – Ravi Sisodia Dec 09 '13 at 11:35
  • If You constrained the height of UITextView, then it can't be resized. It will throw warning in console, when you resize the 'cell'. – Ravi Sisodia Dec 09 '13 at 11:37
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/42829/discussion-between-anil-and-bhole) – Anil Dec 10 '13 at 05:35
0

You may forget implement the heightForRowAtIndexPath method of TableView's delegete;

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat  yourTextViewsHeight = ... calculate it here
    return yourTextViewsHeight;
}
ethem
  • 201
  • 1
  • 6
0

I think you might have too many constraints for the text view. I can't be sure of this because it's difficult to communicate information about constraints that are built in IB.

Your text view only needs two constraints, one for each axis, in order to be fully constrained. One constraint should position the text view horizontally, the other vertically. The text view's intrinsic content size will be used by the Auto Layout system to automatically generate size constraints for the text view.

I think some of the constraints you have in place now are preventing resizing of the text view. This happens because, by default, constraints you create yourself are required (priority 1000). The automatically-generated sizing constraints, on the other hand, have a lower priority and will be overruled by any conflicting constraints that are required.

Note: just because the text view only needs two constraints to be fully constrained doesn't mean that you can't have more constraints. A table cell with 4 labels, 3 image views, and 1 text view is a complex layout, so you will most likely constrain other UI objects relative to the text view, rather than the superview.

bilobatum
  • 8,918
  • 6
  • 36
  • 50
0
I had the same issue but in a different situation. I have UItableview with two custom cells.

First Custom cell - self expanding textview. (like email type message box)
Second Custom Cell -  Static image. 

Have a  look at my code. You will get an insight to automatic resizing of cells.

//  ViewController.swift
//  ListWrap

import UIKit

class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate, UITextViewDelegate {

    @IBOutlet var listWrapTableView: UITableView!

    //CustomCells
    var CellIdentifier: String = "ListWrapTableViewCellID"
    var tapGesture: UITapGestureRecognizer!

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    override func viewWillAppear(animated: Bool) {
        //Adding Tap Gesture To Table
        tapGesture = UITapGestureRecognizer(target: self, action: "tapRecognized:")
        self.listWrapTableView.addGestureRecognizer(tapGesture)
        tapGesture.cancelsTouchesInView = false
        tapGesture.enabled =  true
    }
    func tapRecognized(recognizer: UITapGestureRecognizer){
        self.listWrapTableView.endEditing(true)
    }
    func textViewDidBeginEditing(textView: UITextView) {
        if (CellIdentifier == "ListWrapTableViewCellID")
        {
            tapGesture.enabled =  true
        }
        else
        {
            tapGesture.enabled =  false
        }
    }
    // MARK: - Table view data source
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
    {
        self.listWrapTableView.rowHeight = UITableViewAutomaticDimension
        return self.listWrapTableView.rowHeight
    }
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        if (indexPath.row == 0)
        {
            let surveyCell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier)! as! ListWrapTableViewCell
            return surveyCell
        }
        else if (indexPath.row == 1)
        {
            let reportsCell = tableView.dequeueReusableCellWithIdentifier("ListWrapSecondTableViewCellID")! as! ListWrapSecondTableViewCell
            return reportsCell
        }
        else
        {
            let cell:UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "")
            return cell
        }
    }
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
    {
    }
}



The first Custom cell:

//  ListWrapTableViewCell.swift
//  ListWrap

import UIKit

class ListWrapTableViewCell: UITableViewCell{

    @IBOutlet var listWrapTextView: UITextView!

//
//  override init?(style: UITableViewCellStyle, reuseIdentifier: String!) {
//      super.init(style: style, reuseIdentifier: reuseIdentifier)
//  }

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

    /// Custom setter so we can initialise the height of the text view
    var textString: String {
        get {
            return listWrapTextView.text
        }
        set {
            listWrapTextView.text = newValue
            textViewDidChange(listWrapTextView)
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        listWrapTextView.scrollEnabled = false
        listWrapTextView.delegate = self
    }
    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        if selected {
            listWrapTextView.becomeFirstResponder()
        } else {
            listWrapTextView.resignFirstResponder()
        }
    }
}
extension ListWrapTableViewCell: UITextViewDelegate {
    func textViewDidChange(textView: UITextView) {

        let size = textView.bounds.size
        let newSize = textView.sizeThatFits(CGSize(width: size.width, height: CGFloat.max))

        // Resize the cell only when cell's size is changed
        if size.height != newSize.height {
            UIView.setAnimationsEnabled(false)
            tableView?.beginUpdates()
            tableView?.endUpdates()
            UIView.setAnimationsEnabled(true)

            if let thisIndexPath = tableView?.indexPathForCell(self) {
                tableView?.scrollToRowAtIndexPath(thisIndexPath, atScrollPosition: .Bottom, animated: false)
            }
        }
    }
}
extension UITableViewCell {
    /// Search up the view hierarchy of the table view cell to find the containing table view
    var tableView: UITableView? {
        get {
            var table: UIView? = superview
            while !(table is UITableView) && table != nil {
                table = table?.superview
            }

            return table as? UITableView
        }
    }
}

The second custom cell: 

//  ListWrapSecondTableViewCell.swift
//  ListWrap


import UIKit

class ListWrapSecondTableViewCell: UITableViewCell {

    override func awakeFromNib() {
        super.awakeFromNib()
    }
    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

}


attaching storyBoard for further reference.

enter image description here

Alvin George
  • 14,148
  • 92
  • 64
0

Your layout is a bit more complex, but it should not matter if everything is set up properly.

You do not have to calculate anything (by using sizeThatFits).

All you have to do is disable UITextView's scrolling enabled property then on textViewDidChange call tableView.beginUpdates() and tableView.endUpdates(). That doesn't break the first responder and resizes the table view smoothly.

For a detailed explanation, check out a post I wrote which also includes a working sample project.

Jure
  • 3,003
  • 3
  • 25
  • 28
-1

try this

set your UITextView outlet like this in your custom UITableViewCell class

[yourTextView setAutoresizesSubviews:YES];
yourTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth;

hope this will work for you

aakil nikil
  • 277
  • 3
  • 7