Strange issue with updating UICollectionView size - ios

I have a UICollectionView, which initial height is 50.
I want to update it when I know the size of the cell.
For what I do:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let categoriesCount = 16
let numberOfItemsInRow = 3
let numberOfRows = ceil(CGFloat(categoriesCount) / CGFloat(numberOfItemsInRow))
let newHeight = (numberOfRows * cell.frame.size.height)
collectionHeight.constant = newHeight
collectionView.collectionViewLayout.invalidateLayout()
}
But despite of calling invalidateLayout - the height of the collectionView stays the same - 50.
But when I switch screens it shows the correct height when I open that screen with UICollectionView for the second time.
What I am doing wrong and how can I update the height of my UICollectionView?
I even tried collectionView.updateConstraints() after the collectionView.collectionViewLayout.invalidateLayout but still no luck
UPDATE
I've tried also
collectionView.layoutIfNeeded()
self.superview?.layoutIfNeeded()
but no result.

For this, you might have to call the self.view.layoutIfNeeded() method to force the view to update it's layout.
As per the documentation:
...this method lays out the view subtree starting at the root.
hence, updating your view
For more info, you can read this thread where you can pick bits and pieces to add to your solution.

Instead of trying to update the collectionView later based on the size of the cell, can we not use estimated height adjustment of the cell.

Change the Estimated size option in size inspector of collection view to "None" from "Automatic".
See the Image Below for reference.
enter image description here
I tried many answers given in different threads, none worked in my case but this one.
Check https://stackoverflow.com/a/59783595/11806784 this thread for reference.

Related

Adding UILabel/UIImageView to UIContentViewCell disturbs it's size

I have a UICollectionViewCell defined in storyboard which has a UILabel added to it's contentView. Collection view uses a flow layout and I return a fixed size of cell in flowlayout delegate as follows:
let sizeOfItem = CGFloat(210.0)
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: sizeOfItem, height: sizeOfItem)
}
I added the following constraint for UILabel in storyboard and the cell automatically starts resizing itself to match the text size in UILabel. This is not what I want. I want the cell to be fixed size and label to autoshrink instead.
I even tried setting contentHuggingPriority of label to lowest value (i.e. 1). This stops the cell from auto-shrinking if the text in label is small. But still the cell grows when the text is big. I don't want this to happen either. I want the cell to be fixed size as returned by sizeForItem in delegate and the label to adapt it's font size.
EDIT: I also set contentCompressionProperty to lowest and it then works. But I am wondering what is the right way to fix this kind of scenario where contentView does not depend on subviews.
EDIT 2: The problem also appears when having UIImageView in contentView and the image is bigger or smaller than defined cell size. Setting intrinsic content size of UIImageView does not help.
To get the exact cell size as specified in the delegate method, you can't use an equal width constraint since then it will be a dynamic-size cell.

Set dynamic height constraint on collectionviewcell which is in uitableviewcell based on image height

I am trying to add custom height constraint to collection view cell which is present in uitableview cell. The height of constraint is dependent on image which needs to be fetched from url(Using sdwebimage to fetch image in collectionviewcell). Trying to to update constraint in sizeforitem in collectionviewcell. The problem i am facing because of this is that initially fixed height is getting set as per storyboard constraint and as soon as i am scrolling the height constraint is getting updated which is creating problems.
Need to know following
1)Need to know a proper way to implement this thing
2)Where and how should i update constraint so that the tableviewcell as well as collectionviewcell will be set to exact height as of image
3)Exactly where should i download images from sdwebimage
https://i.stack.imgur.com/brWvy.png
You can try to set height for collection view cell in
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {}
I think this will be helpful to you.
This function will be called every time once user start to swipe Up/Down or Left/Right in the case of the Horizontal collection view.

Why on Xcode 11, UICollectionViewCell changes size as soon as you scroll (I already set size in sizeForItem AtIndexPath:)?

I have collectionview inside tableview cell and I use nib for my collection view cell (in which I use autolayout for my imageview and labels and it is on Freeform mode). I'm setting cell size in tableviewcell class which is handling the delegate for collectionview inside it by this method:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 200, height :150)
}
It always works but after Xcode 11 it doesn't.
I have the same problem. And my solution is to change the Estimate size to None in the Xcode 11.
You set collectionview Estimate size to None in Xcode 11. The reason for this is that cells in a UICollectionView can now self-size with Auto Layout constrained views in the canvas. To opt into the behavior for existing collection views, enable “Automatic” for the collection view’s estimated size, and “Automatic” for cell’s size from the Size inspector.
If deploying before iOS 13, you can activate self sizing collection view cells by calling performBatchUpdates(_:completion:) during viewDidLoad()
Ref:
https://developer.apple.com/documentation/xcode_release_notes/xcode_11_release_notes
As Anh Tuan said in another answer here, you just need to change the Estimate Size to none in the Size Inspector of the Collection View from the Storyboard.
But if you wanna do this programmatically, you can try this code:
let layout = myCollectionViewReferenceHere.collectionViewLayout as! UICollectionViewFlowLayout
layout.estimatedItemSize = .zero
This problem is coming in Xcode 11. Go to the attribute inspector and change the estimateSize to None. will fix every thing.

When UICollectionView will fill cells and get height in swift

I have a uiCollectionView with some elements. I need to know the collectionView's height before viewDidAppear to do some stuff magic. I use the below snippet code:
collectionView.collectionViewLayout.collectionViewContentSize.height
// or
collectionView.contentSize.height
However, this is only return true value in viewDidAppear and in viewWillAppear and viewDidLoad others return 0 (before viewDidAppear I need that). I need to know about collectionView's lifecycle and where the elements fill in the collectionView to determine it's height.
If you want to know about height of UICollectionView then use the code
collectionView.frame.size.height
If you want to know how much scrollable height is then use
collectionView.contentSize.height
The datasource methods of UICollectionView are called just before the viewDidLayoutSubviews function.
UICollectionView's loading is an asynchronous process so you can't be sure the loading is completed before viewDidAppear is called.
I suggest you to use KVO to observe value changes of your collectionView's contentSize, and perform your magic when the value changes.
Another option "dirty" option from my pov is to do something like
collectionView.reloadData()
DispatchQueue.main.async{
// This will be called when after reloadData() completes
}
Implement the following UICollectionViewDelegateFlowLayout
method which returns the size of specified item's cell before rendering it.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = collectionView.frame.size.height
// Your logic
}
How can you get contentSize of collectionView before the containing view(ViewController's view) is loaded?. I think you forgot the Life cycle of view.
If your controller contains subViews(like collectionView, tableView). Then first of all main parent view is loaded(viewDidLoad, viewWillApper or viewDidApper) and after that all subview is directly loaded in according to view hierarchy.
So you can only get full correctly contextSize after collectionView completely loaded. If you trying to access contentSize before view loaded then it return default value which is 0. So if you want to do some magic animation on collectionView you can hide when initialise, And after complete collectionView is loaded then Unhide it and perform animation.

How to approach creating Dynamic UICollectionView Item Sizes?

I've been working on this problem for probably 24 hours now and I can't seem to find out how you're intended by Apple to set the dynamic height of a CollectionViewCell.
This is where I'm completely lost:
1.) I need each Cell's frame size in my UICollectionView to be determined by the UILabel's text within each cell. The UILabel's frame is constrained to the edges of the cell, so simply grabbing the UILabel.frame's height gives me the precise value I need.
2.) 1 BIG PROBLEM, the UICollectionView's sizeForItemAtIndexPath's function is executed first before the UILabels are even populated in the UICollectionView's cellForItemAtIndexPath function.
Below is a hack I've tried which failed misrably:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MsgCell", forIndexPath: indexPath) as! CustomCollectionViewCell
cell.txtLabel.text = dummyChatFeed[indexPath.row].messageBody
cell.txtLabel.sizeToFit()
cell.lineBreakMode = NSLineBreakMode.ByWordWrapping
// store the value of this proper label height back into the array:
dummyChatFeed[indexPath.row].frameHeight = cell.txtLabel.frame.height
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
// grab that proper label height again:
var theHeightThatFits = dummyChatFeed[indexPath.row].frameHeight
return CGSize(width: Double(self.view.frame.width), height: Double(theHeightThatFits))
}
The dummyChatFeed is an NSObject Class which contains only these 4 properties:
var messageBody = ""
var author = ""
var frameHeight = CGFloat(0)
var indexPathRow = 0
This solution I've tried barely works, and it's buggy as hell since the height of the Cell depends on the label within it. But what is the best way to go from here?
I thought initially that it's best to keep an array of objects with one property of the object that stores the label text while another property of the object should store the value of the recommended height, but I've just learned the hard way that it would never work.
Am I suppose to programmatically declare a UILabel within my dummyChatFeed and calculate the width and height from there based on text and font?
I need my app to be backwards compatible with iOS 8.4 so I can't use UIStackViews.

Resources