How to set dynamic table height based on table row? - ios

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

Related

Stop UICollectionView From Scrolling and Make it Show All Cells

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

hide table view cell and remove table view space

I'm trying to hide table view and remove space. But the table view space not removed. When first time user coming this page there is no data inside the table view so I write ishidden=true.table hidden but space not remove.user add data manually and table view appear.this working proper after add data .
I'm trying
self.tableView.tableFooterView = UIView()
self.tableView.ishidden=true
above code table hidden but not remove table space. So how to remove space of hide table view..
You need to set the height constraint for table view.
The best place to set the height is numberOfSectionsInTableView function.
This function called once on every reload of data.
//Assuming your data source
var dataSource: [String] = []
//connect this outlet to tableViewHeight Constraint
#IBOutlet var tableViewHeight: NSLayoutConstraint!
//connect this outlet to tableView
#IBOutlet var tableView: UITableView!
func numberOfSections(in tableView: UITableView) -> Int {
//need to check the data availability
if self.dataSource.count == 0{
//need to set the table height constraint to zero
self.tableViewHeight.constant = 0
self.tableView.isHidden = true
return 0
}
else {
//need to set the table height constraint to desired frame.
self.tableViewHeight.constant = self.view.frame.size.height //Assuming full screen.
self.tableView.isHidden = false
return 1
}
If you just want to hide tableView then just set the tableView.alpha to 0 and disable tableView interaction. You can set your no data view before tableview in storyboard hierarchy and then manage with tableview alpha values to hide and show no data view
you can do with the help of tableview contentSize observer key.For that you have to take Outlet of tableview height and set into tableView observer method.
Set tableView scroll disable.
override func viewWillAppear(_ animated: Bool) {
tbl.addObserver(self, forKeyPath: "contentSize", options: [.new], context: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
tbl.removeObserver(self, forKeyPath: "contentSize")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if object is UITableView {
print("contentSize:= \(tbl.contentSize.height)")
self.heightTbl.constant = tbl.contentSize.height
}
}
Hope this will help you.

Auto Height of UICollectionView inside UITableViewCell

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
}

UITableView not shown inside UIStackView

I have a UIStackView where it can show a label, image, table view or nothing based on user selection. Here is my current hierarchy for the dynamic view:
UIStackView_Parent
UILabel - some text, fixed view
UIStackView_Child - this is the container that can show multiple things or nothing
UIView - another fixed view
When I call UIStackView_Child.addArrangedSubview(...) with a label or image it works perfectly, but addArrangedSubview(tableView) does not show the table. I have tableView.scrollEnabled = false and set frame to fixed height based on number of cell and defined the table's cellHeight.
Any help would be much appreciated, thanks!
Another way to do this without explicitly setting the frame height and width is to embed your table view inside an empty view, pin the table view to the top, bottom, left and right and set a "greater than or equal to" constraint to the table view's height.
That's because stackview tries to compress content as much as possible. When you initally add a tableview, I'm assuming it has no content, so stackview compresses the width to 0 because it has nothing to show. You have to do one of two things:
After the table gets populated with data, you have to set the tableView's frame. I do this by calculating how big each cell is going to be. I know the width is going to be as big as the view that contains stackview. So it winds up being something like
let estimatedHeight = tableView.numberOfRows(inSection: 0) //You may need to modify as necessary
let width = parentView.frame.size.width
tableView.frame = CGRect(x: 0, y: 0, width: width, height: estimatedHeight)
Once you set the tableView's frame, stackview should automatically adjust.
You can also automatically monitor the height of the UITableView with Combine:
import Combine
var tableViewHeightConstraint: NSLayoutConstraint?
var cancellables: Set<AnyCancellable> = []
// Table View Content Size monitor
tableView.publisher(for: \.contentSize)
.receive(on: DispatchQueue.main)
.sink { self.tableViewHeightConstraint?.constant = $0.height }
.store(in: &cancellables)
Or if you don't have combine available you can add an Observer:
tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if(keyPath == "contentSize") {
// Here you could get from change or simple get it directly from the table view
tableViewHeightConstraint?.constant = tableView.contentSize.height
}
}

how to set dynamic height of a Collection View, the 'view' not the 'cells'?

I have embedded a collection view in another view and disabled the collection view's scrolling ability, what i want to achieve is similar to Instagram's profile tab. However, I cannot figure out how should I set the height of the collection view in this case since the number of cells are dynamic.
I tried searching different solutions but most results are on changing the cells dynamically but not the collection view height itself. Is there any default/standard solutions for that?
Set the width you want the collection view to have (hopefully that is static), and request a layout:
collectionView.frame = CGRectMake(0., 0., width, 0.);
[collectionView layoutIfNeeded];
The calculated height of the collectionView will then be available at:
collectionView.contentSize.height
For Swift please follow below steps:
Declare a CGFloat variable in declaration section:
var height : CGFloat!
At viewDidAppear you can get it by:
height = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height
Maybe when you reload data then need to calculate a new height with new data then you can get it by: addObserver to listen when your CollectionView finished reload data at viewWillAppear:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
....
....
self.shapeCollectionView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.Old, context: nil)
}
Then add bellow function to get new height or do anything after collectionview finished reload:
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
let newHeight : CGFloat = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height
var frame : CGRect! = self.myCollectionView.frame
frame.size.height = newHeight
self.myCollectionView.frame = frame
}
And don't forget to remove observer:
self.myCollectionView.removeObserver(self, forKeyPath: "contentSize")
I hope this will help you to solve your issue in swift.
1) you need to set height constraint to collectionview in storyboard or xibs
2) make outlet of heightConstraint
3) and use this code while relaod collectionview
collectionViewHeightConstaint.constant = collectionView.contentSize.height
In your ViewController, set the frame of the collectionView. For example:
self.collectionView.frame = CGRectMake(0, 0, 100, 200);
Based on the fact you know the height of the cells and you know the number of cells on the screen. I would...
self.collectionView.frame = CGRectMake(0, 0, DEFINED_WIDTH, DEFINED_CELL_HEIGHT*Number of Cells);
or have I miss understood the question?

Resources