Set TableView height by the number or rows - ios

I have got TableView in the MainstoryBoard and the number of rows is random every time.
I want the height of the whole TableView to be flexible - by that I mean, for example: if I got 4 rows in the TableView and each TableView row height is 22 so the TableView height will be 88.
Another example:
number of rows: 2
row height = 22
TableView will be 44.
How can I make it?

You can change the UITableView height as per the contentSize as below:
Swift 2.2
tableView.frame = CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width, tableView.contentSize.height)
Swift 3 or 4+
tableView.frame = CGRect(x: tableView.frame.origin.x, y: tableView.frame.origin.y, width: tableView.frame.size.width, height: tableView.contentSize.height)
and make sure you write the above line in viewDidAppear method
You need to write the above line in viewDidLayoutSubviews also.
Swift 2.2
func viewDidLayoutSubviews(){
tableView.frame = CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width, tableView.contentSize.height)
tableView.reloadData()
}
Swift 3 or 4+
func viewDidLayoutSubviews(){
tableView.frame = CGRect(x: tableView.frame.origin.x, y: tableView.frame.origin.y, width: tableView.frame.size.width, height: tableView.contentSize.height)
tableView.reloadData()
}

Simply take outlet for tableview height constraint and update it in willDisplay cell: UITableViewCell method of UITableView
#IBOutlet weak var tblHConstraint: NSLayoutConstraint!
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
self.tblHConstraint.constant = self.tblView.contentSize.height
}

nothing worked here is the solution i ended up using which gives accurate height.
extension UITableView {
var contentSizeHeight: CGFloat {
var height = CGFloat(0)
for section in 0..<numberOfSections {
height = height + rectForHeader(inSection: section).height
let rows = numberOfRows(inSection: section)
for row in 0..<rows {
height = height + rectForRow(at: IndexPath(row: row, section: section)).height
}
}
return height
}
}
Usage:
tableView.contentSizeHeight
will give you the actual calculated height of your table view content.

Take outlet of the Height Constraint of the parent view and assign it the height of table view's content + constant(extra height of other contents in the view)
heightConstraintView.constant = tableView.contentSize.height + constantValue //use the value of constant as required.
and write this code in the cellForRowAt method.

To use with with autolayout:
Somewhere in viewDidLoad()
tableView.anchor(top: view.topAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor)
and then in your viewDidLayoutSubViews()
tableView.heightAnchor.constraint(equalToConstant: tableView.contentSize.height).isActive = true

DispatchQueue.main.async {
var frame = tableView.frame
frame.size.height = tableView.contentSize.height
tableView.frame = frame
}
OR
DispatchQueue.main.async {
self.TblViewHeightConstraint.constant = CGFloat((self.array.count) * 30)//Here 30 is my cell height
self.TblView.reloadData()
}

Please find the below code.
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UIScreen.main.bounds.height
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let tableViewHeight = self.tableView.frame.height
let contentHeight = self.tableView.contentSize.height
let centeringInset = (tableViewHeight - contentHeight) / 2.0
let topInset = max(centeringInset, 0.0)
self.tableView.contentInset = UIEdgeInsets(top: topInset, left: 0.0, bottom: 0.0, right: 0.0)
}

Use this for Swift 3
override func viewDidLayoutSubviews(){
tableView.frame = CGRect(x: tableView.frame.origin.x, y: tableView.frame.origin.y, width: tableView.frame.size.width, height: tableView.contentSize.height)
tableView.reloadData()
}

Note: when your are increasing the tableview height it will goes out side the view. and you will get a problem with scrolling.
Ex: take a view inside that keep tableview and give constraints to left,right,bottom,top to Zero(0).
now reload the tableview assume 2 rows each row height is 20 now total rows height is 40. its fine and gave those height to table view, Table view will show only 40 height (you can assign tableview height by taking constraints (or) table view content size both are give same height according to rows height).
But if you reload 50 rows you will get scrolling problem because those rows height given to table view height, here table view height expands more than your screen height.
To solve this problem you should use scroll view outside the tableview.

Take outlet of tableview height and set it with your tableview row height in cellForRowAt like this,
tableviewHeight.constant = (tableView.contentSize.height * no_of_rows) + bottom_space

Related

Expanding ScrollView with TableViewCells

I am trying to expand my view when tableViewCells are added to the tableview to scroll the whole view rather than the table, but my implementation of increasing the size of the tableView and the ScrollView isn't quite working, I'm not sure if it has something to do with the code or the constraints set. What sort of constraints would I have to set in story board to make this work?
This is what I've tried and it isn't expanding the scroll view:
#IBAction func addExercisePressed(_ sender: Any) {
test.append("nice")
tableView.isScrollEnabled = false
tableView.frame = CGRect(x: tableView.frame.origin.x, y: tableView.frame.origin.y, width: self.view.frame.size.width, height: CGFloat(tableView.visibleCells.count * 120))
scrollView.contentSize = CGSize(width: self.view.frame.size.width, height: tableView.frame.size.height)
tableView.reloadData()
}
tableView.visibleCells.count isn't the same as number of cells at some time , also you need to use auto-layout constraints by giving the table in IB a default height and hooking it as an IBOutlet then inside viewDidLayoutSubviews/viewDidAppear do
self.tableHeight.constant = numOcells * 120
self.view.layoutIfNeeded()

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

Introduction
Context:
I'm creating one of my first apps but I've ran into an issue I cannot figure out.
I have tableView with cells packed with quite a few UIElements. All constraints are done using the EasyPeasy library which basically just sets auto layout constraints ( I have tried setting them manually also). The UITextView in question is constrained by various numbers to the left, right, top and bottom, I have no constraints on it for height or width.
in cellForRowAt indexPath: I set the textView delegate for each cells textView to self, using a delegate property declared within the cells custom class. I also tag every textView with its cells indexPath.row (gives textView.tag integer in textViewDidChange method).
Issue/acknowledgments:
After browsing SO a lot I've found a few questions alike this but I have not been able to make them work for me, I have implemented parts of them that felt logic to my case. I believe the problem differencing my situation from those questions lies in that for my cell design to work the cells has to have a height of itemHeight or higher.
I have noticed that as I type into the textview the textview itself increases in height (even below the cells border but its not visible as it reaches that point), however the cell itself doesn't resize.
I've tried with a cell that only contains a textView so the problem must lie in the textViewDidchange or heightForRowAt indexPath methods.
Question:
What am I doing wrong here? Why doesn't the cells height change dynamically as I type in the textView?
Code:
func textViewDidChange(_ textView: UITextView) {
var newframe = textView.frame
newframe.size.height = textView.contentSize.height - textView.frame.size.height + itemHeight[textView.tag]
textView.frame = newframe
let ndxPath = IndexPath(row: textView.tag, section: 0)
let cell = tableView.cellForRow(at: ndxPath) as! EventsCell
cell.frame = CGRect(x: cell.frame.origin.x, y: cell.frame.origin.y, width: cell.frame.width, height: textView.frame.height)
tableView.beginUpdates()
tableView.setNeedsLayout() //have tried without this line
tableView.layoutIfNeeded() //have tried without this line
tableView.endUpdates()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if UITableViewAutomaticDimension > itemHeight[indexPath.row] {
return UITableViewAutomaticDimension
} else {
return itemHeight[indexPath.row]
}
}
TextView constraints:
let containerView : UIView = {
let cv = UIView(frame: .zero)
cv.backgroundColor = .white
cv.translatesAutoresizingMaskIntoConstraints = false
cv.layer.cornerRadius = 7
return cv
}()
let eventText : GrowingTextView = { // GrowingTextView is a extension to a regular UITextView
let tv = GrowingTextView()
tv.allowsEditingTextAttributes = true
tv.isScrollEnabled = false
var delegate: UITextViewDelegate?
tv.textContainerInset = UIEdgeInsetsMake(1, 1, 0, 1)
tv.autoresizingMask = .flexibleHeight
tv.translatesAutoresizingMaskIntoConstraints = false
return tv
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
containerView.addSubview(eventText)
contentView.addSubview(containerView)
containerView .easy.layout([Height(CGFloat(95 * itemCount)), Left(8), Right(8)])
eventText .easy.layout([Left(77), Right(5), Top(90), Bottom(4)])
}
Thanks for reading my post.
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 )`

Add a padding function in a tableView (Swift)

I would like to adjust this function (that was created originally for a collectionView)
let place = places[indexPath.row]
let annotationPadding = CGFloat(5)
let font = UIFont.systemFont(ofSize: 15)
let commentHeight = place.heightForComment(font, width: width)
let height = annotationPadding + commentHeight + annotationPadding
return height
So I can add it in func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { } in my tableView, how can I do? (My problem is that CGFloat do not accept width)
Assuming heightForComment for the collection view takes the width of the collection view cell, you can pass in the width of the table view, since table view cells are as wide as their containing table view:
let commentHeight = place.heightForComment(font, width: tableView.bounds.size.width)
If there is additional leading or trailing margins for the comment in the table view, you can subtract that from the table view width:
let commentHeight = place.heightForComment(font, width: tableView.bounds.size.width - (20 * 2)) // 20 points margin on left and right

How to add a view on top of UITableView that scrolls together, but stick to top after scrolling

I have a UIView with height of 100 pixels that's on top of my UITableView. When I scroll up I want the UIView to scroll together with my UITableView as if it's part it. When 50 pixels of my UIView is hidden from scrolling up, I want to pin the UIView at the top while I continue scrolling up. How can this be achieved? I tried using changing my UIView's top NSLayoutConstraint constant equal to my tableview's content offset, but they don't scroll at the same speed.
First make sure your tableView is grouped:
self.tableView = UITableView(frame: CGRect(x: 0, y: (self.navigationController?.navigationBar.frame.maxY)!, width: self.view.bounds.size.width, height: (self.view.bounds.size.height - (self.navigationController?.navigationBar.frame.maxY)!)), style: .grouped)
self.tableView.delegate = self
self.tableView.dataSource = self
self.view.addSubview(self.tableView)
Then you need to add the UIView into the subview of your tableView:
self.myView = UIView()
self.myView.backgroundColor = UIColor.green
self.myView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 100)
self.tableView.addSubview(self.myView)
Add a header of height 100 for your tableView's first section so that your cells are at right place:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 100
}
Then adjust the frame of your UIView when scrolling:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.y
if(offset > 50){
self.myView.frame = CGRect(x: 0, y: offset - 50, width: self.view.bounds.size.width, height: 100)
}else{
self.myView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 100)
}
}
Demo:
If I understand you'r question correctly, You can do it by implementing table view scrollViewDidScroll: method like this
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
static CGFloat previousOffset;
CGRect rect = self.yourUIView.frame;
//NSLog you'r UIView position before putting any condition
// NSLog(#"Origin %f",rect.origin.y);
rect.origin.y += previousOffset - scrollView.contentOffset.y;
previousOffset = scrollView.contentOffset.y;
//assuming you'r UIView y position starts from 0
//Before setting the condition please make sure to run without any
//condition if not working
if(rect.origin.y>=-50 && rect.origin.y<=0){
self.yourUIView.frame = rect;
}
}
Hope it helps...
What you need is to implement the viewForHeader inSection method for your tableView (remember to make your ViewController implementing the tableviewDelegate protocol) and once you've got it you should set your tableViewStyle to UITableViewStylePlain
Share your code if you are interested in more help.
Source: UITableView with fixed section headers
I got the same problem with
navigationItem.largeTitleDisplayMode = .always
navigationBar.prefersLargeTitles = true
and
tableView.contentInset = UIEdgeInsets(top: 40, left: 0, bottom: 0, right: 0)
For me nothing from any above options can help but I realised that you can just call
tableView.setContentOffset(CGPoint(x: 0, y: -tableView.contentInset.top), animated: false)
in viewDidload() and problem gone! :)
Totally forgot about this method and spent too much time for this creepy behavior.
Good Luck!

Resizable TableView Cell

So I have been trying to create a generic chat bubble-ish look with a resizable view and label inside a UITableView cell. Things were working well until I tried to add in the resizable feature. It cuts off just a bit at the bottom (or does not give any margin), and I have not worked with completely dynamic cells like this before so I am not sure how to fix this. I tried adding a 20px buffer but it did not help. I appreciate the help!
(Code below)
import QuartzCore
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var listOfStrings = [String] ()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// When I Uncomment the 2 lines below, the cell cuts off and only displays a little bit of the blue view.
// self.tableView.rowHeight = UITableViewAutomaticDimension
// self.tableView.estimatedRowHeight = 75
listOfStrings.append("Switch, Button, Segmented Control, Slider, Textfield")
listOfStrings.append("Switch, Button, Segmented Control, Slider, Textfield")
listOfStrings.append("Switch, Button, Segmented Control, Slider, Textfield")
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(indexPath.row)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("someCell") as! SomeTableViewCell
cell.contentView.viewWithTag(0)!.backgroundColor = UIColor.clearColor()
let size = cell.layer.bounds
let tableSize = self.tableView.layer.bounds
let viewCGR = CGRect(x: size.minX, y: size.height/2, width: tableSize.width, height: size.height/2)
let view: UIView = UIView(frame: viewCGR)
let labelCGR = CGRect(x: 0, y: 0, width: viewCGR.width, height: viewCGR.height)
let label: UILabel = UILabel(frame: labelCGR)
label.numberOfLines = 0
label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)
label.textAlignment = NSTextAlignment.Center
label.text = listOfStrings[indexPath.row]
label.sizeToFit()
self.tableView.updateConstraints()
let newViewCGR = CGRect(x: viewCGR.minX, y: viewCGR.minY, width: label.frame.width+20, height: label.frame.height+20)
view.frame = newViewCGR
view.sizeToFit()
label.textColor = UIColor.blackColor()
label.center.x = view.center.x
// self.tableView.updateConstraints()
view.addSubview(label)
self.tableView.updateConstraints()
view.backgroundColor = UIColor.blueColor()
view.layer.cornerRadius = 6
cell.addSubview(view)
self.tableView.updateConstraints()
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listOfStrings.count
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Btw I am not using autolayout + constraints to get the functionality of having the bubble be able to appear on the left or right (depending on incoming or outgoing).
After the new API by apple you don't calculate height. You just use autolayout to do the work. I think this link could help you a lot. By the way there is a nice tutorial Self-sizing Table View Cells
you don't have to calculate height row for height cell .
you add this code in viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 50
#Ryan find the height of the cell with the text using following method,
CGRect textRect =
[attributedText boundingRectWithSize:CGSizeMake(cellWidth, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
context:nil];
call this method from
heightForRowAtIndexPath:
and set the height of the cell here as textRect.size.height

Resources