I'm trying to show images in a UICollectionView. The code below is responsible for showing the cell.
super.viewWillAppear(animated)
memes = appDelegate.memes
collectionView?.reloadData()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
let space:CGFloat = 3.0
let dimension = (view.frame.size.width - (2 * space)) / 3.0
flowLayout.minimumInteritemSpacing = space
flowLayout.minimumLineSpacing = space
flowLayout.itemSize = CGSize(width: dimension, height: dimension)
flowLayout.sectionInset = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)
}
and
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MemeCollectionViewCell", for: indexPath) as! MemeCollectionViewCell
let meme = memes[(indexPath as NSIndexPath).row]
cell.memeImageView?.image = meme.meme
// Configure the cell
return cell
}
All I see is a blank view controller. When I click the screen, the details VC popups up (as it should). It seems like the information for the view is there but just isn't being rendered on the screen. What changes do I need to make in order to ensure the images show?
Your collectionview delegates are not being called.
you have to write
collectionview.delegate = self
collectionview.dataSource = self
And if the delegates are being called, please check if meme.meme has and image.
it turns out I didn't have the module name set in the collection view controller in storyboard.
1) use default method to provide flowLayout to a collectionView
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: width/3, height: 100)
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets{
return UIEdgeInsetsMake(0, 0, 0, 0)
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat{
return 0
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat{
return 0
}
2) make sure you had initialised in code before you reload your collectionViw
collectionview.delegate = self
collectionview.dataSource = self
3) add breakpoints in numberOfItemsInSectionafter relaoding and check do your meme array really have value the you reload CollectionView and if yes , of course it will after this go to CellforRowAt and check do your memeImageView is getting value or not . print image view using breakpoint just for try
I faced the same issue, when I debugged the issue it was that collectionView is not getting height to be displayed and delegate method cellForItem was not even working.
Solution:
I set me leading and trailing constraint to the parentView outside stackView.
My view hierarchy was parentView->stackView->collectionView.
Comment if you have any question.
Related
I'm trying to have collection view with 2 column but dynamic height.
I have used Autolayout and given required constraints to the Cell
By this way I can calculate the dynamic height but its column grids fails.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MenuListCollectionViewCell
cell.frame.size.width = collectionView.frame.width/2
cell.menuList = self.menuList[indexPath.row]
let resizing = cell.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.fittingSizeLevel)
return resizing
}
This is how I want it to look
Not sure what you mean by "its column grid fails".
Anyway, you need to write a custom collection view layout. The default one (UICollectionViewFlowLayout) allows you to change height of the cells by providing the sizeForItemAt, but that won't change the behavior of the layout that will always arrange cells in rows of the same height (the height of the highest cell).
If I understood correctly, you just want the same layout of this raywenderlich tutorial.
Basically:
Create a subclass of UICollectionViewLayout implementing it's methods:
collectionViewContentSize: return width and height of the collection view content
prepare: where you can calculate the sizes of cells and
collectionView content
layoutAttributesForElements: where you
return an array of UICollectionViewLayoutAttributes in the given
rect
layoutAttributesForItem: where you return the same kind
of attributes, this time specific for an item
assign an object of this class to the collection view layout property
you can use this
This code is somehow written that you can change section inset or minimumInteritemSpacing and this calculate and resize with this parameters
you can use this code or download project from Github
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let numberofItem: CGFloat = 2
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let collectionViewWidth = self.collectionView.bounds.width
let extraSpace = (numberofItem - 1) * flowLayout.minimumInteritemSpacing
let inset = flowLayout.sectionInset.right + flowLayout.sectionInset.left
let width = Int((collectionViewWidth - extraSpace - inset) / numberofItem)
return CGSize(width: width, height: width)
}
I was able to achieve your desired outcome by doing the following.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (self.view.frame.size.width/2 - 5), height: 100)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = customCollectionView.dequeueReusableCell(withReuseIdentifier: "test", for: indexPath)
cell.layer.backgroundColor = UIColor.red.cgColor
return cell
}
Make sure you have implemented all the correct delegates
UICollectionViewDelegate
UICollectionViewDatasource
UICollectionViewDelegateFlowLayout
where you see height: make it your own desired height as you have specified in your question.
KEY: Make sure in your storyboard you have set the estimate size of the collection view to NONE -> otherwise the code will not work as expected
I came across an issue in collection view in our project -
Collectionview consist of 4 sections. Each section consist of 1 cell.
If we keep height zero for a collection view cell in section 0, cellforrrowatindexpath will not be called for section zero. The problem we are facing is cellforrowatindexapath will not be called for subsequent sections as well. Collection view will not display anything on the screen though other section cells heights are provided.
Updated:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
Switch indexpath.section {
case 0: return CGSize(width: UIScreen.main.bounds.width, height: 0)
case 1: return CGSize(width: UIScreen.main.bounds.width, height: 200)
case 2: return CGSize(width: UIScreen.main.bounds.width, height: 300)
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
Switch indexpath.section {
case 0: return A()
case 1: return B()
case 2: return C()
}
}
If anyone has better understanding of why collection view works in this way please let me know.
If you use storyboard, try to change Collection View's Estimate Size to None in Size Inspector section.
Please check:
I had a similar issue once. It might be resolved by returning CGFloat.leastNonzeroMagnitude or 0.1, instead of 0.
Add this in viewDidLoad():
if let fl = theCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
fl.estimatedItemSize = CGSize(width: 1, height: 1)
}
Now sizeForItemAt and cellForItemAt will be called for your additional sections.
I created UIImageView inside CollectionViewCell. I wanted for the layout to have the same spacing between the left most cell to left edge as spacing between inner cell, and same spacing for right most cell to the right edge of the screen. The current layout I have is like this:
In the image above the left edge to left most cell has 10 points spacing while the inner cells spacing is twice of that. That is because I have the UIImageView with 10points smaller in all 4 directions (up,down,left,right) against the CollectionViewCell.
I have spend significant time on this issue including searching StackOverflow but can't seems to find the answer.
I have no issue to create N number of cells in a row as per this posting and I've also played with the cell spacing as per this posting
I managed to find the solution to the above problem.
I did the following:
Remove all inner spacing between UIImageView and the CollectionViewCell. Then remove any offset and make the size match. Put constraint of 0 on all 4 directions.
Use the second reference mentioned above as template. I updated my ViewController with following contents:
extension ToDoCategoryViewController: UICollectionViewDelegateFlowLayout {
fileprivate var sectionInsets: UIEdgeInsets {
return UIEdgeInsetsMake(0, Constant.hardCodedPadding, 0, Constant.hardCodedPadding)
}
fileprivate var innerSpacing: CGFloat { return Constant.hardCodedPadding }
fileprivate var rowSpacing: CGFloat { return Constant.hardCodedPadding }
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var itemPerRow: CGFloat {
switch collectionView.bounds.width {
case 300..<500:
return 3
case 500..<800:
return 4
default:
return 5
}
}
let innerSpacingCount = itemPerRow - 1
let edgesPadding = Constant.hardCodedPadding * 2.0
let itemWidth = (collectionView.bounds.width - (innerSpacing * innerSpacingCount + edgesPadding)) / itemPerRow
let itemHeight = itemWidth * CGFloat(1.25)
return CGSize(width: itemWidth, height: itemHeight)
}
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 rowSpacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return innerSpacing
}
}
In my code elsewhere, I set the Constant.hardcodedPadding value, e.g. CGFloat(30.0)
Thus, I get the following view:
my qustion is simple.I have searched a lot and i'm familiar with UICollectionView but never saw this behavior of UICollectionView. please don't answer quickly and please read all my question.
I'm working on a project that uses collectionView (and auto layout). my collectionView is paging enabled and is working fine. but when I scroll it too fast (I mean very too fast) it scrolls two pages and shows the contents for next next row (previous previous row). also this code :
decelerationRate = UIScrollViewDecelerationRateFast
didn't work.
I created a temp project that have 1 controller and a simple collectionView (that is paging enabled and the cells's size are same as the viewController size). and there is just a label in the cell. the lable number is same as the collectionView row. also set this:
decelerationRate = UIScrollViewDecelerationRateFast
when I scroll it very fast from 1 to 2, it stops on 3 or vice versa.
this is my temp project code (that is auto layout):
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let collectionViewLayout = UICollectionViewFlowLayout()
collectionViewLayout.sectionInset = UIEdgeInsets.zero
collectionViewLayout.minimumLineSpacing = 0
collectionViewLayout.minimumInteritemSpacing = 0
collectionViewLayout.scrollDirection = UICollectionViewScrollDirection.horizontal
self.collView.collectionViewLayout = collectionViewLayout
self.collView.decelerationRate = UIScrollViewDecelerationRateFast
}
func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollCell
print("cell For Item At Row \(indexPath.row)")
cell.lblNumber.text = "\(indexPath.row)"
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: collectionView.frame.size.width, height: collectionView.frame.size.height)
}
anybody knows what should I do? is it a normal behavior for UICollectionView?
I have a UICollectionView that displays two kind of cells according to whether the item in its datasource is a "folder" or a "photo"
It appears that when the UICollectionView is populated with both type of cells and when there are less than 3 number of "photo" cells to show per row, the cells are not being left-aligned one next to each other but rather try to fill out the whole empty space of the row.
Preview 1:
Preview 2
However when the exact UITableViewCollection displays only "photos" cells, it seems to be behaving normally:
Here are the delegate methods that handle the cell sizes and spacing:
extension DropboxBrowserViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize
{
let folderTypeCellHeight = CGFloat(64.0)
let collectionViewWidth = collectionView.bounds.size.width
let numOfPhotosPerRow = CGFloat(3.0)
let layout = collectionViewLayout as! UICollectionViewFlowLayout
let insets = (layout.sectionInset.left, layout.sectionInset.right)
// Go full width if the cell displayed a folder name
if let _ = listingsDataSource?.listings[indexPath.row] as? Files.FolderMetadata {
return CGSize(width: collectionViewWidth, height: folderTypeCellHeight)
}
// return size to fit `numOfPhotos` photos per row
else {
let cellSpacing = SavePhotosCollectionViewCell.cellSpacing
let cellSize = (collectionViewWidth - insets.0 - insets.1 - (CGFloat((numOfPhotosPerRow - 1)) * cellSpacing)) / numOfPhotosPerRow
return CGSize(width: cellSize, height: cellSize)
}
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat
{
return 2.0
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
Any ideas on why this might be happening?
Thanks
EDIT
The storyboard looks like this. I don't why the "photos" cell is being displayed in the middle over there and if this has something to do with the issue.