I have a UITableView with two UITextViews side by side inside each of the cells. I want both the UITableViewCell and UITextView to increase in height so that the user doesn't need to scroll within the UITextView. Here is what I've tried:
In the TableViewController Class:
self.tableView.estimatedRowHeight = 44
self.tableView.rowHeight = UITableViewAutomaticDimension
In the TableViewCell Class (got this from here) :
func textViewDidChange(textView: UITextView) {
var frame : CGRect = textView.frame
frame.size.height = textView.contentSize.height
textView.frame = frame
}
When the user types beyond the set width of the UITextView, the UITableView increases height from 44 to about 100 and the UITextView doesn't increase in height. I have the constraints set up so that the UITextView's height is equal to that of the UITableViewCell.
Any ideas why this is happening and how to correctly dynamically change the UITextView and UITableView's heights?
My answer is based on what we exactly use in production of our social app Impether, since you asked me on Twitter that you used the app and you saw expanding UITextView there.
First of all, we have a custom UITableViewCell based class containing the UITextView
, which will be expanded (this class has a corresponding xib file also, which you can design on your own):
class MultiLineTextInputTableViewCell: UITableViewCell {
//our cell has also a title, but you
//can get rid of it
@IBOutlet weak var titleLabel: UILabel!
//UITextView we want to expand
@IBOutlet weak var textView: 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 initialize the height of the text view
var textString: String {
get {
return textView?.text ?? ""
}
set {
if let textView = textView {
textView.text = newValue
textView.delegate?.textViewDidChange?(textView)
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Disable scrolling inside the text view so we enlarge to fitted size
textView?.scrollEnabled = false
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected {
textView?.becomeFirstResponder()
} else {
textView?.resignFirstResponder()
}
}
}
Having a custom cell defined, you can use it in a UITableViewController
based class like that:
class YourTableViewController: UITableViewController {
//in case where you want to have
//multiple expanding text views
var activeTextView: UITextView?
override func viewDidLoad() {
super.viewDidLoad()
//registering nib for a cell to reuse
tableView.registerNib(
UINib(nibName: "MultiLineTextInputTableViewCell", bundle: nil),
forCellReuseIdentifier: "MultiLineTextInputTableViewCell")
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
//hide keyboard when view controller disappeared
if let textView = activeTextView {
textView.resignFirstResponder()
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
//put your value here
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//put your value here
return 2
}
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let row = indexPath.row
let cell = tableView.dequeueReusableCellWithIdentifier(
"MultiLineTextInputTableViewCell",
forIndexPath: indexPath) as! MultiLineTextInputTableViewCell
let titleText = "Title label for your cell"
let textValue = "Text value you want for your text view"
cell.titleLabel.text = titleText
cell.textView.text = textValue
//store row of a cell as a tag, so you can know
//which row to reload when the text view is expanded
cell.textView.tag = row
cell.textView.delegate = self
return cell
}
override func tableView(tableView: UITableView,
estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
//standard row height
return 44.0
}
override func tableView(tableView: UITableView,
heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView,
canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
}
//extension containing method responsible for expanding text view
extension YourTableViewController: UITextViewDelegate {
func textViewDidEndEditing(textView: UITextView) {
let value = textView.text
//you can do something here when editing is ended
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange,
replacementText text: String) -> Bool {
//if you hit "Enter" you resign first responder
//and don't put this character into text view text
if text == "\n" {
textView.resignFirstResponder()
return false
}
return true
}
func textViewDidBeginEditing(textView: UITextView) {
activeTextView = textView
}
//this actually resize a text view
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)
let thisIndexPath = NSIndexPath(forRow: textView.tag, inSection: 0)
tableView?.scrollToRowAtIndexPath(thisIndexPath,
atScrollPosition: .Bottom,
animated: false)
}
}
}
First make sure your auto layout constraint doesn't conflict with frame you set. (If everything is OK but still doesn't work) Then try changing frame
to bounds
. A view's frame (CGRect) is the position of its rectangle in the superview's coordinate system. Using frame
may cause strange problem sometimes in my experience.
func textViewDidChange(textView: UITextView) {
var frame : CGRect = textView.bounds
frame.size.height = textView.contentSize.height
textView.bounds = frame
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With