I have a UITableView that has few cells with different heights. Each row's is dynamically adjusted according to the content it contains.
I have a custom UITableViewCell with a UICollectionView in it that should be resized according to how many cells it has (all the cells should be visible, without inside scrolling).
I also have a UITableViewCell with another UITableView that should be resized according to how many cells it has (all the cells should be visible, without inside scrolling).
The problem is that I don't have the contentSize of the collectionView and the tableView while heightForRow:atIndexPath gets called so I can't set the values to something right.
I've tried to set it to UITableViewAutomaticDimension and to set the cell.contentView.frame.size.height to the contentSize when it got set (added an observer on "contentSize") but then the cells were on top of each other (the collectionView was ontop of the tableView instead of above it).
The tableView code is a regular tableView code.
The collectionView code is:
self.collectionView.collectionViewLayout = UICollectionViewLeftAlignedLayout()
let collectionViewFlowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
collectionViewFlowLayout.estimatedItemSize = CGSize(width: 34, height: 50)
What is the best way to adjust the size of the tableView and the collectionView?
Thank you!
You can just ask UITableView to adjust its cell height again with this piece of code:
self.tableView.beginUpdates()
self.tableView.endUpdates()
Related
I have an iOS 14 collectionView, configured with compositional layout.
You can scroll vertically through the collectionView, and you can scroll horizontally within the sections.
I would like to know if there is a way to scroll programmatically, within a section?
Something like collectionView.setContent(offset: 100, inSection: 2)
And is there a way to get the contentOffset of a particular section?
I can't find any function to do that.
Sincerely,
Jery
Its a hack but for such sections the parent is a UIScrollView which you can get a reference to by referencing a cell in the section and then referencing its superview.
Like this:
let cell = collectionView.cellForItem(at: indexPath)!
let scrollView = cell.superview as! UIScrollView
scrollView.setContentOffset(<#CGPoint#>, animated: true)
The table view cell has a tableview inside it. This tableview has sections and rows, when user clicks on section the rows shows or collapse. but I can not get the content height of this cell.
on section's button click I am doing this:
self.tableViewViewHeightConstraint.constant = CGFloat.greatestFiniteMagnitude;
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
self.tableViewViewHeightConstraint.constant = self.tableView.contentSize.height
delegate?.reloadMainTable(with: self.indexPath)
after setting the constraints I am reloading this cell in main tableview with the delegate but not able to get a fixed height
Try to check contentView height of UITableviewCell
For more detail of contentView
I have a horizontal UICollectionView that's contained inside a UICollectionView header of a vertical UICollectionView. The header of the vertical UICollectionView has a dynamic height that changes due to user interaction (dragging the outer UICollectionView down expands it, scrolling up collapses it). The horizontal UICollectionView is constrained to the size of the header and will also shrink in height when the header is reduced in height. When this happens I get this error
the behavior of the UICollectionViewFlowLayout is not defined because:
2018-07-19 13:42:09.959 IDAGIO[81891:2239798] the item height must be
less than the height of the UICollectionView minus the section insets
top and bottom values, minus the content insets top and bottom values.
2018-07-19 13:42:09.959 IDAGIO[81891:2239798] Please check the values
return by the delegate.
because the UICollectionView shrinks in height, but the cells of it keep their old size and are therefore higher than allowed (I only get this error while scrolling up, so it's not related to any insets, the heights are fine, they just don't match exactly at that point in time).
I already tried to call collectionView.collectionViewLayout.invalidateLayout() every time I get a scroll event of the vertical collection view and then return the collection view size in
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
but the layout update then is always a bit too late, so I still get the above error. Also it looks weird as the cell height also visually is a bit behind.
My question: is there a way to dynamically respond to height changes of a horizontal UICollectionView so the cell height is updated accordingly and in time? (Maybe setup some height constraint that automatically keeps the cell height equal to the collection view height?)
I know about dynamic cell sizing, but that usually means to constrain a cell to the size of it's content. What I want to achieve is to always constrain the height of the cell (and it's content) to the collection view height.
I found an easy solution myself, without having to pass or store the scroll offset at all:
Just subclass UICollectionViewFlowLayout, override shouldInvalidateLayout and set the itemSize to the size of the new bounds.
override func shouldInvalidateLayout(forBoundsChange: CGRect) -> Bool {
if !forBoundsChange.size.equalTo(collectionView!.bounds.size) {
itemSize = forBoundsChange.size
return true
}
return false
}
In Swift 4.2
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
if !newBounds.size.equalTo(collectionView!.bounds.size) {
itemSize = newBounds.size
return true
}
return false
}
UICollectionView has a property derived from UIScrollView called contentinsetadjustmentbehavior, setting that to false should fix your issue if you are setting the size of the items on the collectionView to the size of the bounds of the collectionView.
Because the scrollView can be adjusting the insets which in turns changes the collectionView.adjustedContentInset
https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior
So basically setting:
collectionView.contentInsetAdjustmentBehavior = .never
Reloading data on scroll event is one of the options but it is definitely not the best one because of all the calculations involved in reloading data again and again. You'll probably find a better solution by subclassing UICollectionViewLayout and do all the calculations on prepare(). Then you can just use delegation to pass the scroll offset to your custom layout and make the cell sizing accordingly.
I have tableview cell like this.
And I update that image height constraint and reload. I am using auto-resizing cell and testing on iOS 10.
How to animate UITableViewCell height using auto-layout?
imageHeightConstraint.constant = 200 //example only. it will be dynamic. Some image height will be 200, 400, etc
self.contentView.layoutIfNeeded()
UIView.setAnimationsEnabled(false)
self.delegate?.getTableView!().beginUpdates()
self.delegate?.getTableView!().endUpdates()
UIView.setAnimationsEnabled(true)
I tried to reload only 1 row too and not okay.
self.contentView.layoutIfNeeded()
let indexPath = IndexPath(item: self.tag, section: 0)
self.delegate?.getTableView!().beginUpdates()
self.delegate?.getTableView!().reloadRows(at: [indexPath], with: .none)
self.delegate?.getTableView!().endUpdates()
Problem is it doesn't change height at all unless I reload table like this.
imageHeightConstraint.constant = 200
self.delegate?.getTableView!().reloadData()
But I don't want to call reloadData since it will reload all visible cell and resource intensive (laggy too when user scroll quickly). How shall I update my image constraint properly and update table view cell (auto-resizing) height correctly?
Edit - Using with intrinsic size
I delete my height constraint and just use property of intrinsic size in UIImageview. Then height is correct but width is too big (since it is intrinsic). How shall I do?
ImageViews have intrinsic size. That means they take size based on the content of image. Unless you apply the width and height constraint on image ur imageView should be able to resize itself as per the image size.
You can make use of tableView's height property
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 100
Now select your cell and set the leading, trailing,top and bottom constraint. Make sure not to set height/aspect ratio constraint manually.
Now if you simply load the image to imageView, cell will automatically take size based on the size of the image.
Edit:
This works if the image you are loading is in specific size range. If you want to scale down the image or set the height constraint to image and update its height once you download the image, you need not reload the whole tableView rather you can reload a specific cell in tableView
self.tableView.reloadRows(at: [yourIndexPath], with: .none)
In this case make sure you implement
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
}
EDIT:
Set your imageView content mode to AspectFit or AspectFill from ScaleTofill
I have a table view with a bunch of cells (custom cell, which only has its content view).
In tableView:cellForRowAtIndexPath: I'm adding a predefined UIView (which has several subviews) to the content view of the custom cell. I set up all constraints for the UIView and its subviews before.
Last but not least, I set the vertical and horizontal constraints for the content view of my custom cell (superview) and the UIView, which was added before (subview).
The constraint strings look like this:
H:|[view]|
V:|[view]|
Unfortunately, I still get the default height for all table view cells. I'm wondering If there's a way to let auto layout do the calculation of the height automatically according to content size.
Check out my detailed answer to this question here: https://stackoverflow.com/a/18746930/796419
It takes a bit of work to set up, but you can absolutely have Auto Layout constraints driving a completely dynamic table view without a single hardcoded height (and let the constraint solver do the heavy lifting and provide you with the row height).
Auto Layout won't help with the cell height. You'll need to set that in tableView:heightForRowAtIndexPath. I guess you're probably asking this because your cell heights are variable, not fixed. i.e., they depend on the content.
To resolve that, pre-calculate the cell heights and store them in an array. Return the value for the appropriate indexPath in the tableView:heightForRowAtIndexPath method.
Be sure to calculate content sizes on the main thread, using sizeThatFits of UILabel classes and such like.
If your calculation is intensive, do the majority of it off main apart from the view related methods such as sizeThatFits.
I solved the problem by using CGSize size = [view systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; in tableView:heightForRowAtIndexPath:.
To set automatic dimensions for row height, ensure following steps to make, auto dimension effective for cell/row height layout.
Assign and implement dataSource and delegate
Assign UITableViewAutomaticDimension to rowHeight & estimatedRowHeight
Implement delegate/dataSource methods (i.e. heightForRowAt and return a value UITableViewAutomaticDimension to it)
Swift:
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Don't forget to set dataSource and delegate for table
table.dataSource = self
table.delegate = self
// Set automatic dimensions for row height
// Swift 4.2 onwards
table.rowHeight = UITableView.automaticDimension
table.estimatedRowHeight = UITableView.automaticDimension
// Swift 4.1 and below
table.rowHeight = UITableViewAutomaticDimension
table.estimatedRowHeight = UITableViewAutomaticDimension
}
// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Swift 4.2 onwards
return UITableView.automaticDimension
// Swift 4.1 and below
return UITableViewAutomaticDimension
}
For label instance in UITableviewCell
Set number of lines = 0 (& line break mode = truncate tail)
Set all constraints (top, bottom, right left) with respect to its superview/ cell container.
Optional: Set minimum height for label, if you want minimum vertical area covered by label, even if there is no data.