So I have UICollectionView on my Controller
UICollection view consist of two sections.
So when I start scrolling, it changes cell sizes
My code for layout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let section = indexPath.section
let row = indexPath.row
if section == 0 {
if (row == 0){
let width = view.frame.width
return.init(width: width, height: 120)
}else{
let width = view.frame.width
return.init(width: width, height: 50)
}
}else{
//
//return.init(width: 200, height: 120)
let width = collectionView.bounds.width / 2 - 10
return.init(width: width, height: 253)
}
}
Producs are in second section, but it looks like it gives a width from first section
Make sure estimateSize is set to NONE in storyboard.
Your error the first size for section 0 but anther section you changeded the cell size you can use this code :
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.bounds.width / 2 - 10
let height = collectionView.bounds.height
return.init(width: width, height: height)
}
Related
I am trying to have 2 collectionView cells per row in my CollectionView, I want them to both be half of the width of the collectionView, but when doing that it only displays 1 cell per row, this is the code I have to size it:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.collectionView.frame.width / 2, height: 190)
}
I'm not sure why this shouldn't work, can anyone help out?
When I subtract some width it displays 2 per row, but then the cells' width are smaller than half of the screen size
You need to set minimumLineSpacing to 0
layout.minimumLineSpacing = 0
Or
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
Had to set spacing to 0 in the UICollectionView attribute inspector
Sometime you must use both via collectionViewDelegateFlowLayout and second like below:
func setupCollectionView() {
let layout = UICollectionViewFlowLayout()
layout.sectionInset = .zero
layout.minimumLineSpacing = 0 //as per your requirement
layout.minimumInteritemSpacing = 0 //as per your requirement
layout.scrollDirection = .vertical
layout.itemSize = CGSize(width: self.yourCollectionView.frame.width / 2, height: 190)
self.yourCollectionView.collectionViewLayout = layout
}
I've a fixed grid of collection view cells (UICollectionView) but the cells in the bottom row always appears with a slightly smaller width on screen. The frame size (or bounds) and calculated width used within collectionViewLayout: UICollectionViewLayout sizeForItemAt are the same for all rows.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let settings = currentContents[indexPath.item]
let height = CGFloat(30)
// https://stackoverflow.com/questions/54915227/uicollectionview-remove-space-between-cells-with-7-items-per-row
var cellWidth = CGFloat()
let availableWidth = collectionView.bounds.size.width
print("available width", availableWidth)
let minimumWidth = floor(availableWidth / collectionContents.cellsPerRow5)
print("minmum width", minimumWidth)
cellWidth = minimumWidth * settings.portion - 1
print("cell width", cellWidth)
return CGSize(width: cellWidth, height: height)
}
I'd like to get the bottom row to line up with the other rows, but can't imagine what is happening that is changing the widths after returning the value in the layout delegate method (or how to fix).
UIKit does not like values with more than 2 decimals, round them or it will do it for you.
Here, UICollectionViewFlowLayout rounds your cell size and starts to fill the lines with an "interitemspacing" at least equal to the minimumInteritemSpacing you specified. On the last line, it used exactly the minimumInteritemSpacing value and doesn't fill entirely the line.
Fix it using better rounded values, giving the illusion that all is perfectly aligned.
I usually use those extensions:
extension CGFloat {
func xx_rounded(_ rule: FloatingPointRoundingRule = .down, toDecimals decimals: Int = 2) -> CGFloat {
let multiplier = CGFloat(pow(10.0, CGFloat(decimals)))
return (self * multiplier).rounded(.down) / multiplier
}
}
extension CGSize {
func xx_rounded(rule: FloatingPointRoundingRule = .down, toDecimals: Int = 2) -> CGSize {
return CGSize(
width: width.xx_rounded(rule, toDecimals: toDecimals),
height: height.xx_rounded(rule, toDecimals: toDecimals)
)
}
}
Change:
return CGSize(width: cellWidth, height: height)
to
return CGSize(width: cellWidth, height: height).xx_rounded()
Just replace
cellWidth = (minimumWidth * settings.portion) - 0.5
WITH
cellWidth = (minimumWidth * settings.portion)
Specified minimumInteritemSpacingForSectionAt to 0.5 instead of 0
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0.5
}
CVTVCell.swift
Just reload your UICollectionView in awakeFromNib method after your data set into currentContents. Please refer below sample code.
override func awakeFromNib(){
super.awakeFromNib()
currentContents = collectionContents.allEndings[0]
self.cView.reloadData()
}
Download code from here
The best way I found was to capture the remaining space (minus 1 to avoid rounding up) after calculating the initial column width, and then use this remaining space for additional columns.
For a two column example:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
if indexPath.item == 0 || indexPath.item == 1
{ rowHeight = 40 }
else { rowHeight = 30 }
var cellWidth = CGFloat()
let minimumWidth = floor(availableWidth / numberOfColumns)
var columnPortion = CGFloat()
let columnNumber = indexPath.item % Int(numberOfColumns)
switch columnNumber
{
case 0:
columnPortion = columnPortion1
cellWidth = minimumWidth * columnPortion - 10
remainder = availableWidth - cellWidth - 1
case 1:
columnPortion = columnPortionFinal
cellWidth = remainder
default:
break
}
return CGSize(width: cellWidth, height: rowHeight)
}
I read
How to vertically center UICollectionView content
But when I used the codes here
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let navBarHeight = self.navigationController?.navigationBar.frame.height
let collectionViewHeight = (self.collectionView?.frame.height)! - navBarHeight!
let itemsHeight = self.collectionView?.contentSize.height
let topInset = ( collectionViewHeight - itemsHeight! ) / 4
return UIEdgeInsetsMake(topInset ,0, 0 , 0)
}
But when scrolling collection view this will show incorrect form
so here is my codes because my collection view cells are square and equal to screen width
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
showImagesCV.contentInset = UIEdgeInsets(top: spacing, left: spacing, bottom: spacing, right: spacing)
if let flowLayout = showImagesCV.collectionViewLayout as? UICollectionViewFlowLayout{
cellWidth = UIScreen.main.bounds.width
cellHeight = cellWidth //yourCellHeight = cellWidth if u want square cell
flowLayout.minimumLineSpacing = spacing
flowLayout.minimumInteritemSpacing = spacing
UIView.performWithoutAnimation {
}
}
}
}
extension showImagesViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: cellWidth, height: cellHeight)
}
}
I want the spacing between cells be 0 and each cell that is showing (current page (because the collection view is in paging mode)) be the center of the screen
Regarding the SO Question you mentioned and using, that is to centre the whole section not individual cells. Some of your code might be invalid(needs to be redone).
First and most important, set the constraints of your collectionView for full screen. Remember to take safeAre/TopGuide into account, this will make sure that collection view is below the navigation bar if there is one.
This will make sure that your collectionView is up to date
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myCollectionView.collectionViewLayout.invalidateLayout()
}
Dump/comment below code and set insets to (0,0,0,0) in inspector of collectionView in story board. In same inspector change Min Spacing to 0 for cells and for line, both.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let navBarHeight = self.navigationController?.navigationBar.frame.height
let collectionViewHeight = (self.collectionView?.frame.height)! - navBarHeight!
let itemsHeight = self.collectionView?.contentSize.height
let topInset = ( collectionViewHeight - itemsHeight! ) / 4
return UIEdgeInsetsMake(topInset ,0, 0 , 0)
}
Also remove/comment below code
showImagesCV.contentInset = UIEdgeInsets(top: spacing, left: spacing, bottom: spacing, right: spacing)
if let flowLayout = showImagesCV.collectionViewLayout as? UICollectionViewFlowLayout{
cellWidth = UIScreen.main.bounds.width
cellHeight = cellWidth //yourCellHeight = cellWidth if u want square cell
flowLayout.minimumLineSpacing = spacing
flowLayout.minimumInteritemSpacing = spacing
UIView.performWithoutAnimation {
}
}
Now change your sizeForItemAt like this.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return collectionView.frame.size
}
UICollectionViewCell
Now in your collectionView cell, make sure that your image view is 1:1 and in centre. Also handle constraints of your other UI components.
PS : This is easy approach, other would be to write custom flowLayout
I have setup my collection view in a way, that my first image comes full size to the right and half the size of screen vertically with the following code
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.row == 0 {
return CGSize(width: self.collectionView.frame.width, height: ( self.collectionView.frame.height / 2 ))
}
let width = self.collectionView.frame.width / 3 - 1
return CGSize(width: width, height: width)
}
it works ok. But when collection view has only 2 item then the second item comes in the middle rather that on the left.
How to fix this ??
I have a very simple demo which use UICollectionView to show a list of pictures, I use a customize cell class which contains an UIImage and a Label as below, there is no constraint.
Then I set the size of the cell in code as below. there are 3 columns on each row. all margins are set to 10.
// Calculate size of cell
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let totalSpace = marginLeft + marginRight + (colomnSpacing * CGFloat(numberOfItemsPerRow - 1))
let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(numberOfItemsPerRow))
// 20 is for label height.
return CGSize(width: size, height: size + 20)
}
// Section margin, if you only have one section, then this is the CollectionView's margin.
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: marginTop, left: marginLeft, bottom: marginBottom, right: marginRight) // top, left, bottom, right.
}
// Set row spacing
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return rowSpacing
}
// Set column spacing
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return colomnSpacing
}
Here is the code used to set the image and label size. the image's width takes 1/3 width of the screen(exclude the column space), and the height equals to width. the label width was equal to image width, label's height was set to 20.
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! SearchImageCell
// Set image size
cell.imageView.frame = CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height - 20)
cell.imageView.contentMode = .ScaleAspectFill
let currentItem = self.responseArray[indexPath.row]
let imageUrl = currentItem["imgurl"] as? String
let url = NSURL(string: imageUrl!)
// Load the image
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let data = NSData(contentsOfURL: url!)
dispatch_async(dispatch_get_main_queue(), {
cell.imageView.image = UIImage(data: data!)
})
}
// Set label size
cell.imageDesc.frame = CGRect(x: 0, y: cell.imageView.frame.height, width: cell.frame.width, height: 20)
let shujia = currentItem["shujia"] as? String
cell.imageDesc.text = shujia!
cell.imageDesc.font = cell.imageDesc.font.fontWithSize(14)
cell.imageDesc.textAlignment = .Center
return cell
}
The question is:
When I load the image first, the size of the image was incorrect, like below, you can see the image's height occupy entire height of the cell, and the label was not show up.
But, when I scroll down and up(This makes the cell being reused), the image was resized, and I got the correct result, this is what I want!
I tried to add some debug code to print the size of the cell/image/label, it seems all size was correct.
cell width: 100.0
cell height: 120.0
image width: 100.0
image height: 100.0
label width: 100.0
label height: 20.0
So, what's wrong?
There are some similar question on SO(here, here), but the answers not works for me, like
1. cell.layoutIfNeeded()
2. self.collectionViewLayout.invalidateLayout()
3. self.collectionView?.layoutIfNeeded()
Please help, let me know if you need more code.
set translatesAutoresizingMaskIntoConstraints to true for your image view and label. maybe it works.
Notes: when use IB it will set them to false