UICollectionViewLayout Not Resizing UIImageViews Within Cells [duplicate] - swift

This question already has an answer here:
UICollectionViewCell not forcing Cell Size
(1 answer)
Closed 2 years ago.
My collectionview images are not getting resized. I'm using the following in the viewcontroller that contains the collectionview, and it sizes correctly when the cells are empty; however with an imageview inside it doesn't behave properly.
Thanks!
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let noOfCellsInRow = 2
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let totalSpace = flowLayout.sectionInset.left
+ flowLayout.sectionInset.right
+ (flowLayout.minimumInteritemSpacing * CGFloat(noOfCellsInRow - 1))
let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(noOfCellsInRow))
return CGSize(width: size, height: size)
}

Add this to your ViewController
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
}
And in Storyboard check if the collection view - Estimate Size is set to None in Size Inspector

Related

Issues with resizing my UICollectionViewCell

I am currently trying to resize my cell within my CollectionView. I have already implemented the dataSource, CollectionViewDelegate and CollectionViewDelegateFlowLayout.
This is the code I have for the latter:
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout, sizeForItemAt
indexPath: IndexPath) -> CGSize
{
return CGSize(width: 100.0, height: 100.0)
}
}
The problem is that when I head over to my storyboard and select my CollectionView and then go to the Attributes Inspector to switch "Estimate Size" from "Automatic" to "None," I would get three columns after running my app [Image #1].
The result I am looking for is to be able to have one middle column with a bigger cell as I show on Image #2.
Has anybody dealt with this problem before?
In order to show a collection view cells as a list, you can change your cell width equals to the width of the collection view. Giving constant width will not work for different device size.
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout, sizeForItemAt
indexPath: IndexPath) -> CGSize
{
let width = collectionView.frame.width
return CGSize(width: width, height: 100.0)
}
}
And give your desired constraint values for the inner rectangular view to achieve the desired look. Make collectionView cell background to clear.

Nested vertical UICollectionView and dynamic height

I have a parent UICollectionView that nests 2 other UICollectionViews:
Parent UICollectionView (Vertical)
First nested UICollectionView (Horizontal) - Grey
Second nested UICollectionView (Vertical) - Red
The parent UICollectionView
I use numberOfSections(in collectionView:) and manually set the number of sections in the parent collection view. In this case I return 2; one for first (grey) nested horizontal UICollectionView and one for the second (red) nested vertical UICollectionView
Then in collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) I return 1. That means each nested UICollectionView will be placed inside a single item.
For each section I dequeue collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) a specific cell [see Nested UICollectionViews step 3] for the first nested horizontal UICollectionView and a specific cell [see Nested UICollectionViews step 3] for the second nested vertical UICollectionView.
Then I set the size of each item in section using collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath). This is where my problem begins. But let me continue on how I setup the nested UICollectionViews.
Nested UICollectionViews
This is a 3-step procedure for each nested UICollectionView:
I create the UICollectionViewCell (the appearance of the cell)
I use a UICollectionViewController to:
Fetch the data from Firebase
Dequeue the cell mentioned in step 1 and pass in the data fetched from Firebase
Set the dimensions of each item using collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath)
I create another UICollectionViewCell that:
Initializes the UICollectionViewController mentioned in step 2
Uses the view of this UICollectionViewController and adds it to the cell's contentView by filling the superview (aka the cell)
contentView.addSubview(UICollectionViewController().view) UICollectionViewController().view.fillSuperview()
The problem
When I set the dimensions of the section where the horizontal UICollectionView (grey) will be, I just need to match the height of the UICollectionView to the height of each cell.
But when I set the dimensions of the section where the vertical UICollectionView will be (red) I need to know the number of cells to calculate the correct height. Otherwise, the section could either be too big or too small.
Any idea how I can notify the parent UICollectionView controller of the number of cells of the vertical nested collection view?
Thanks in advance.
For those nested behaviours I recommend creating UICollectionView by code, instead of IB Storyboard, so you do not have to deal so much with autoresizing constraints. You can create UICollectionView programmatically (instead of UICollectionViewController with desired frame.size and then assigning UICollectionViewFlowLayout to it so you can better controll how big should be your item layout in your nested collectionView based on its frame. Some example code is here:
lazy var layout: UICollectionViewFlowLayout = {
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.itemSize = self.frame.size
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
return layout
}()
lazy var customCollectionView: UICollectionView = {
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.width * 0.5), collectionViewLayout: self.layout)
collectionView.backgroundColor = UIColor.clear
collectionView.dataSource = self
collectionView.delegate = self
let collectionCellNib = UINib.init(nibName: "CustomCell", bundle: Bundle.main)
collectionView.register(collectionCellNib, forCellWithReuseIdentifier: "customCellId")
return collectionView
}()

Collection cell wrongly resized in reloadData

I don't know why but the cells of my collectionView are automatically wrongly resized after a reloadData.
After first init, then after reloadData :
I'm using UICollectionViewDelegateFlowLayout to define the size of the cell but it don't take care of it :
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let paddingSpace = sectionInsets.left * (2 + 1)
let availableWidth = view.frame.width - paddingSpace
let widthPerItem = availableWidth / 2
return CGSize(width: widthPerItem, height: widthPerItem)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets {
return sectionInsets
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return sectionInsets.left
}
When execution come in the cellForItemAt, the cell have the right size, the shadow is created in this function and it have the right width.
One last thing is that only the width is resized, the height stay as I want.
Does any one have an idea why the width is resized wrongly after the reloadData ?
I was having a similar issue when making an extension of the UICollectionViewDelegateFlowLayout and it turns out that setting the collectionView's Estimated Size to "None" in the storyboard (Size Inspector) solved it.
As stated in the Xcode 11 Release Notes:
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(). (45617083)
So, newly created collectionViews have the attribute "Estimated Size" set as "Automatic" and the cell's size is computed considering its subview dimensions, thus ignoring the UICollectionViewDelegateFlowLayout extension methods, even though they are called.

Last UICollectionViewCell appearing different to the others

I'm creating a UICollectionView on my main menu and all the cells look fine bar the last one, where the image is not centered relative to the cell- it appears to be anchored to the top left corner of the cell (not sure about that though).
here is the image that contains the problem.
I didn't really know where to start with this, as all the cells use the same code and constraints.
I checked that it wasn't an image issue by repeating a previously used image (see above screenshot)
My only idea is that the final cell is in a row on its own whereas the other rows have two cells per row.
extension MainMenuViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return mainMenuOptions.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainMenuCell", for: indexPath) as! MainMenuCollectionViewCell
let imageView = cell.mainMenuImage!
let label = cell.mainMenuLabel!
imageView.image = mainMenuImages[indexPath.item]
imageView.frame.size.height = cell.frame.size.width - 5
imageView.frame.size.width = cell.frame.size.width - 10
cell.mainMenuLabel.text = mainMenuOptions[indexPath.item]
let labelText = cell.mainMenuLabel!
label.frame.size.height = Utils.heightForView(text: labelText.text!, font: labelText.font!, width: cell.frame.width)
label.frame.size.width = cell.frame.size.width
label.center.x = cell.frame.size.width / 2
label.center.y = cell.mainMenuImage.frame.size.height + (cell.frame.size.height - cell.mainMenuImage.frame.size.height) / 2
cell.layer.borderColor = UIColor.darkGray.cgColor
cell.layer.borderWidth = CGFloat(0.5)
cell.layer.cornerRadius = CGFloat(10)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainMenuCell", for: indexPath) as! MainMenuCollectionViewCell
let padding: CGFloat = 40
let cellWidth = (collectionView.frame.size.width - padding) / 2
let labelText = mainMenuOptions[indexPath.item]
let cellHeight = cellWidth + Utils.heightForView(text: labelText, font: cell.mainMenuLabel.font!, width: cell.frame.width) + 70
return CGSize(width: cellWidth, height: cellHeight)
}
}
Utils.heightForView() is just a function that calculates the size required for a label to fit all the text. If you need to see it I'll happily add it.
Thanks very much in advance! I hope this is the right amount of code needed but if not let me know and I'll add more.
EDIT: Cell class
class MainMenuCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var mainMenuLabel: UILabel!
#IBOutlet weak var mainMenuImage: UIImageView!
}
When you dequeue a cell in cellForItem it doesn't necessarily have its final size at that point, yet you're depending on that being correct for your code to work.
You should be using autolayout constraints or a stack view inside the cell to give you the correct layout without needing to do any work at dequeue time.
You seem to be trying to give variable heights for your cells as well, which is going to look pretty messy in a flow layout, although it doesn't seem to be making any difference to the cells in the screenshot which I'd expect to have different heights given their titles. Dequeuing a cell in sizeForItem could have some unexpected side effects too, since that's going to mess with the reuse pool.

Swift 4 - sizeForItemAt on UICollectionView is not resizing the entire cell content

I'm trying to dynamically resize a CollectionView cell based using the height of the CollectionView set by Autolayout. The cell has an ImageView and a Label, but only the ImageView is being resized.
My code looks like this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.templateCollectionView.frame.height * 5 / 6, height: self.templateCollectionView.frame.height * 5 / 6)
}
Any idea where I'm going wrong?