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
}
Related
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
There is table view in scroll view, for the perfect scrolling need height of table view. But there is dynamic height of cell and in cell multiple content with dynamic data like image(calculating height of image with kingfisher library) and content(with 0 number of lines). So unable to calculate height of each cell. So I am using this for getting height of cell:-
let totalCount = self.itemArray.data1.count + self.itemArray.data2.count
if totalCount != self.totalHeightOfTable.count {
//Appending height of cell
self.tableView.reloadData()
self.totalHeightOfTable.append(cell.frame.height)
self.heightOfPost = self.totalHeightOfTable
if totalCount == self.totalHeightOfTable.count {
// Call back to get height of tableView
self.getTotalHeightOfTableView?(self.totalHeightOfTable)
}
}
because the tableView is inside a scrollview , I am not able to calculate the height for each cell of the tableView dynamically or at run time. The height I get at runtime is greater and there is a blank white space at the end of the tableView. So the total height for table view is always greater than the sum of all the cells in the table view.
UI structure attached
I understood your problem that you want to calculate dynamic table height with flexible cells height inside tableview and according to this height you want to update your parent scrollview contentSize height .
I want to tell you that please remove your all your height calculation and just place this below simple function in side your view controller.
IMPORTANT:- Don't give any Height Constraint to your table view if you are using AutoLayout from your storyboard or programatically.
kindly take care of your variable names of tableview and scrollview and replace them respectively .
//MARK:- viewDidLayoutSubviews will call after dynamic height calculation automatically
override func viewDidLayoutSubviews() {
//isScrollEnabled of table view should be dissable because our table is inside scrollview.
tableView.isScrollEnabled = false
//if above tableView.contentSize.height not zero and giving acurate value then proceed further and update our parent scroll view contentsize height for height.
print(tableView.contentSize.height)
//place some bottom peeding as you want
let bottomPedding:CGFloat = 30
//Finally update your scrollview content size with newly created table height + bottom pedding.
scrollview.contentSize = CGSize.init(width: scrollview.contentSize.width, height:tableView.contentSize.height + bottomPedding)
}
if this will work then fantastic then great , rest you can contact me any time we will figure it out.
asrathoreforiphone#gmail.com
You can use the height of the contentSize by taleView.contentSize.height
Welcome to Stackoverflow!
You can definitely get the contentSize, and get the height from that, of your tableView. I've been using this method and works every time.
One way to do it is to add an observer, like so:
tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
and then override your controller's method, observeValue, like so:
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 {
let height = newSize.height // <----- your height!
}
}
}
}
To add, perhaps it would be best if in your viewWillDisappear or deinit method, remove that observer.
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'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.
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
}
}