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.
Related
I have made a full screen collection view with paging turned on:
Here's how I configure it in my code:
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 0.0
layout.minimumLineSpacing = 0.0
layout.itemSize = UIScreen.main.bounds.size
layout.scrollDirection = UICollectionViewScrollDirection.horizontal
layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0)
allQuotesCollectionView.collectionViewLayout = layout
And in my Storyboard:
The issue I'm having is when scrolling for the first time screen loaded it has bug in scroll animation:
After this bug happens, then scroll becomes smooth.
How can I fix this? I have also used Github Framework but it didn't help. I need any possible solution to this issue.
Will be grateful for any help, many thanks.
After battling with this trouble for 3 or 4 days finally found reason:
My mistake was here:
override func viewDidLayoutSubviews() {
let midIndexPath = IndexPath(row: infiniteSize / 2, section: 0)
allQuotesCollectionView.scrollToItem(at: midIndexPath, at: .centeredHorizontally, animated: false)
}
After I moved this code into viewDidAppear method - everything started to work and bug disappeared.
So, my final code looks like this right now:
override func viewDidAppear(_ animated: Bool) {
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 0.0
layout.minimumLineSpacing = 0.0
layout.itemSize = UIScreen.main.bounds.size
layout.scrollDirection = UICollectionViewScrollDirection.horizontal
layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0)
allQuotesCollectionView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
allQuotesCollectionView.collectionViewLayout = layout
allQuotesCollectionView.isPagingEnabled = true
let midIndexPath = IndexPath(row: infiniteSize / 2, section: 0)
allQuotesCollectionView.scrollToItem(at: midIndexPath, at: .centeredHorizontally, animated: false)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25, execute: {
UIView.transition(with: self.view, duration: 1, options: .transitionCrossDissolve, animations: {
self.allQuotesCollectionView.isHidden = false
})
})
}
I guess you need to setup collection view after screen has loaded with paging on, mainly starting index.
Hope this will help someone in future!
I'm really in need of some help here. I've been trying for a few days but can't seem to fix it...
I'm trying to lay out images in a UICollectionView grid style, with scrolling disabled. So I have 4 images/cells, and I'm trying to completely fill the UICollectionView with these images with 1 spacing between them.
The problem, however is that the spacing between the cells is split between center and the bottom.
I've noticed that when changing the scrollDirection of the UICollectionView from vertical to horizontal, the problem still exists, but just changes direction too, so I'm guessing it has something to do with that.
example:
code:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if isInFeed && self.postImages!.count > 1 {
guard let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout else {
return CGSize()
}
flowLayout.minimumInteritemSpacing = 1
flowLayout.minimumLineSpacing = 1
switch (self.postImages!.count) {
case 2:
// we split the collectionView into 2 parts (+ 2*0.5 spacing)
return CGSize(width: (self.collectionView!.bounds.width/2)-CGFloat(0.5), height: self.collectionView!.bounds.height)
case 3:
// we split the collectionView into 3 parts, the first one taking up half, the other 2 images taking up 1/4th (+spacing)
if indexPath.row == 0 { return CGSize(width: (self.collectionView!.bounds.width/2)-CGFloat(0.5), height: self.collectionView!.bounds.height) }
else { return CGSize(width: (self.collectionView!.bounds.width/2)-CGFloat(0.5), height: (self.collectionView!.bounds.height/2)-CGFloat(0.5)) }
case 4:
// we split the collectionView into 4 parts (1/4th + spacing)
// if indexPath.section == 0 { return CGSize(width: (self.collectionView!.bounds.width/2)-CGFloat(0.5), height: (self.collectionView!.bounds.height/2)-CGFloat(1))}
// else {return CGSize(width: (self.collectionView!.bounds.width/2)-CGFloat(0.5), height: (self.collectionView!.bounds.height/2)-CGFloat(0))}
return CGSize(width: (self.collectionView!.bounds.width/2)-CGFloat(0.5), height: (self.collectionView!.bounds.height/2)-CGFloat(0.5))
default: break
}
}
// If not in Feed or just one image, take up entire collectionView
return CGSize(width: self.collectionView!.bounds.width, height: self.collectionView!.bounds.height)
}
I also tried subclassing UICollectionViewLayout and UICollectionViewFlowLayout, but without success, I've read Apple's documentation, but that doesn't seem to help much.
Hope this explains the problem clearly, I'd be happy to elaborate.
Have a great day!
Edit:
I changed the scroll direction from vertical to horizontal and added 2 extra calculated spacing to each cell just to show off what happens if I do that.
Edit 2:
Finally managed to fix it! (altough not as clean as I'd like it, it does work now)
I put the following code inside of the collectionViewLayout method:
if indexPath.section == 0 {
flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 1 * UIScreen.main.scale)
} else {
flowLayout.sectionInset = UIEdgeInsets.zero
}
Big thanks to everyone trying to help out!
The final solution that worked for me (for anyone that runs into a similar situation):
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
if section == 0 {
return UIEdgeInsetsMake(0, 0, 0, 1 * UIScreen.main.scale)
} else {
return UIEdgeInsets.zero
}
}
Try this code. Add in a function and call in view did load. You might need to make a little adjustments but it works in my case.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
let width = UIScreen.main.bounds.width
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: width / 2, height: width / 2)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
collectionView!.collectionViewLayout = layout
In Storyboard try with Size Inspector with Minimum Spacing Zero and all Section Insects zero.
layout.minimumLineSpacing = 1.0f;
layout.minimumInteritemSpacing = 1.0f;
CGSize collectionSize = collectionView.frame.size;
CGFloat cellsPerRow = 2.0f;
CGFloat spacing = 0.5f;
CGFloat itemWidth = collectionSize.width/cellsPerRow - spacing;
CGFloat itemHeight = collectionSize.height/cellsPerRow - spacing;
return CGSizeMake(itemWidth,itemHeight);
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)
}
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
}
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/