UiCollectionView Spacing issue - swift - ios

I am having a spacing issue with my Collection.i Couldn't figure out why. I want the images from the Second row till the last low to be equally spaced. can someone help me to fix this. tnx.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if (indexPath.row == 0) {
return singleUserModel.isevenItems(totalCount: CurrentUser.items.count) ? CGSize(width: self.view.frame.width * 0.4 , height: self.view.frame.height / 4) : CGSize(width: self.view.frame.width , height: self.view.frame.height / 4)
} else {
return CGSize(width: self.view.frame.width * 0.4, height: self.view.frame.height / 4)
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return CurrentUser.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UserDetailCell", for: indexPath) as! UserDetailCell
configureCell(cell: cell, forRowAtIndexPath: indexPath as NSIndexPath)
return cell
}
func configureCell(cell: UserDetailCell, forRowAtIndexPath indexPath: NSIndexPath) {
DispatchQueue.global(qos: .background).async {
cell.ItemImage.image = self.singleUserModel.loadImage(imageUrl: self.CurrentUser.items[indexPath.row])
cell.activityIndicator.isHidden = true
}
}
View look Like this
Actual Output

You need to set layout accordingly, Now your layout will be changed according to screen size So you need to calculate according to one screen size then it will work in other screen sizes:
Now, the layout will like this, In this 2 image will be shown in the width and if you want to set space from left and right side then set layout width accordingly.
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if (indexPath.row == 0) {
return singleUserModel.isevenItems(totalCount: CurrentUser.items.count) ? CGSize(width: self.view.frame.width * 0.4 , height: self.view.frame.height / 4) : CGSize(width: self.view.frame.width , height: self.view.frame.height / 4)
} else {
return CGSize(width: (self.view.frame.width/2), height: self.view.frame.height / 4)
}
}
Remove minimins spacing for cell and lines and section inset will also Zero like below image:

Related

Why is the last row of the collectionView not aligned properly?

I am working on the collectionView layout as below:
But the last row is not getting align properly not sure why?
Here is the collectionView setup code:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(collectionViewCell.self)", for: indexPath) as? collectionViewCell {
cell.imageView.backgroundColor = .black
return cell
}
return UICollectionViewCell()
}
Here is the implementation of the flow layout delegate method:
let sectionInsets = UIEdgeInsets(top: 30.0, left: 20.0, bottom: 30.0, right: 20.0)
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.row == 2 { // This item should occupy the entire available width
let paddingSpace = sectionInsets.left + sectionInsets.right
let availableWidth = collectionView.frame.width - paddingSpace
let widthPerItem = availableWidth
return CGSize(width: widthPerItem, height: (widthPerItem / 2))
}
// adjust 2 cells in the single row
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
let availableWidth = collectionView.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
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
}
What you are seeing is normal for a flow layout. All rows are left and right justified except for the last row, which is left justified only. It’s just like a printed book: the last line of a paragraph does not get spaced out to touch the right margin.
I had the same problem. But I could fix by setting collectionView(_:layout:minimumInteritemSpacingForSectionAt:).

Center horizontal UICollectionView based on content

I have a horizontal UICollectionView which works fine. But I want to center cell when there is no need for scrolling. i.e.
if all cell are able to fit within the view width & user don't need to scroll then Do center the cell
if all cell are not able to fit within the view width & user need to scroll then Don't center the cell (default behaviour)
Graphical example:
UICollectionView that needs scrolling then keep default behavior
UICollectionView that doesn't need scrolling
What I have tried so far:
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var dataList:[String] = ["icon_rotate", "icon_rotate2", "icon_rotate3", "icon_rotate4", "icon_rotate5", "icon_rotate6"]
override func viewDidLoad() {
super.viewDidLoad()
//other stuff
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return (UIDevice.current.userInterfaceIdiom == .pad) ? 20.0 : 10.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return (UIDevice.current.userInterfaceIdiom == .pad) ? 20.0 : 10.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.height * 0.75, height: collectionView.frame.size.height * 0.75)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataList.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "editorCell", for: indexPath) as! EditorCell
cell.iconView.image = UIImage(named: dataList[indexPath.row])
return cell
}
}
This results in default behavior of UICollectionView so I searched in SO and found this post: How to center horizontally UICollectionView Cells?
and I added this code:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
let totalCellWidth = CellWidth * CellCount
let totalSpacingWidth = CellSpacing * (CellCount - 1)
let leftInset = (collectionViewWidth - CGFloat(totalCellWidth + totalSpacingWidth)) / 2
let rightInset = leftInset
return UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: rightInset)
}
This code does work fine when there's few cells to be loaded but I want this to dynamically detect if centering is needed or let the default behavior stay in action.
Thanks
Assuming your current code is working when you the cells will fit - that is, it centers the "row of cells" when desired - change this line:
let leftInset = (collectionViewWidth - CGFloat(totalCellWidth + totalSpacingWidth)) / 2
to:
let leftInset = max(0, (collectionViewWidth - CGFloat(totalCellWidth + totalSpacingWidth)) / 2.0)
That way your insets will never be less than Zero

How to set custom collectionView Cell Frame?

I want first UICollectionViewCell by full width of screen and further UICOllectionViewCell is 2*2.
For that i have tried below code.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell : FirstCell = collectionView.dequeueReusableCell(withReuseIdentifier: "FirstCell", for: indexPath) as! FirstCell
cell.layoutSubviews()
return cell
}
else {
let cell : SecondCell = collectionView.dequeueReusableCell(withReuseIdentifier: "SecondCell", for: indexPath) as! SecondCell
cell.layoutSubviews()
return cell
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.item == 0 {
let width = (UIScreen.main.bounds.size.width - 16)
return CGSize(width: width, height: 200)
}
else {
let width = (UIScreen.main.bounds.size.width - 24) / 2
let height = (UIScreen.main.bounds.size.width * 200) / 320
return CGSize(width: width, height: height)
}
}
But the Output comes as below.
Requirement: First Green Cell is of full Width and 2nd Yellow cell comes not from Center but it comes from left side and 3rd, 4th and so on cell comes 2*2
Any help will be appreciated.
Thanks in Advance.
To achieve this, you should take 3 section in collection view.
section 1 - 1 cell
section 2 - 1 cell (width should be less than half of available width for collection cell)
section 3 - number of cell you wanted
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return section == 2 ? 4 : 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch indexPath.section {
case 0:
return CGSize(width: UIScreen.main.bounds.width - 20, height: 200)
case 1:
return CGSize(width: (UIScreen.main.bounds.width - 30) / 2, height: 200)
default:
return CGSize(width: (UIScreen.main.bounds.width - 30) / 2, height: 200)
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectioncell", for: indexPath)
cell.backgroundColor = .red
return cell
}
}
Your view will look like:

UICollectionView showing 2 items instead of 3

I have implemented my UICollectionView like this
extension MyViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return photos.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath)
cell.backgroundColor = .black
return cell
}
}
extension MyViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let widthPerItem = view.frame.width / 3
return CGSize(width: widthPerItem, height: widthPerItem)
}
}
but instead of 3 items in a row I'm getting 2 items
how can i fix it? i didn't set any UIEdgeInset for my items and sections
Try this:
extension MyViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let numRowItems:CGFloat = 3
let padding:CGFloat = 2
let width = (collectionView.bounds.width / numRowItems) - padding
let height = collectionView.bounds.height - (2 * padding)
return CGSize(width: width, height: height)
}
}
You forgot to account for minimumInteritemSpacing and minimumLineSpacing properties of UICollectionViewFlowLayout - they both default to 10.0 if you didn't set them.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let spacing : CGFloat = (collectionViewLayout as? UICollectionViewFlowLayout)?.minimumInteritemSpacing ?? 0.0
let widthPerItem = (view.frame.width - spacing * 2)/ 3
return CGSize(width: widthPerItem, height: widthPerItem)
}
This is because your screan width can not fit 3 cells. This is because collection view has cell min line spacing property in your storyboard if you try with small width in sizeForItemAt they will fit.
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let widthPerItem = view.frame.width / 3 - ((collectionViewLayout as? UICollectionViewFlowLayout)?.minimumInterItemSpacing ?? 0.0)
return CGSize(width: widthPerItem, height: widthPerItem)
}
in obj c
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
//If you want your cell should be square in size the return the equal height and width, and make sure you deduct the Section Inset from it.
return CGSizeMake((self.view.frame.size.width/2) - 16, (self.view.frame.size.width/2) - 16);
}
swift
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// Compute the dimension of a cell for an NxN layout with space S between
// cells. Take the collection view's width, subtract (N-1)*S points for
// the spaces between the cells, and then divide by N to find the final
// dimension for the cell's width and height.
let cellsAcross: CGFloat = 3
let spaceBetweenCells: CGFloat = 1
let dim = (collectionView.bounds.width - (cellsAcross - 1) * spaceBetweenCells) / cellsAcross
return CGSize(width: dim, height: dim)
}

Swift 3 - UICollectionView Center Cell with Padding

I have a UICollectionView that scrolls horizontally and I would like to have one cell visible on the screen at a time.
I am programmatically setting the cell width and height in the following method:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath) as! Cell
let width = collectionView.bounds.size.width - 40
let height = collectionView.bounds.size.height - 40
cell.bounds.size = CGSize(width: CGFloat(width), height: CGFloat(height))
return cell
}
And after struggling to figure out how to work with insets, spacing, section insets (both programmatically and in the inspectors in XCode) I can't seem to figure out what I need to set and where I need to set it.
Here is a diagram of my desired spacing:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width - 40, height: yourCollectionViewHeight - 40)
}
left 20 - add Footer Size and Header Size in storyboard with width = 20
spacing between cells - add Minimum Spacing in storyboard = 40
I could have made some mistakes and you will not get exactly your layout, but I hope you would get the idea.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width * 0.7, height: 300)
}
func collectionView(_ collectionView: UICollectionView, layoucollectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let margin = collectionView.frame.width * 0.3
return UIEdgeInsets(top: 10, left: margin / 2, bottom: 10, right: margin / 2)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return collectionView.frame.width * 0.3 / 2
}
If you want single cell visible on the screen at a time then important thing is that you should enable paging for your CollectionView. You can do it in swift as below :
collectionView.isPagingEnabled = true
Now conform your ViewController class to UICollectionViewDelegateFlowLayout and implement it's method sizeForItemAt indexPath which returns CGSize for your collectionview cell. For Example:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 200, height: 200)
}

Resources