UICollectionViewFlowLayout minimumLineSpacing not applied to whole collection view - Swift - ios

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;

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)

Equal horizontal spacing UICollectionView, including insets - UICollectionViewFlowLayout?

I'm trying to create equal horizontal spacing between two UICollectionViewCell and between a UICollectionViewCell an the screen border. It should look like this.
I think it is possible to use UICollectionViewFlowLayout to adjust the width of every UICollectionViewCell, so they have equal spacings regardless of the phone's size. There should always only be 3 cells on each row.
First of all you should add constants for all parameters of UICollectionView to the controller:
let itemsPerRow = 3
let spaceBetweenItems: CGFloat = 20.0
let spaceBetweenLines: CGFloat = 40.0
let itemHeight: CGFloat = 50.0
You mentioned that item's width should be adjustable. You can calculate it with this function:
var itemWidth: CGFloat {
return (self.collectionView.frame.width - CGFloat(self.itemsPerRow + 1) * self.spaceBetweenItems) / CGFloat(self.itemsPerRow)
}
Of course you need to customise layout of your UICollectionView instance:
var collectionViewLayout: UICollectionViewFlowLayout {
let result = UICollectionViewFlowLayout()
result.itemSize = CGSize(width: self.itemWidth, height: self.itemHeight)
result.minimumInteritemSpacing = self.spaceBetweenItems
result.minimumLineSpacing = self.spaceBetweenLines
result.sectionInset = UIEdgeInsets(top: 0.0, left: self.spaceBetweenItems, bottom: 0.0, right: self.spaceBetweenItems)
result.scrollDirection = .vertical
return result
}
And in the function viewDidLoad or your controller you should call setupCollectionView(). Here is a code of this function:
func setupCollectionView() {
self.collectionView.frame = self.view.frame
self.collectionView.collectionViewLayout = self.collectionViewLayout
}
You will see something like this:

UICollectionView edge to edge layout

Goal: edge-to-edge UICollectionView with 2 cells on all size iPhones.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let screenSize = collectionView.bounds
let screenWidth = screenSize.width
print("Zhenya: \(screenWidth)") // prints correct 375, which is half of iPhone 6
let cellEdgeLength: CGFloat = screenWidth / 2.0
return CGSize(width: cellEdgeLength, height: cellEdgeLength)
}
Also
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
let flow = UICollectionViewFlowLayout()
flow.scrollDirection = UICollectionViewScrollDirection.vertical
flow.minimumInteritemSpacing = 0
flow.minimumLineSpacing = 0
collectionView.collectionViewLayout = flow
}
However on iPhone 6:
Collection Cell attributes:
Collection View attributes:
Update:
func for gradient, that actually gets the right width:
(located at custom UICollectionViewCell class)
func addGradient () {
let gradient = CAGradientLayer()
gradient.frame = gradientView.bounds
let topColor = UIColor(red:0.07, green:0.07, blue:0.07, alpha:1)
let botomColor = UIColor.clear
gradient.colors = [topColor.cgColor, botomColor.cgColor]
if gradientWasRemoved == false {
gradientView.layer.insertSublayer(gradient, at: 0)
} else if gradientWasRemoved == true {
self.addSubview(gradientView)
}
Update 2:
Note: Testing on iPhone 7 Plus.
I found that UICollectionViewFlowLayout overrides cell size crated in sizeForItemAtIndexPath: (seems like it)
With this code:
let flow = UICollectionViewFlowLayout()
let screenSize = collectionView.bounds
let screenWidth = screenSize.width
let cellEdgeLength: CGFloat = screenWidth / 2.0
flow.itemSize = CGSize(width: cellEdgeLength, height: cellEdgeLength)
flow.scrollDirection = UICollectionViewScrollDirection.vertical
flow.minimumInteritemSpacing = 0
flow.minimumLineSpacing = 0
collectionView.collectionViewLayout = flow
I have this:
Then I decided manual specify cell edge length (half of iPhone7Plust width = 207):
let cellSide: CGFloat = 207
flow.itemSize = CGSize(width: cellSide, height: cellSide)
flow.scrollDirection = UICollectionViewScrollDirection.vertical
flow.minimumInteritemSpacing = 0
flow.minimumLineSpacing = 0
collectionView.collectionViewLayout = flow
I get this:
Found the solution.
Problem was in the inner imageView autolayout settings. I didn't specify them.
Why that little piece of BS was getting on the way of my flow.itemSize, I don't know.
What is more weird to me, is why when manually specified cellEdgeLength as 207 in the second update, all of the sudden, it did override the lack of imageView constraints.

UICollectionView cells display weirdly - Swift 3

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
}

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