UICollectionView: remove space between cells (with 7 items per row) - ios

I have a UICollectionView with 7 items per row (or actually, the width of each item is the collectionView.bounds.width divided by 7).
Here is a code example:
final class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let randomColors: [UIColor] = [.red, .blue, .green, .yellow, .orange, .purple, .cyan, .gray, .darkGray, .lightGray, .magenta]
var colors: [UIColor] {
var result: [UIColor] = []
for _ in 0..<200 {
result.append(randomColors[Int(arc4random_uniform(UInt32(randomColors.count - 1)))])
}
return result
}
#IBOutlet weak var collectionView: UICollectionView!
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.size.width / 7, height: 45)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return .leastNormalMagnitude
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return .leastNormalMagnitude
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: .leastNormalMagnitude, left: .leastNormalMagnitude, bottom: .leastNormalMagnitude, right: .leastNormalMagnitude)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return colors.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ColorCollectionViewCell", for: indexPath) as! ColorCollectionViewCell
cell.color = colors[indexPath.row]
return cell
}
}
final class ColorCollectionViewCell: UICollectionViewCell {
var color: UIColor? {
didSet {
self.backgroundColor = color
}
}
}
And the storyboard:
Since dividing the collectionView's width by 7 often gives a floating result, this results in spaces appearing between some cells:
What can I do here?
Thanks for your help.

You have to somehow distribute the additional pixels. Don't worry that all cells won't have exactly the same width, 1px difference won't be visible:
let numColumns = 7
let availableWidth = collectionView.bounds.size.width
let minWidth = floor(availableWidth / CGFloat(numColumns))
let remainder = availableWidth - minWidth * CGFloat(numColumns)
Now what to do with the remainder? Let's just add pixels to cell from the left:
let columnIndex = indexPath.row % numColumns
let cellWidth = CGFloat(columnIndex) < remainder ? minWidth + 1 : minWidth
return CGSize(width: cellWidth, height: 45)
This is just the basic idea.

Related

How to show three rounded images in Collectionview cell in all iPhone screen sizes in swift

I have taken collectionview in storyboard and in its cell i have taken ImageView and its below one label
i need to show only three rounded images horzontally and number of rows from JSON vertically in all screen sizes
like below image i need:
in cell for imageview costraints like below
leading = trailing = top = bottom = 5
and in coding:
class FriendsViewController: UIViewController {
// MARK: - IBOutlets
#IBOutlet weak var galleryCollectionView: UICollectionView!
// MARK: - LifeCycleMethods
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
layout.scrollDirection = .vertical
let width = UIScreen.main.bounds.width/4
let height = width*1.1
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 5
layout.itemSize = CGSize(width: width, height: height)
self.galleryCollectionView.collectionViewLayout = layout
}
}
class FriendsCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imgView: UIImageViewX!
#IBOutlet weak var lblTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
imgView.layer.cornerRadius = imgView.frame.height/2.0 }
}
so i am getting weird o/p in different screen sizes why?
o/p of ipodtouch7
In your collectionview cell Set constraints as shown in attached image, i.e. ur image view width should depend on imageview height, you can do it using ratio.
Then in willDisplay method calculate cornerRadius
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
{
guard let cell = cell as? TestCollectionViewCell else
{
return
}
let cellHeight : CGFloat = cell.frame.size.height
let labelHeight : CGFloat = cell.label?.frame.size.height ?? 0.0
cell.imageView?.layer.cornerRadius = (cellHeight - labelHeight) / 2.0
}
Below is the entire code
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
#IBOutlet weak var collectionView : UICollectionView? = nil
let horizontalSpaceBetweenCell : CGFloat = 16
let verticalSpaceBetweenCell : CGFloat = 16
let edgeInsets = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
override func viewDidLoad()
{
super.viewDidLoad()
setup()
}
func setup()
{
let nib = UINib(nibName: "TestCollectionViewCell", bundle: nil)
collectionView?.register(nib, forCellWithReuseIdentifier: "TestCollectionViewCell")
}
func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 40
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TestCollectionViewCell", for: indexPath) as? TestCollectionViewCell
{
return cell
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
{
guard let cell = cell as? TestCollectionViewCell else
{
return
}
let cellHeight : CGFloat = cell.frame.size.height
let labelHeight : CGFloat = cell.label?.frame.size.height ?? 0.0
cell.imageView?.layer.cornerRadius = (cellHeight - labelHeight) / 2.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let numberOfCellsPerRow = 3
let otherSpace = (edgeInsets.left + edgeInsets.right)
let width = (collectionView.frame.size.width - otherSpace) / CGFloat(numberOfCellsPerRow)
return CGSize(width: width, height: 80) // Height can be anything 80, 90 ,100
}
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 16
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return edgeInsets
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
}
}
Please change Widhth to height :
imgView.layer.cornerRadius = imgView.frame.size.height/2.0

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:).

Collection View cell in Swift is not formatting correctly

I am new to Swift and I am trying to create a UICollectionViewCell. It builds successfully but the cells are not the size that I thought I was creating. There are supposed to be two columns and and it is supposed to contain an image, description and price. I know it is something simple but I cannot figure out what I am doing wrong. See the image below. I assume I need to fix something in the size inspector but I cannot figure out what.
import UIKit
class ProductsVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
#IBOutlet weak var productsCollection: UICollectionView!
private(set) public var products = [Product]()
override func viewDidLoad() {
super.viewDidLoad()
productsCollection.dataSource = self
productsCollection.delegate = self
}
func initProducts(category: Category){
products = DataService.instance.getProducts(forCategoryTitle: category.title)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return products.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProductCell", for: indexPath) as? ProductCell {
let product = products[indexPath.row]
cell.updateViews(product: product)
return cell
}
return ProductCell()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height: CGFloat = 280
return CGSize(width: (collectionView.frame.size.width - 10) / 2, height: height)
//10 is minimum line spacing between two columns
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
}
FYI
Try this:
// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// You can change collection view cell height here, It's up to your design
let height: CGFloat = 150
let width: CGFloat = (collectionView.frame.size.width - 10 - a) / 2 // a is extra spacing of collection view (like leading or trailing constraints)
// Converting CGFloat to Int make sure the width of total cells are less than or equal the screen width
return CGSize(width: Int(width), height: Int(height)) // 10 is minimum line spacing between two columns
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
}
Please check extra spacings example with the below photo:
a = 20 with my example design (leading = 10, trailing = 10)
just provide the itemSize when you declare the collectionView:
let layout = UICollectionViewFlowLayout()
layout.itemSize = sizeForEachItem
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)

iOS Swift CollectionView Cell are Cutting Down

we are using UICollectionView for displaying some data's. My problem is when scrolling cell horizontal cell's are cutting down . How to fix it?
Here is my tried code:
func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 1
}
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
{
return 1.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,minimumLineSpacingForSectionAt section: Int) -> CGFloat
{
return 1.0
}
func collectionView(_ collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAt indexPath:IndexPath) -> CGSize
{
let width = UIScreen.main.bounds.width
return CGSize(width: width,height: collectionView.frame.size.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
{
return UIEdgeInsetsMake(0, 0, 0, 0)
// top, left, bottom, right
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let ViewSize = UIScreen.main.bounds.width
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath as IndexPath) as! CSDashboardCustomCell
cell.MainView.frame = CGRect(x: 10, y: 10, width: ViewSize - 40, height: 142)
cell.MainView.backgroundColor = UIColor.white
cell.MainView.layer.shadowColor = UIColor.lightGray.cgColor
cell.MainView.layer.shadowOpacity = 1
cell.MainView.layer.shadowOffset = CGSize.zero
cell.MainView.layer.shadowRadius = 2
cell.MainView.layer.cornerRadius = 6
return cell
}

UICollectionView not obeying UICollectionViewDelegateFlowLayout and insets

So I have a CollectionView that has the following code:
class MyViewController: UIViewController {
...
#IBOutlet weak var imageCollectionView: UICollectionView!
fileprivate let sectionInsets = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
fileprivate let itemsPerRow: CGFloat = 3
...
override func viewDidLoad() {
super.viewDidLoad()
imageCollectionView.delegate = self
imageCollectionView.dataSource = self
}
}
...
extension MyViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
let availableWidth = collectionView.bounds.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 I'm going for is 3 cells per row with very little space between but whenever I run the app I get something like this:
Where each black block is a cell.
For similar questions I've found, the answer is usually to implement the UICollectionViewDelegateFlowLayout functions which I've done and my functions are being called but just not working as expected! Even if I manually change the widthPerItem value to be slightly smaller, it still doesn't work!
Can anyone see if I'm missing something obvious here?
Thank you!
Did you make sure the minimum inter-item spacing is set to only 1 point?
You can set this up in Interface Builder or by overriding the following delegate method:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1.0;
}
Another possible reason I see is that the result of dividing availableWidth by itemsPerRow is a floating number. In the end the sum of the cell widths and the inset and might actually exceed the collection view width by 0.x pixels. To make sure this doesn't happen, you may want to use floor:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
let availableWidth = collectionView.bounds.width - paddingSpace
let widthPerItem = floor(availableWidth / itemsPerRow)
return CGSize(width: widthPerItem, height: widthPerItem)
}

Resources