Adaptive UICollectionView Cell Width - ios

I am new to collection view and Auto Layout and I have a problem making the cell sizes adapt to the various devices in the simulator. I am using the flow-layout and I set the sizes in the size inspector. The image I've provided shows the way I need the cells to look(canvas on the left iPhone5)on all devices. The iPhone4 display is good but the 6s is incorrect.
Can someone please show me how this is done as I cannot find the precise information I am looking for.
Thanks
Also, I'm not sure why the iPhone5 doesn't display the cells on the preview section like the 4&6 do..? any clues..?
screen shot1

With UICollectionView, you need to calculate the size of your cells, and the space around them (insets and interitem spacing) using delegate methods.
Something like this should work for you:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 8, left: 4, bottom: 0, right: 4)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return CGFloat(8)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let cellWidth = (view.bounds.size.width - 16) / 2
let cellHeight = cellWidth * 0.8 // multiply by some factor to make cell rectangular not square
return CGSize(width: cellWidth, height: cellHeight)
}
Within a UICollectionViewCell is where you would use Auto Layout to position the things in the cell, like labels, images, etc.

Related

UICollectionViewCell width is bigger than what I set in sizeForItemAt

Width/height of my UICollectionView matches all available space.
I want to display two cells in one row (two columns)
So width of one cell should take half width of UICollectionView (collectionView.frame.width / 2)
So I programmatically change width of cell in function of UICollectionViewDelegateFlowLayout:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width / 2.0, height: 150)
}
But visually width of cell is much bigger than collectionView.frame.width / 2.0 (tested on iPhone SE simulator)
So it takes more than half-width space of screen
Here I printed info about sizes:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
print("didSelectItemAt \(cell.frame.width), table width: \(collectionView.frame.width), item calc width: \(collectionView.frame.width / 2)")
}
didSelectItemAt cell width 187.5, table width: 375.0, half-width:
187.5
Why does it happen?
There is also an issue with margins but first I have to solve this one
Update
Though it works fine with iPhone 6s Simulator (I edited image to place two items in the first row to check if they fit):
So what is wrong with iPhone SE?
I don't want to find out that there can be the same issue with other iPhones or iPads too
375.0 is the size of iPhone 6s and X, not iPhone SE which is 320.0
The reason is there is a width constraint of collectionView which was set at iPhone 6s mode but later when switching to the SE simulator, this constraint was not updated.
I think there is problem of cell spacing. it seems there is default cell spacing exists in your implementation. you should try below code to adjust two cell in one raw of collectionview:
extension ViewController : UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let collectionViewWidth = collectionView.bounds.width
return CGSize(width: collectionViewWidth/2, height: 150)
}
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 0
}
}
moreover, you should check constraint of your UICollectionView if it is as per your requirement Or not.
Try to use the wide from the view, not the collectionView.
Try this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (view.frame.width / 2.0) - 5, height: 150) // provide little bit space between cells
}
Hope this will help.
The best solution is, to reload your collection view in main thread with a bit delay.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.10) {
self.myCollectionView.reloadData()
}

How to stretch/fill collection view cell across view?

I'm using this method below in my UICollectionView to stretch/fill the cell, but it seems like it's constraining to the margins. It's stopping like 10 px shy of the edges. I basically want to set the constraints of the cell to "0" and "0" to fill the width completely and NOT constrained to the margins.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let customHeight = CGFloat(116)
return CGSize(width: collectionView.frame.size.width, height: customHeight)

iOS UICollectionView with self sizing items bug?

I'm trying to implement UICollectionView (flowLayout) with self sizing items.
Implementation is very simple - I have just set estimatedItemSize and set UICollectionViewCell constraints to manage it's size.
Everything works fine at first data reload after collectionView was created, but on another or some other reload few items at the top becomes same size as estimatedItemSize is. If scroll down and up - items size looks good again.
I have spend 2 days with this issue experimenting different cell constraints, trying to setNeedsLayout in various places and other stuff around collectionView. Is it bug?
In this post I had an interesting problem that I found my own answer to. There are two important functions to be using properly for the sizing purposes which are:
minimumInteritemSpacingForSectionAtIndex
insetForSectionAtIndex
sizeForItemAtIndexPath
Example of me using them in Swift
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionView, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0, 0, 0, 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
let screenRect: CGRect = collectionView.bounds
let screenWidth: CGFloat = screenRect.size.width
let cellWidth: CGFloat = screenWidth / 24
//Replace the divisor with the column count requirement. Make sure to have it in float.
let size: CGSize = CGSize(width: cellWidth, height: cellWidth)
return size
}
The biggest secret is to be careful about attaching your views to the trailing edge and the bottom layout. If you only attach them to leading and the top, then you can set the width and height programmatically through frame or constraints. I think that doing it through constraints is a little more straight forward, though in my personal project I have chose to do it the other way because it makes the functionality slightly cleaner in my opinion.
Try to accomplish as much of a cells layout as possible inside of cellForItemAt.

Placing the cells in a collection view in the centre rather than the outsides in swift

By default, a collection view will attempt to space out the cells in the collection by spacing them evenly so that there will be one cell touching either side of the collection view. Example:
But, when the cell is too wide to fit multiple cells in one row on the collection view, it will centre the cell in the collection view.
Is there a way to make the behaviour of centring cells, like in the 3rd picture, the default for the collection view so that there is 0 space between the individual cells and space on either side to the edges of the collection view?
Not that I'm aware of. You should implement something like this:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
CGFloat width = UIScreen.mainScreen().bounds.size.width
return CGSize(width, anyHeight)
}
and then implement the delegate method below as well:
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsetsZero
}
Make sure to implement rotation handling too if it's applicable.
My answer would be to use a carefully set up something like
let insets = UIEdgeInsets(top: yourvalues, left: yourvalues, bottom: yourvalues, right: yourvalues),
and feed it to either YourCollectionView.contentInset, or
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return sectionInsets
}
This should work! Lemme know if you have any troubles with it!

Display Instagram photos correctly in a CollectionView on all screen sizes

This one has me perplexed. Ok, here we go.
I'm pulling down IG images, they come in 3 sizes:
150x150, 306x306, 640x640
What I would like to do is display them 3 across, on all iPhone screen sizes (5,5S,6,6+). The 306x306 sizes come down super fast, and look great. So that's my starting point (150x150 are a bit blurry, 640x640 is also an option, I can also use those, just trying to save some bandwidth).
The edges can be flush to the sides, with a 1 px/pt line between them. I have kind of wracked my brain around this. My understanding is that the function (below) should override any other setting in my Storyboard, and where I should be focused (I've printed out the sizes I've captured from println().
My challenge is finding out the final widths and heights needed to work on all screens. I've tried a number of permutations, but have not nailed it yet. I'm close, but no cigar. This is all using a CollectionView in storyboards. Advice appreciated. It's a heck of a problem, spent all day on this one.
I don't think autolayout can help at all, it all has to be done in code, but who knows?
Thanks [lots!] :-)
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var width = CGRectGetWidth(collectionView.bounds)
println("width: \(width)")
// iphone5 320
// iphone5S 320
// iphone6 375
// iphone 6+ 414
return CGSize(width: NeedCorrectWidthHere, height: NeedCorrectHeightHere)
}
This does partially work, with a gap between images:
var screenWidth = CGRectGetWidth(collectionView.bounds)
var cellWidth = screenWidth/3
return CGSize(width: cellWidth-10, height: cellWidth-10)
This seemed to work. A pretty simple solution in the end.
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var screenWidth = CGRectGetWidth(collectionView.bounds)
var cellWidth = screenWidth/3
return CGSize(width: cellWidth-2, height: cellWidth-2)
}
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 3
}
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 3
}

Resources