setting constant number of columns & weird collectionView width - ios

I'm trying to set a number of columns in collection view to always be 3, regardless of device.
I'm using this code in viewDidLoad method:
let width = CGRectGetWidth(self.collectionView.frame)/3.0
let layout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSizeMake(width, width)
layout.minimumInteritemSpacing = 0
layout.sectionInset = UIEdgeInsetsZero
layout.scrollDirection = UICollectionViewScrollDirection.Vertical
print("width:\(width), collectionViewWidth:\(CGRectGetWidth(self.collectionView!.frame)), mainViewWidth:\(CGRectGetWidth(self.view.frame))")
I always get only 1 column. The print method return some useful debugging information. They state the following:
width:186.666666666667,
collectionViewWidth:560.0,
mainViewWidth:320.0
What is going on? How is the collectionViewWidth bigger than main's? What is the correct way of setting a constant number of columns? I specified the size of collectionView using constraints in Interface Builder and it should be less than mainView's.

Related

Collection View Scale Flow Layout

I want to create layout for following layout inside collection view.
Align one cell in centre and two cell outside with scale down using transform.
I have used third party library DXScaleFlowLayout and implemented following code to make like same.
let scaleLayout = DXScaleFlowLayout()
scaleLayout.transformScale = 0
scaleLayout.isPagingEnabled = true
scaleLayout.minimumAlpha = 0.8
scaleLayout.minimumLineSpacing = 0
scaleLayout.minimumInteritemSpacing = 0
let width = self.cvBusinessPlans.bounds.width
let height = self.cvBusinessPlans.bounds.height
scaleLayout.itemSize = CGSize(width: width, height: height)
scaleLayout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.cvBusinessPlans.collectionViewLayout = scaleLayout
But it didn't work for me. If anyone know how to make this then please help. I have search of SOF, but I can't find exact or similar solution.

CollectionView won't stop complaining about undefined behaviour

I have a set of 4 different elements that can be shown in a small horizontal collectionView. There will only be one row, and the collectionView and the elements will always be the same height as each other - however, it needs to be dynamic, in case people increase their text size (accessibility larger text).
To make this work, I have a "spacer" label next to the CollectionView, and tell the collectionView to be the same height as the label, and I do the same in all the cells. They all have the same font.
When I try to run it, the CollectionView prints out this:
The behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less than the height of the UICollectionView
minus the section insets top and bottom values,
minus the content insets top and bottom values.
Please check the values returned by the delegate.
The relevant UICollectionViewFlowLayout instance is <...>,
and it is attached to <..; frame = (0 438.5; 414 29.5);
clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <..>;
layer = <CALayer:..>; contentOffset: {0, 0}; contentSize: {32, 29.5};
adjustedContentInset: {0, 0, 0, 0}> collection view layout: <...>.
Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes
to catch this in the debugger.
But everything I can find returns 29.5 or less.
collectionView.contentInsetAdjustmentBehavior = .never
flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
ContentInset is .zero, sectionInset is 0 for top and bottom, lineSpacing and interItem-spacing is 0, scrollDirection = .horizontal..
I have not overridden any sizeForItem or anything like that, because it should happen automatically, and I shouldn't have to create custom height logic.
I have tried this in each cell:
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
setNeedsLayout()
layoutIfNeeded()
layoutAttributes.frame.size = contentView.systemLayoutSizeFitting(UICollectionViewCell.layoutFittingCompressedSize)
print("Height: ", layoutAttributes.size)
return layoutAttributes
}
But it just prints out 29.5, which should be correct.
It does print it out after the warnings though, so something happens before this.
When I print out the height of the cell directly after its initialization like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let item = items[indexPath.row]
if let someItem = item as? SomeCellViewModel, let cell = self.dequeueReusableCell(withReuseIdentifier: "SomeIdentifier", for: indexPath) as? SomeCell{
cell.bind(with: someItem)
print("height: ", cell.bounds.size.height)
return cell
}else{ ...
it prints out "50.0". And as I understand, that's the default "Item Size", but I've changed all that! In Storyboard/IB/Xib, the "Collection View Flow Layout" was 50x50, but I have changed it to different things (0x0, 1x1, 20x20, tested a lot). Still it prints out 50.0. The xibs for the cells aren't even 50 in their file. If I try to use the systemLayoutSize-thing directly here to set its frame.size, it warns me about an autoresizing constraint that wants to be 0. I assume I shouldn't be doing such logic here anyway, so I nevermind this.
Since the warning says items "must be less than the height minus X minus Y" (not less than or equal to), I tried making the CollectionView a point taller than the spacer-logic, making the CollectionView 30.5 and the cells 29.5, but I still get the warning.
I have tried setting the symbolic breakpoint, but there is nothing of value there..
The thing is - it looks and behaves correct - on iOS 12. It does not work on iOS 11. There might be a separat constraint-issue within the elements that makes it wrong on iOS 11, I'm looking into it. Either way;
Why am I receiving this warning?
Edit: if I implement sizeForItemAt:IndexPath and statically tell it to be
width:100, height: 29.5, there is no warning at all. Everything is good. But I don't want to do that. I shouldn't need to calculate the individual height like this. And I don't really want to deal with prototype-cells either.
This should work.

Auto layout calculating same height for each collectionViewCell

So I have a collection view that uses a custom layout that I found on Github called CHTCollectionViewWaterfallLayout I like it so far, however I am looking to make the custom collection view cell I made dynamic in height, and I am having trouble connecting Auto Layout to calculate this. I shared a simple sample project on Github that generates random string sizes and displays them, only problem is that Auto Layout generates the same cell height for each Collection View Cell. The project can be found here.
To give you a run down of my thought process, I calculate the cell height by using the method CHTCollectionViewDelegateWaterfallLayout sizeForItemAtIndexPath. Since I also use the delegates method columnCountforSection my thought is since I provide a finite number of columns based on the orientation, I take the collectionView frame width and I divide by the number of columns to get me my width for the cell.
func collectionView (collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
var cell = dict["heightCell"] as? CollectionViewCell
let randomString = RadomStrings[indexPath.item]
let float = CGFloat(columnCount)
let width = collectionView.bounds.size.width / float
if cell == nil {
cell = NSBundle.mainBundle().loadNibNamed("CollectionViewCell", owner: self, options: nil)[0] as? CollectionViewCell
dict["heightCell"] = cell
}
cell?.RandomStringLabel.text = randomString
cell!.frame = CGRectMake(0, 0, CGRectGetWidth(collectionView.bounds), CGRectGetHeight(cell!.frame))
cell!.setNeedsLayout()
cell!.layoutIfNeeded()
var size = cell?.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
size?.width = width
return CGSize(width: width, height: size!.height)
}
My constraints are pretty basic. I have a constraint on each axis. One on the top of the content view, leading, trailing and bottom. I also have a height on the label that is greater than or equal to 45.
Using Auto Layout to calculate TableViewCell heights is easy to me and I like it because I like the DRY principle behind this height calculation approach. So I would like to keep this height calculation process the same throughout my app. CollectionViews are a relatively new layout process for me, so I would love to learn what I am doing wrong here. Hopefully I am clear for everyone, thanks!
I figured it out! What I didn't do is put a width constraint on the custom cell I created! Duh!

UICollectionView spacing is incorrect every other row

I am creating a custom flow layout for a UICollectionView, and I am trying to get the spacing right. Despite setting minimumLineSpacing = 2 every other line, there is only 1px between each row of images, as can be seen here. I believe this is because Apple now uses points instead of pixels, but how do you fix this? Here is my code in my CollectionViewController that sets the flow layout:
let screenWidth = self.collectionView?.bounds.size.width
let flowLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
flowLayout.minimumInteritemSpacing = 2
flowLayout.minimumLineSpacing = 2
let totalSpacing = flowLayout.minimumInteritemSpacing * 4.0
let imageSize = (screenWidth!-totalSpacing)/4.0
flowLayout.itemSize = CGSize(width: imageSize, height: imageSize)
I figured it out. If the height of the cell is a repeating float, the spacing between each row will alternate, every other row. For instance, if I want to fit three cells with 2 points spacing between each one, each cell will be 123.6666666667 points wide on an iPhone 6. (An iPhone 6 is 375 points across, leading to the calculation (375-4)/3 = 123.6666666667). If I proceed to use the same value for height as I do width, the last 2/3 of a point will cause the rows' spacing to alternate, as seen here.
What I've found is that the cell height does not necessarily have to be an int to fix this; setting it to say 123.5 will solve this problem, but ints of course will also work.

Set/Override Padding In UICollectionView Between Cells

I have a UICollectionView and I'm running into an issue with getting the padding between cells. In theory, I should be able to divide the screen by 4, and I can get a cellsize that has 4 images which perfectly take up the screen width. But, it chooses not to do that. Instead it creates 3 images with HUGE padding as shown below:
_cellSize = CGSizeMake(UIScreen.mainScreen().bounds.width/4,UIScreen.mainScreen().bounds.width/4)
So I decreased the cell size a bit to create some padding, and this was closer but I need about half this padding:
_cellSize = CGSizeMake(UIScreen.mainScreen().bounds.width/4.4,UIScreen.mainScreen().bounds.width/4.4)
So I tried making the cells a bit bigger and it rolls over to the next row and puts 3 items with HUGE padding again.
_cellSize = CGSizeMake(UIScreen.mainScreen().bounds.width/4.3,UIScreen.mainScreen().bounds.width/4.3)
Can I specify the padding between cells without it deciding that if the padding is below "x" amount that it creates a new row instead? Like can I set the threshold padding before a new row to say, 0?
I want to make it this thin:
You only need to do the correct calculation that includes the spaces you want to the edges as well as the space between the cells. For instance, if you want 3 cells across with 5 points to the edges and between the cells, you should have something like this,
override func viewDidLoad() {
super.viewDidLoad()
var layout = self.collectionView.collectionViewLayout as UICollectionViewFlowLayout
layout.sectionInset = UIEdgeInsetsMake(0, 5, 0, 5);
layout.minimumInteritemSpacing = 5; // this number could be anything <=5. Need it here because the default is 10.
layout.itemSize = CGSizeMake((self.collectionView.frame.size.width - 20)/3, 100) // 20 is 2*5 for the 2 edges plus 2*5 for the spaces between the cells
}

Resources