UITableView not shown inside UIStackView - ios

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
}
}

Related

TableView height is not adopting dynamic size

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

TableView inside ScrollView , How to calculate tableview height at runtime?

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.

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

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
}

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