How to Resize Table Cell Based on Textview

Swift: Continuous resizing of tableViewCell while user types in a UITextView

  • The constraints that determine the height should be laid out in such a way that the textView is attached directly to the top and bottom of the contentView or to views which are connected to the top and bottom of the contentView, so that autolayout can make out the height by connecting the constraints.
  • Make sure that you do not mention a height for the textView and disable scrolling. Let automatic dimension take care of all that.
  • Now all you need to do is call tableView.beginUpdates() and tableView.endUpdates() on textViewDidChange

Here is my repo which demonstrates the same.


OP Edit:

  • You should store the additional height that you add in a variable in the cell class so the cells can reload an appropriate height when the tableVIew is reloaded.

  • You should also change textViewDidChange method

    cell.frame = CGRect(x: cell.frame.origin.x, y: cell.frame.origin.y, width: cell.frame.width, height: textView.frame.height) 

    to

    let newFrame = ”originalCellHeight” - ”originalTextViewHeight” + textView.contentSize.height

    cell.frame = CGRect(x: cell.frame.origin.x, y: cell.frame.origin.y, width: cell.frame.width, height: newFrame )`

UITextView inside tableview not resizing according to content

I have tried with UITextView inside UITableViewCell.

Constraints

UITextView, top, bottom and right as 2, 2 and 5, Width as 50. Font Size as 13, Alignment as Center. Give Outlet connection for Width constraints as txtVwWidthConst

UIViewController

@IBOutlet weak var tblView: UITableView!
var textWidthHeightDict = [Int : CGSize]()

override func viewDidAppear(_ animated: Bool) {

stringValue = [0 : "qwer\nasdasfasdf", 1 : "qwe", 2 : "123123\nasdas\nwqe", 3 : "q\n3\n4", 4 : "klsdfjlsdhfjkhdjkshfjadhskfjhdjksfhkdjsahfjksdhfkhsdfhjksdfjkasklsdfjlsdhfjkhdjkshfjadhskfjhdjksfhkdjsahfjksdhfkhsdfhjksdfjkasklsdfjlsdhfjkhdjkshfjadhskfjhdjksfhkdjsahfjksdhfkhsdfhjksdfjkas"]

for i in 0..<stringValue.count
{
GettingTextViewSize(getStr: stringValue[i]!, fontSize: 14, loopValue: i)
}
tblView.reloadData()
}

func GettingTextViewSize(getStr : String, fontSize: CGFloat, loopValue : Int)
{
var textSize = (getStr as! NSString).size(withAttributes: [NSAttributedStringKey.font : UIFont.systemFont(ofSize: fontSize)])
if textSize.width > self.tblView.frame.width
{
// IF STRING WIDTH GREATER THAN TABLEVIEW WIDTH
let multipleValue = textSize.width / self.tblView.frame.width
textSize.height = (textSize.height * (multipleValue + 1.0))
textSize.width = self.tblView.frame.width

textWidthHeightDict[loopValue] = textSize
}
else
{
textSize.height = textSize.height + 10 //ADDING EXTRA SPACE
textSize.width = textSize.width + 10 //ADDING EXTRA SPACE

textWidthHeightDict[loopValue] = textSize
}
}

func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

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

return stringValue.count
}

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

let cell = tableView.dequeueReusableCell(withIdentifier: "table", for: indexPath) as! TblTableViewCell

cell.backgroundColor = cellBGColr[indexPath.row]

cell.txtVw.inputAccessoryView = toolBar
cell.txtVw.text = stringValue[indexPath.row]
cell.txtVw.tag = indexPath.row
cell.txtVwWidthConst.constant = (textWidthHeightDict[indexPath.row]?.width)!
cell.selectionStyle = .none
return cell
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var heightVal = (textWidthHeightDict[indexPath.row])
return heightVal!.height + 8 //ADDING EXTRA SPACE
}

UITableViewCell

class TblTableViewCell: UITableViewCell {
@IBOutlet weak var txtVw: UITextView!
@IBOutlet weak var txtVwWidthConst: NSLayoutConstraint!
// TEXTVIEW WIDTH CONSTRAINTS

override func awakeFromNib() {
super.awakeFromNib()
}
}

Output

Sample Image

Note

UITextView only, we have to calculate String size. But, in UILabel, this concept is very simple. Let me know, if you have any queries.

iOS static table auto resize based on textView

Swift 3 & Xcode 8.3.2

Use UILabel instead of UITextView, and set numberOfLine = 0, so it will automatic resize according to its content

or

if you want to keep UITextView instead UILabel, here is the code

class YourClass: UITableViewController, UITextViewDelegate {

var yourCustomCell: UITableViewCell = UITableViewCell()

override func viewDidLoad() {
super.viewDidLoad()

table.estimatedRowHeight = 40.0 // Replace with your actual estimation
table.rowHeight = UITableViewAutomaticDimension

// Tap to dismiss keyboard
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(EditInfoViewController.dismissKeyboard))
view.addGestureRecognizer(tap)

// Add tableView delegate
tableView.dataSource = self
tableView.delegate = self

// Add textView delegate
yourTextView.delegate = self

}
// Text view delegate, dont forget to add yourTextView.delegate = self in viewDidLoad
func textViewDidChange(_ textView: UITextView) {
if textView == yourTextView {
let newHeight = yourCustomCell.frame.size.height + textView.contentSize.height
yourCustomCell.frame.size.height = newHeight
updateTableViewContentOffsetForTextView()
}
}
// Animate cell, the cell frame will follow textView content
func updateTableViewContentOffsetForTextView() {
let currentOffset = tableView.contentOffset
UIView.setAnimationsEnabled(false)
tableView.beginUpdates()
tableView.endUpdates()
UIView.setAnimationsEnabled(true)
tableView.setContentOffset(currentOffset, animated: false)
}

// UITableViewDelegate, UITableViewDataSource
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableViewAutomaticDimension
}

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

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = yourCustomCell
cell.selectionStyle = .none
return cell
}
}

The result is here:
Before the code

Result after using textViewDelegate, and custom resizing function
After the code

Resize text view and table view cell dynamically in static table view

You don't have to do anything just go through this code:-
There is a new function to replace sizeWithFont, which is boundingRectWithSize.

Add the following function to my project, which makes use of the new function on iOS7 and the old one on iOS lower than 7. It has basically the same syntax as sizeWithFont:

   -(CGSize)text:(NSString*)text sizeWithFont:(UIFont*)font constrainedToSize:(CGSize)size{
if(IOS_NEWER_OR_EQUAL_TO_7){
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
font, NSFontAttributeName,
nil];

CGRect frame = [text boundingRectWithSize:size
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:attributesDictionary
context:nil];

return frame.size;
}else{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [text sizeWithFont:font constrainedToSize:size];
#pragma clang diagnostic pop
}
}

You can add that IOS_NEWER_OR_EQUAL_TO_7 on your prefix.pch file in your project as:

#define IOS_NEWER_OR_EQUAL_TO_7 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 7.0 )

I have referred from this link:-

UITableViewCell with UITextView height in iOS 7?

Resize UITableViewCell containing UITextView upon typing

Here is a swift solution that is working fine for me. Provided you are using auto layout, you need assign a value to estimatedRowHeight and then return UITableViewAutomaticDimension for the row height. Finally do something similar to below in the text view delegate.

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 44.0
}

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

// MARK: UITextViewDelegate
func textViewDidChange(textView: UITextView) {

// Calculate if the text view will change height, then only force
// the table to update if it does. Also disable animations to
// prevent "jankiness".

let startHeight = textView.frame.size.height
let calcHeight = textView.sizeThatFits(textView.frame.size).height //iOS 8+ only

if startHeight != calcHeight {

UIView.setAnimationsEnabled(false) // Disable animations
self.tableView.beginUpdates()
self.tableView.endUpdates()

// Might need to insert additional stuff here if scrolls
// table in an unexpected way. This scrolls to the bottom
// of the table. (Though you might need something more
// complicated if editing in the middle.)

let scrollTo = self.tableView.contentSize.height - self.tableView.frame.size.height
self.tableView.setContentOffset(CGPoint(x: 0, y: scrollTo), animated: false)

UIView.setAnimationsEnabled(true) // Re-enable animations.
}

how to properly resize the table view cell?

When you define the constraints for your text view, I’d suggest you define them solely between the text view and its superview.

So, if adding it programmatically, you’d add it to the content view of the cell, and define the constraints between the text view and the cell’s content view:

class CustomCell: UITableViewCell {
weak var textView: UITextView!

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configure()
}

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

func configure() {
let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(textView)
textView.isScrollEnabled = false
self.textView = textView

NSLayoutConstraint.activate([
textView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
textView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
textView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
textView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10)
])
}
}

Note the disabling of the scrolling, which instructs the text view to use its intrinsic size to determine its height.

Anyway, then my view controller is as follows:

class ViewController: UIViewController {

@IBOutlet weak var tableView: UITableView!

let strings = ["Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"Praesent quis nisl justo. Sed ipsum lacus, consectetur quis varius a, ornare sit amet nisl. Curabitur vulputate felis quis pulvinar maximus. Donec sem lorem, ultrices sed ultricies ac, placerat sit amet purus. Nam elementum risus justo, vitae tincidunt mauris sodales vitae. Integer id fermentum quam. Vivamus a arcu neque. In consectetur, velit in sollicitudin finibus, quam nibh rutrum augue, sed dignissim purus ex id elit. Duis sit amet volutpat sapien. Ut leo sapien, iaculis sit amet ultrices eget, fringilla nec dolor.",
"Etiam aliquam risus vitae cursus mollis. Fusce vulputate nisi sodales est euismod rutrum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla dignissim ante sed massa viverra, in lobortis ligula semper. Maecenas placerat nec erat ut malesuada."]

override func viewDidLoad() {
super.viewDidLoad()

tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 100
}

}

// MARK: - UITableViewDataSource

extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return strings.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.textView.text = strings[indexPath.row]
return cell
}
}

That yields:

Sample Image

All of that having been said, I’d probably add the text view and its constraints, set its properties, etc., right in the storyboard cell prototype and then hook up an outlet, and then my cell is radically simplified:

class CustomCell: UITableViewCell {
@IBOutlet weak var textView: UITextView!
}

That achieves precisely the same result with less code. But by showing my code above, you get a sense of precisely what I’d set up in IB.



Related Topics



Leave a reply



Submit