Nested tableView causing childTableView's content getting clipped - ios

I am working with nested tableViews having a parentTableView and childTableView .
parentTableView has the cards and childTableView will have the data which will be available at runtime so I have used resizable cells
Now my problem is when I initially load the tableviews, the childTableView data is getting clipped
But when i reload the parentTableView (I have managed a logic for that) using reloadData(), the data is displaying correctly
I have subclassed my childTableView like --
class SectionsTableView: UITableView {
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return self.contentSize
}
override var contentSize: CGSize {
didSet{
self.invalidateIntrinsicContentSize()
}
}
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
}
This reloadData() is a workaround but it creates issues in other part of my code which I have to explicitly deal with. Has anyone experienced the same ?

Related

Is it possible to add UITableView within a UITableViewController?

Insted of scroll view i used Tableview controller, inside Tableview controller i desined by adding collection view, textfield also i want to add table view inside it.
I implemeted but it crashes the app beacuse of parent table view. Any solution?
Create a custom cell with tableView(Autoheight TableView) inside it. Add cell to your TableViewController.
Custom Cell with AutoHeightTableView
Set isScrolling, bounces of AutoHeightTable to false
Register custom cell to TableViewController
Load custom cell table data
final class AutoHeightTableView: UITableView {
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return self.contentSize
}
override var contentSize: CGSize {
didSet{
UIView.performWithoutAnimation {
self.invalidateIntrinsicContentSize()
}
}
}
}

set dynamic height for the parent child tableView Swift

I have created nested tableView approach, In a view controller I set tableView and then nested tableView for the Cell.
So its like Parent tableView and Child tableView.
For adjusting content height tableView row height autoDimension I have created custom class for it and override intrinsicContentSize but I am still getting scrollbar in nested tableview. ContentSize updating but row height not adjusting. I have already set automatic row height for both the parent and child table view.
How can I avoid this scrollbar when my content expand it should auto set tableView row height?.
Attaching GitHub code reference and images as well.
https://github.com/MasamMahmood/autoHeightTableView
Custom Class Code:
class MyOwnTableView: UITableView {
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
print("ContentSizee \(contentSize)")
return self.contentSize
}
override var contentSize: CGSize {
didSet{
self.invalidateIntrinsicContentSize()
}
}
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
}
Images:

Nest UICollectionView into UITableViewCell

I'm trying to build something
I'm trying to build a tag list view using UICollectionView and nest it into my custom UITableViewCell.
What do I have now
After searching the internet, I find the key to the problem:
Subclass UICollectionView and implement it's intrinsic content size property.
However, when I nest my custom UICollectionView into a self-sizing UITableViewCell, the whole thing doesn't work well. The layout is broken.
No matter how do I change the code, I get one of the following 3 buggy UIs.
The height of the collection view is always wrong, either too small or too large, it can not hug it's content just right.
When I use Debug View Hierarchy to check the views, I find that although the UI is broken, the contentSize property of the collection view has a correct value. It seems that the content size property can not be reflected to the UI in time.
class IntrinsicCollectionView: UICollectionView {
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: collectionViewLayout.collectionViewContentSize.height)
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
isScrollEnabled = false
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
There are many solution about how to create a custom UICollectionView with intrinsic content size. Some of them can work correctly. But when nesting them into a UITableViewCell, none of them works well.
There are also some answer about just nest one UICollectionView into UITableViewCell without other views. But if there are also some UILabel in UITableViewCell, it won't work.
I upload all the code to github. https://github.com/yunhao/nest-collectionview-in-tableviewcell
Thank you!
I'll try to explain what's going on....
To make it easy to understand, in your ListViewController let's work with just one row to begin with:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1 // items.count
}
In your ListViewCell class, add these lines at the end of prepareViews():
// so we can see the element frames
titleLabel.backgroundColor = .green
subtitleLabel.backgroundColor = .cyan
collectionView.backgroundColor = .yellow
In your IntrinsicCollectionView class, let's add a print() statement to give us some information:
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
// add this line
print("collView Width:", frame.width, "intrinsic height:", collectionViewLayout.collectionViewContentSize.height)
return CGSize(width: UIView.noIntrinsicMetric, height: collectionViewLayout.collectionViewContentSize.height)
}
When I then run the app on an iPhone 8, I get this result:
and I see this in the debug console:
collView Width: 66.0 intrinsic height: 350.0
collView Width: 343.0 intrinsic height: 30.0
What that tells me is that the collection view is asked for its intrinsicContentSize before it has a complete frame.
At that point, it fills in its cells, and its layout ends up with a .collectionViewContentSize.height of 350 (this row has six "tags" cells).
Auto-layout then performs another pass... the collection view now has a valid frame width (based on the cell width)... and the cells are re-laid-out.
Unfortunately, the table view has already set the row height(s), based on the initial collection view intrinsicContentSize.height.
So, two steps that may (should) fix this:
In ListViewCell, invalidate the content size of the collection view when you get the tags:
func setTags(_ tags: [String]) {
self.tags = tags
collectionView.reloadData()
// add this line
collectionView.invalidateIntrinsicContentSize()
}
Then, in ListViewController, we need to reload the table after its frame has changed:
// add this var
var currentWidth: CGFloat = 0
// implement viewDidLayoutSubviews()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if view.frame.width != currentWidth {
currentWidth = view.frame.width
tableView.reloadData()
}
}
That seems (with very quick testing) to give me reliable results:
and on device rotation:

Self sizing UICollectionView with FlowLayout and variable number of cells

Please note that this is NOT a question about self sizing UICollectionViewCell.
Is it possible to create self sizing UICollectionView (with UICollectionViewFlowLayout) size of which depends on cells inside it?
I have a collection view with variable number of cells. I would like to constrain width of the collection view and then allow it to expand vertically depending on quantity of cells.
This question is similar to this one CollectionView dynamic height with Swift 3 in iOS but I have multiple cells per row.
Bonus points, if one could still use self sizing cells inside of collection view but it is ok if collection view delegate provides cell sizes.
I don't have enough reputation to comment, but I think
this is the answer
that you are looking for. Code below:
class DynamicCollectionView: UICollectionView {
override func layoutSubviews() {
super.layoutSubviews()
if bounds.size != intrinsicContentSize() {
invalidateIntrinsicContentSize()
}
}
override func intrinsicContentSize() -> CGSize {
return self.contentSize
}
}
#Michal Gorzalczany's answer led me to this answer (for my specific case):
Subclass UICollectionView
class DynamicCollectionView : UICollectionView {
weak var layoutResp : LayoutResponder?
override func invalidateIntrinsicContentSize() {
super.invalidateIntrinsicContentSize()
self.layoutResp?.updateLayout()
}
override var intrinsicContentSize : CGSize {
return self.contentSize
}
override var contentSize: CGSize {
get { return super.contentSize }
set {
super.contentSize = newValue
self.invalidateIntrinsicContentSize()
}
}
}
Protocol LayoutResponder should be adopted by the view which deals on a high level with layout of collection view (I have a relatively complex layout).
protocol LayoutResponder : class {
func updateLayout()
}
extension RespView : LayoutResponder {
func updateLayout() {
self.layoutResp?.setNeedsLayout()
self.layoutResp?.layoutIfNeeded()
}
}
In my case I actually forward updateLayout() even further up the chain.
I guess for simpler layouts you can skip step 2 alltogether.
This is in my opinion is a bit "hacky" so if someone has a better approach I would appreciate if you share.

Adjust the size of UITableView dynamically which would display as a non-scrollable list of content

I'm working on a project in Swift 3.0 and it has a UITableView inside a UIScrollView that has already been set its constraints.
The requirement is to display a list of songs that would return from an array in this UITableView, but the size of this UITableView should dynamically arrange according to the size of the array, so I do not need to scroll the UITableView since I can see all the songs in the UITableView (this UITableView is inside my scrollView, so instead I can use my scrollView do move up or down the list).
How would I achieve this?
You can change height of the tableview based on the content.
var frame = tableView.frame
frame.size.height = tableView.contentSize.height
tableView.frame = frame
If you are using autolayout create outlet for tableview height and assign tableview content size to its constant.
tableviewHeightConstraint.constant = tableview.contentSize.height
1.Create a subclass for tableView and override intrinsicContentSize.
class MyOwnTableView: UITableView {
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return self.contentSize
}
override var contentSize: CGSize {
didSet{
self.invalidateIntrinsicContentSize()
}
}
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
}
2. In Interface builder change the class of your tableView to MyOwnTableView (subclass of UITableView).
Set automatic row height for the table view.
tableView.estimatedRowHeight = 60.0
tableView.rowHeight = UITableViewAutomaticDimension

Resources