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 thecontentView
or to views which are connected to the top and bottom of thecontentView
, 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()
andtableView.endUpdates()
ontextViewDidChange
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
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:
Result after using textViewDelegate, and custom resizing function
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:
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
iOS Uiimage Storage Formats, Memory Usage and Encoding/Decoding
How to Know Users Click Fast Forward and Fast Rewind Buttons on the Playback Controls in Iphone
Why Can't I Call the Default Super.Init() on Uiviewcontroller in Swift
Class Plbuildversion Is Implemented in Both/Applications
Ios:Retrieve Rectangle Shaped Image from the Background Image
How to Convert an Uiimage to Grayscale in Swift Using Cifilter
Fbsdkaccesstoken Currentaccesstoken Nil After Quitting App
Conditionally Import a Framework (Such as Speech) Based on iOS Version in Swift
Could Not Launch Process Launch Failed: Timed Out Waiting for App to Launch
Warning Frame for "Navigation Bar" Will Be Different at Run Time Appears in Xcode 8 Swift 3
How to Make Return Key on iPhone Make Keyboard Disappear
Uifont - How to Get System Thin Font
How to Hide "-" (Delete) Button While Editing Uitableview
How to Do a Native "Pulse Effect" Animation on a Uibutton - iOS