UICollectionView cells display weirdly - Swift 3 - ios

I'm trying to display some cells in the UICollectionView, But please see the images attached and the code I've provided below, its very weird.
After days of research I didn't find a helpful answer. Please help.
I'm displaying about 200 cells in a view. This is a weird phenomenon happens on the later half when scrolling.
Also, I only need identical size for all cells. Any insights will be appreciated.
![image2][https://i.stack.imgur.com/aEgGy.png]
![image1][https://i.stack.imgur.com/QKei3.png]
var inset = 2
var dividendWidth = 2 // Number of cells in a row
var dividendHeight = 5 // Number of rows in a visible "page"
func configureCellLayout() {
let layout = UICollectionViewFlowLayout()
navHeight = navigationController?.navigationBar.frame.size.height
tabHeight = tabBarController?.tabBar.frame.size.height
statusHeight = UIApplication.shared.statusBarFrame.height
emptyViewWidth = screenSize.width - inset*(dividendWidth+1)
emptyViewHeight = screenSize.height - navHeight - tabHeight - statusHeight - inset*(dividendHeight+1)
layout.sectionInset = UIEdgeInsetsMake(2, 2, 2, 2)
layout.minimumLineSpacing = inset
layout.minimumInteritemSpacing = inset
modifyDividend()
let itemWidth: CGFloat = emptyViewWidth / dividendWidth
let itemHeight: CGFloat = emptyViewHeight / dividendHeight
layout.itemSize = CGSize(width: itemWidth, height: itemHeight)
collectionView.collectionViewLayout = layout
}

Related

Error when trying to convert NSLayoutDimension to CGFloat

I am currently trying to convert an NSLayoutDimension to a CGFloat, so that I can get my collection view to conform to a certain layout.
let spacing: CGFloat = 3
let width = CGFloat(myCollectionView.widthAnchor)
let height = CGFloat(myCollectionView.heightAnchor)
let itemWidth = width / 3 - spacing
let itemHeight = height / 3 - spacing
var itemSize: CGFloat = 0
if itemWidth >= itemHeight {
itemSize = itemWidth
}
else if itemHeight > itemWidth {
itemSize = itemHeight
}
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsetsMake(20, 0, 10, 0)
layout.itemSize = CGSize(width: itemWidth, height: itemHeight)
layout.minimumInteritemSpacing = spacing
layout.minimumLineSpacing = spacing
myCollectionView.collectionViewLayout = layout
Setting width and height is giving the error of:
Cannot invoke initializer for type 'CGFloat' with an argument list of type '(NSLayoutDimension)'.
I have tried setting as a float and then converting float to cgfloat but that has not worked either.
Does anyone know how to convert NSLayoutDimension to CGFloat?
Any help is much appreciated
Thanks.
if you want width and height of your collection view try the code below:
let width = CGFloat(myCollectionView.frame.width)
let height = CGFloat(myCollectionView.frame.height)

UICollectionviewCell always placing at the edge of the view

my UICollectionViewCells are always placing at the edges of the UICollectionView. There is a middle space always like this.
What is the reason for that? And I want to keep 3 items per row always. But in this way it just set 2. How to fix this issue?
Please help me.
Thanks
UPDATE
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical //.horizontal
layout.itemSize = cellSize
layout.sectionInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
layout.minimumLineSpacing = 1.0
layout.minimumInteritemSpacing = 1.0
layout.minimumLineSpacing=1.0
btncollectionView.setCollectionViewLayout(layout, animated: true)
btncollectionView.reloadData()
Works for both horizontal and vertical scroll directions, and variable spacing
Declare number of cells you want per row
let numberOfCellsPerRow: CGFloat = 3
Configure flowLayout to render specified numberOfCellsPerRow
if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
let horizontalSpacing = flowLayout.scrollDirection == .vertical ? flowLayout.minimumInteritemSpacing : flowLayout.minimumLineSpacing
let cellWidth = (view.frame.width - max(0, numberOfCellsPerRow - 1)*horizontalSpacing)/numberOfCellsPerRow
flowLayout.itemSize = CGSize(width: cellWidth, height: cellWidth)
}

UICollectionView crashes when column count changes

I have a UICollectionview and the number of columns are changing randomly. Every time the column count changes I invoke the following function.
func setTheCollectionViewSize(){
if self.activeLockerList.count > 0 {
self.btnLockerDropdown.setTitle("Lockers Set \(self.activeLockerList[0].lockerId)", for: UIControlState.normal)
}
screenSize = UIScreen.main.bounds
screenWidth = screenSize.width
screenHeight = screenSize.height
//if the collection view layout is not invalidated then the app will crash
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
//setting the collectionview size so there will be no white lines in the drawn boxes
var appropriateScreenWidth = Int(self.screenWidth)
while appropriateScreenWidth % Int(self.numberOfLockersPerColumn) != 0 {
appropriateScreenWidth = appropriateScreenWidth - 1
}
let itemWidth : CGFloat = CGFloat(appropriateScreenWidth) / self.numberOfLockersPerColumn
collectionViewWidthLocal.constant = CGFloat(appropriateScreenWidth)
layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
print(self.numberOfLockersPerColumn)
print(screenWidth)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
layout.sectionInset = UIEdgeInsets(top: 10.0, left: 0, bottom: 10.0, right: 0)
self.collectionView.collectionViewLayout.invalidateLayout()
self.collectionView.setCollectionViewLayout(layout, animated: false)
}
When the cell count for each row increases it works fine. But when the count decreases app crashes at the line self.collectionView.setCollectionViewLayout(layout, animated: false)
As you can see invalidating the collectionview with self.collectionView.collectionViewLayout.invalidateLayout() also didn't help. I've been spending hours and hours still without luck. Thanks in advance.
Did you invoke setTheCollectionViewSize() function before collectionView.reloadData() or after?
It should go in the following order. Try and let me know.
self.collectionView.reloadData()
self.setTheCollectionViewSize()
in other words following should be the order when invalidating collectionview
self.collectionView.reloadData()
self.collectionView.collectionViewLayout.invalidateLayout()
Hope it helps you out.

UICollectionViewFlowLayout minimumLineSpacing not applied to whole collection view - Swift

I'm creating UIView subclass for calendar, wherein UICollectionView with flow layout is used to display dates. The code is :
override func draw(_ rect: CGRect)
{
super.draw(rect)
.
.
configureCalendarView()
.
}
func configureCalendarView()
{
cellWd = Double(self.frame.size.width)/7.0
let layout = UICollectionViewFlowLayout.init()
layout.minimumLineSpacing = 0.5
layout.minimumInteritemSpacing = 0.0
layout.sectionInset = UIEdgeInsetsMake(1, 0, 0, 0)
collectionV = UICollectionView.init(frame: CGRect(x: 0, y:90, width:self.frame.size.width, height:CGFloat(cellWd * 5 + 3.5)), collectionViewLayout: layout)
collectionV?.backgroundColor = self.backgroundColor
collectionV?.isScrollEnabled = false
collectionV?.register(DayCell.classForCoder(), forCellWithReuseIdentifier:reuseID)
collectionV?.dataSource = self;
collectionV?.delegate = self;
collectionV?.backgroundColor = calBgColor //lightgray
self.addSubview(collectionV!)
}
Everything is working fine but minimum line spacing is not having any effect between 2nd and 3rd row. Below is the screenshot :
Whats going wrong ? Any help would greatly be appreciated . Thank you !
layout.minimumLineSpacing = 0.5 translates to 0.5point * scale = pixels.
So if you are running on iphone 6+ 7+ with 3.0 scale, that 0.5 will be 1.5 pixels. And on 2x scale that will be 1 pixel.
To correct this. add this:
layout.minimumLineSpacing = 1.0 / [UIScreen mainScreen].scale;
This way you guaranteed always get 1 pixel spacing.
Edit:
layout.minimumLineSpacing = 1.0f;
CGFloat collectionWidth = self.frame.size.width;
CGFloat days = 7.0f;
CGFloat spacing = 1.0f;
CGFloat itemHeight = floorf((collectionWidth/days) - spacing;

get the actual UICollectionView item spacing

how can I get the actual interItemSpacing for a horizontal flow layout in the UICollectionView ? the only info I can get (and set) is minimumInteritemSpacing.
I need it for the sake of the following small calculation to always display 3 equally width items in the collection view regardless to screen size (i want to replace minimumInteritemSpacing with the current value of interItemSpacing in the formula):
flowLayout.itemSize = CGSizeMake((_collectionView.frame.size.width/3.0)-(flowLayout.minimumInteritemSpacing/3.0), _collectionView.frame.size.height); I put this calculation in viewDidLayoutSubviews and it's not working precisely for some screens because their current interItemSpacing is different than the minimumInteritemSpacing
thanks in advance.
You could get the actual spacing by comparing two sequential cells
Something like
let firstIndex = IndexPath(item: 0, section: 0)
let secondIndex = IndexPath(item: 1, section: 0)
if let att1 = collectionView.layoutAttributesForItem(at: firstIndex),
let att2 = collectionView.layoutAttributesForItem(at: secondIndex) {
let actualSpace = att2.frame.minX - att1.frame.maxX
print (actualSpace)
}
For creating cells with fixing spacing try this:
private let minItemSpacing: CGFloat = 8
private let itemWidth: CGFloat = 100
private let headerHeight: CGFloat = 32
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Create our custom flow layout that evenly space out the items, and have them in the center
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
layout.minimumInteritemSpacing = minItemSpacing
layout.minimumLineSpacing = minItemSpacing
layout.headerReferenceSize = CGSize(width: 0, height: headerHeight)
// Find n, where n is the number of item that can fit into the collection view
var n: CGFloat = 1
let containerWidth = collectionView.bounds.width
while true {
let nextN = n + 1
let totalWidth = (nextN*itemWidth) + (nextN-1)*minItemSpacing
if totalWidth > containerWidth {
break
} else {
n = nextN
}
}
// Calculate the section inset for left and right.
// Setting this section inset will manipulate the items such that they will all be aligned horizontally center.
let inset = max(minItemSpacing, floor( (containerWidth - (n*itemWidth) - (n-1)*minItemSpacing) / 2 ) )
layout.sectionInset = UIEdgeInsets(top: minItemSpacing, left: inset, bottom: minItemSpacing, right: inset)
collectionView.collectionViewLayout = layout
}
For more information, this excellent article:
http://samwize.com/2015/11/30/understanding-uicollection-flow-layout/

Resources