I have table view in which I have another table view. Actually I make dynamic section with cells coming from backend.
The problem is that is not showing the last cell.
Here is the main table view height setting:
//This code is in first view controller updating table view height
override func updateViewConstraints() {
tableViewHieght.constant = tableView.contentSize.height
super.updateViewConstraints()
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
tableView.layer.removeAllAnimations()
tableViewHieght.constant = tableView.contentSize.height
UIView.animate(withDuration: 0.5) {
self.updateViewConstraints()
}
}
Add picture for more clarification
I don't know what I should do for second table view that is in cell so it adopt the dynamic size.
The actual output:
I work on it and i realised the problem is with first table view row height i inspect the constraint and all settings that is necessary for making it dynamic but still it not growing i did a lil hack but i really want to do it with tableview.automatic dimension
// this is first table view cell in that cells every cell contain a label and a table view a second one which contain some label that making a format like section and cells In second table view I used Table view.automatic dimension
I have a collection view that is working fine when it has 10 or less items. Each row has 5 items so up to 10 it has two rows. However, when I have more than 10 items, it creates a 3rd row that is cut off and the collection view starts scrolling. When I try to disable scrolling, the collection view cuts off the last row. I want to ideally have a collection view that will keep expanding vertically no matter how many rows I have. Can anybody point me in the direction for making that happen?
Thank You!
1. For dynamic height updates
You can add an observer for the contentSize of collectionView
collectionView.addObserver(self, forKeyPath: "contentSize", options: [.new,.old], context: nil)
Then you can override this method to check the contentSize changes in collectionView
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
if let obj = object as? UICollectionView, obj == self.collectionView && keyPath == "contentSize" {
if let oldVal = change?[NSKeyValueChangeKey.oldKey] as? CGSize, let newValue = change?[NSKeyValueChangeKey.newKey] as? CGSize,oldVal.height == newValue.height {
return
}
let contentHeight = self.collectionView.contentSize.height
//You can update the height constraint or you can manually update the height of collection view.
self.heightConstraintCV.constant = contentHeight
}
UIView.animate(withDuration: 0.1) {
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
}
2. A little simple way would be to calculate height manually. if you are sure that each row will have 5 elements.
height = (numberOfRows * heightOfEachRow)
3. Set the contentSizeHeight directly as height of collectionView
height = collectionView.contentSize.height
I am facing table height issue at run time.
I have a Scroll View added to my Super view. Over the Scroll View, I have added a UIView in the top then after UIView I have placed one UITableView (scrollEnabled false) and again have added a UIView in the bottom of my UITableView.
Problem: I want user to do a single scroll in the screen, so I have disabled table view scrolling property. Now my problem is I am fetching table array data from server so after getting data from server, my viewDidLayoutSubviews method is not getting called where I have wrote the table height logic. And when I am re-loading the same screen then everything is working fine.
Here is my code :
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if devicesArray.count != 0{
viewDeviceList.frame = CGRect(x: viewDeviceList.frame.origin.x, y: viewDeviceList.frame.origin.y, width: viewDeviceList.frame.size.width, height: tableDeviceList.contentSize.height)
tableDeviceList.reloadData()
}
}
Can anyone suggest me on this. Thank you.
Please try with below code and let me know. I'm working with observer and its working perfectly.
//MARK:- Add this observer into your View/Controlloer
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
self.transactionTableViewHeightConstraints.constant = transactionTableView.contentSize.height
}
//MARK:- Fetch TableListData
func updateTableViewHeightAfterAPICall(){
self.transactionTableView.addObserver(self, forKeyPath: "contentSize", options: [], context: nil)
self.transactionTableView.reloadData()
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
}
Create outlet for tableView height constraint like this
#IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
add this line
self.tableView.reloadData()
self.tableViewHeightConstraint.constant = self.tableView.contentSize.height
I want to create a cell that consist of title text, description text, and a collection view. So I tried to created a cell like this
I added top, trailing, and leading to superview constraint for title and description. Then I also add top, bottom, trailing, and leading constraint to my UICollectionView.
For auto height my tableview cell, I override viewWillAppear
override func viewWillAppear(_ animated: Bool) {
tableView.estimatedRowHeight = 300
tableView.rowHeight = UITableViewAutomaticDimension
}
And in my custom cell class, I called layoutIfNeeded()
public func setRowWithData(model: DataModel) {
// set your view here
contentView.layoutIfNeeded()
}
What I want is, all of cell in collectionView is showed and my tableViewCell's height will adapt into it.
Is there any way to do it? Any help would be appreciated. Thank you!
You can user content size observer of collection view to change the height of collection view according to its content at runtime.
To add the observer to your collection view you can use below method.
self.collectionView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.old.union(NSKeyValueObservingOptions.new), context: nil)
By using below callback method you can set the collection view height runtime.
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
self.collectionViewHeight.constant = self.collectionView.contentSize.height
}
I want to calculate Content size of my UITableView. I am using autolayout with height UITableViewAutomaticDimension.
Tried to get [UITableView contentsize] after reloading tableview, in that case height get calculated based on estimatedRowHeight.
self.tableView.estimatedRowHeight = 50;
self.tableView.rowHeight = UITableViewAutomaticDimension;
Delegate -
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}
Trying to get content size and assign it to height constrain.
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
self.tableHeightConstraint.constant = self.tableView.contentSize.height;
Any suggestions? Thanks in advance.
Edit:
Finally I am solved my problem with some tweak. I changed tableview height to max (In my case 300 is max) before reloading data on table, so tableview has space to resize all cells.
self.tableHeightConstraint.constant = 300;
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
self.tableHeightConstraint.constant = self.tableView.contentSize.height;
Thanks for help.
Finally I am solved my problem with some tweak. I changed tableview height to max (Objective-C: CGFLOAT_MAX, Swift: CGFloat.greatestFiniteMagnitude) before reloading data on table, so tableview has space to resize all cells.
Objective-C:
self.tableHeightConstraint.constant = CGFLOAT_MAX;
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
self.tableHeightConstraint.constant = self.tableView.contentSize.height;
Swift:
tableHeightConstraint.constant = CGFloat.greatestFiniteMagnitude
tableView.reloadData()
tableView.layoutIfNeeded()
tableHeightConstraint.constant = contentTableView.contentSize.height
Posting this so it will be helpful for others.
Finally, I understood you problem and here is the solution of it.
I hope you have already done this.
First take put some fix height of UITableView.
Then take the constraint IBOutlet of UITableView Height.
Then under viewDidLayoutSubviews method you can get the original UITableView height after populating the data.
Update the below code:
override func viewDidLayoutSubviews() {
constTableViewHeight.constant = tableView.contentSize.height
}
Update:
self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
Please check this.
By setting the estimatedRowHeight to zero during table view initialisation and by adding the following code to viewDidLayoutSubviews worked for me!
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.constant = tableView.contentSize.height
All the solutions above weren't working for my case.
I ended up using an observer like this:
self.tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let obj = object as? UITableView {
if obj == self.tableView && keyPath == "contentSize" {
if let newSize = change?[NSKeyValueChangeKey.newKey] as? CGSize {
self.tableView.frame.size.height = newSize.height
}
}
}
}
Using UICollectionView for custom cell size is the best solution for me but if you somehow prefer UITableView, you can use KVO instead.
Using KVO is the best practice to solve this contentSize problem, you don't need to use layoutIfNeeded to force the TableView to update its layout. Using #OnkarK answer (the accepted answer) will cause lag if the max_height (CGFloat.greatestFiniteMagnitude) is too big and setting the height manually is not a good idea.
Using Swift KVO:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
}
override func viewWillDisappear(_ animated: Bool) {
self.tableView.removeObserver(self, forKeyPath: "contentSize")
super.viewWillDisappear(true)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?){
if keyPath == "contentSize" {
if let value = change?[.newKey]{
let size = value as! CGSize
self.tableHeightConstraint.constant = size.height
}
}
}
Using Combine:
override func viewDidLoad() {
super.viewDidLoad()
tableView.publisher(for: \.contentSize)
.receive(on: RunLoop.main)
.sink { size in
self.tableHeightConstraint.constant = size.height
}.store(in: &cancellables)
}
Using RxSwift:
override func viewDidLoad() {
super.viewDidLoad()
tableView.rx.observe(CGSize.self, "contentSize")
.filter({$0 != nil})
.subscribe(onNext: { size in
self.tableHeightConstraint.constant = size!.height
}).disposed(by: bag)
}
for swift just use
tableView.invalidateIntrinsicContentSize()
tableView.layoutIfNeeded()
after reloadData()
and
tableView.contentSize.height
will works perfect.
Example on medium
You can try two things,
Call below method just after some seconds of delay after tableView reloads completely.
- (void)adjustHeightOfTableview
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
//In my case i had to call this method after some delay, because (i think) it will allow tableView to reload completely and then calculate the height required for itself. (This might be a workaround, but it worked for me)
[self performSelector:#selector(adjustHeightOfTableview) withObject:nil afterDelay:0.3];
});
}
When you call above method do not return estimatedHeightForRow,
/*
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 44;
}
*/
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
Just give it a try, it could help you get the problem.
For me the solution was to just set the estimatedRowHeight to a value larger than anything I deemed possible for the data I had to display.
For some reason this ensured that the estimatedRowHeight property wasn't used to determine the contentSize. I don't display the scroll indicator so it didn't really matter to me that the estimatedRowHeight was way off.
In my case, I don't use tableView's height constraint - using auto layout, top and bottom constraint is set and only bottom constraint changes as it goes into/out of edit mode.
Tzegenos's answer is modified in this way:
1. add tableView.estimatedRowHeight = 0 in viewDidLoad()
2. add the following:
override func viewDidLayoutSubviews() {
// 60 is row's height as set by tableView( cellRowAt )
// places is of type [String] to be used by tableView
tableView.contentSize.height = 60 * CGFloat(places.count)
}
You may as well subclass the UITableView and call whomever you want (i.e. it's container view) from it's layoutSubviews() where you definitely have a proper contentSize.
Block based KVO approach
var contentSizeObserver: NSKeyValueObservation?
#IBOutlet weak var myTableView: UITableView!
#IBOutlet weak var tableHeightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
contentSizeObserver = myTableView.observe(\UITableView.contentSize, options: [NSKeyValueObservingOptions.new], changeHandler: { _, change in
if let contentSize = change.newValue {
self.tableHeightConstraint.constant = contentSize.height
}
})
}
override func viewWillDisappear(_ animated: Bool) {
contentSizeObserver = nil
super.viewWillDisappear(true)
}
Use this function inside your viewWillLayoutSubviews and call the second function after every reload. Then this problem will be solved.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
dynamicHegihtChange()
}
func dynamicHegihtChange(){
yourTableName.invalidateIntrinsicContentSize()
yourTableName.layoutIfNeeded()
let addtionalPadding = 10
let height = yourTableName.contentSize.height
self.sevicesTableViewHeightConstraint?.constant =
height+addtionalPadding
}
//for reloading the table
func serviceTableReload(){
DispatchQueue.main.async {
self.servicesTableView.reloadData()
self.dynamicHegihtChange()
}
}