I've been trying to figure out how to accomplish this kind of gridView display for my app,
Image example of what i need to accomplish
i have this block of codes, which came from another question relating to gridView,
but the texts inside each views are not displaying properly
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let flowayout = collectionViewLayout as? UICollectionViewFlowLayout
let space: CGFloat = (flowayout?.minimumInteritemSpacing ?? 0.0) + (flowayout?.sectionInset.left ?? 0.0) + (flowayout?.sectionInset.right ?? 0.0)
let width = self.collectionView.frame.size.width
let size:CGFloat = (width - space) / 2.0
if indexPath.row == 0 {
return CGSize(width: width, height: width / 2.0)
}
return CGSize(width: size, height: size)
}
Well , This is something you have just give layout to collection view but you have to use CustomCell to proper design inside items of collectionview, also for above layout guide i would suggest to use Extension like this
Extension Yourviewcontroller: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = self.collectionView.frame.size.width
let size:CGFloat = (width - space) / 2.0
if indexPath.row == 0 {
return CGSize(width: width, height: width / 2.0)
}
return CGSize(width: size, height: size)
}
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 10
}
}
Regarding custom cell creation use this link https://www.ioscreator.com/tutorials/custom-collection-view-cell-ios-tutorial ,
In your case just use one cell with one image and 2 lables below and make them centerAlign(Using constraint) And use that cell on your viewcontroller as below
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
cell.imageView.image = Yourimage
cell.lableName.text = yourlable
cell.lableDetail.text = yourdetailtext
return cell
}
let me know if you need any other help on this
Related
I have a vertical UICollectionView which contains 5 different types of cells. The 4 of them take the whole width of the UICollectionView, so the func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize gets the work done.
The other 2 are of the same size but their default position needs to be changed; I want them to be aligned from left to right, instead of the centered default.
Here's how I set up my UICollectionView:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch typesOfCellsArray[indexPath.row] {
case .createVoiceRoom:
return CGSize(width: collectionView.frame.width, height: 70)
case .voiceRooms:
let height: CGFloat = content.voiceRooms.count > 3 ? 256 : 128
return CGSize(width: collectionView.frame.width, height: height)
case .seeMoreVoiceRooms:
return CGSize(width: collectionView.frame.width, height: 50)
case .newStatus:
let width = collectionView.frame.width / 4
return CGSize(width: width, height: width + 40)
case .status:
let width = collectionView.frame.width / 4
return CGSize(width: width, height: width + 40)
}
}
Here's how they look:
And here's what I want:
I think I need to subclass UICollectionViewLayout to achieve that, but I've failed... Here's what I did:
class HomeCollectionViewLayout: UICollectionViewLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let collectionView = collectionView else { return nil }
let layoutAttributes = super.layoutAttributesForElements(in: rect)
layoutAttributes?.forEach({ attributes in
if collectionView.cellForItem(at: attributes.indexPath) is NewHomeStatusCollectionViewCell {
let column = attributes.indexPath.row % 3
attributes.frame.origin.x = CGFloat(column-1) * attributes.frame.width
}
})
return layoutAttributes
}
}
Of course, I have set this class as the UICollectionView layout. Any ideas?
You can insert each of you cell into separate section and then control following:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { }
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { }
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { }
I am using UICollectionViewDelegateFlowLayout delegate to make it square but strange spacing is showing at the end.
My Code
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView == self.bannerClcView {
let cellSize = CGSize.init(width: view.frame.size.width, height: view.frame.size.height)
return cellSize
}else if collectionView == self.category {
let cellSize = CGSize.init(width: (self.category.frame.size.width/3), height: category.frame.size.width/3)
return cellSize
}else {
let cellSize = CGSize.init(width: (recomended_Ads_Clc.frame.size.width/2), height: 220)
return cellSize
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
How can I remove the spacing? Please help.
If you open your Storyboard where you've laid out your UICollectionView and select its trailing constraint, I image you'll see something like this in the Attributes inspector:
You'll need to uncheck "Relative to margin" to remove the extra space you're seeing.
Your mistake is with the source view that you compute the width property for the cell inside the function
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
instead of
let cellSize = CGSize.init(width: (recomended_Ads_Clc.frame.size.width/2), height: 220)
return cellSize
write return CGSize(width: (collectionView.frame.width / 2) - 10, height: 220)
And compute the height as you want. 10 will be the space between the cells
I'm using a
CollectionView
as a menu bar at the bottom of my
ViewController
I've created a custom class and have made 5 evenly spaced cells which fills the entire CollectionView on an iPhone 8 with the following, which is the desired result -
layout.itemSize = CGSize(width: self.frame.width / 5, height: self.frame.height)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
self.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.collectionViewLayout = layout;
However when I run the project on any iPhone with a larger screen I get the following result:
My question is, how could I scale the cells up to fit without any margins, even if it requires the CollectionView to increase it's height, I would like the images (which will be placed in the cells) to maintain their aspect ratio from the iPhone 8 screen.
Could I just increase the size of each CollectionViewCell with code?
If so is there a way to do so without working out the width/height of each iPhone released to date?
Or can someone please recommend a different way to accomplish this? if I'm approaching this the wrong way.
Thanks in advance!
It seems that the view's width isn't correct in the place you put the line
layout.itemSize = CGSize(width: self.frame.width / 5, height: self.frame.height)
so replace it with
layout.itemSize = CGSize(width: UIScreen.main.bounds.width / 5, height: self.frame.height)
you can also implement
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize
with conform to UICollectionViewDelegateFlowLayout
Here is my code for CollectionView. You can modify as per your requirement.
class HomeViewController: UIViewController,UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var homeCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
homeCollectionView.delegate = self;
homeCollectionView.dataSource = self;
homeCollectionView.reloadData()
}
//MARK:- COLLECTION VIEW
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
if let count = myArray.count{
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let headerView = homeCollectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HomeHeaderCell", for: indexPath) as! HomeHeaderCell
headerView.UpdateHeaderCell(indexPath: indexPath)
headerView.btn_SeeAll.addTarget(self, action: #selector(SeeAllDestinations), for: .touchUpInside)
return headerView
case UICollectionElementKindSectionFooter:
let footerView = homeCollectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HomeFooterCell", for: indexPath) as! HomeFooterCell
return footerView
default:
return UICollectionReusableView()
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 80)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 10)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
let myCell:HomeTopDestinationsCell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeTopDestinationsCell", for: indexPath) as! HomeTopDestinationsCell
myCell.backgroundColor = UIColor.colorFromCode(0xf1f1f1)
myCell.UpdateCellValue(obj:(myArray[indexPath.row])!)
return myCell as HomeTopDestinationsCell;
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
//Use for size
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
if DeviceType.IS_IPAD{
return CGSize(width: (self.view.frame.width-60)/3 , height: (self.view.frame.width-60)/3);
}
return CGSize(width: (self.view.frame.width-60)/2 , height: (self.view.frame.width-60)/2);
}
//Use for interspacing
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10.0
}
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10.0
}
}
I am trying to do a layout as Pinterest uses for their image gallery. But the issue is that I'm returning the image height to the size of the cells and when the images have different heights, it leaves spaces in the cells.
I have walked through lots of articles, but I have not found a simple solution. Is there any simple way we can do this?
Please see this image
class ViewController: UIViewController {
#IBOutlet var collectionVIew: UICollectionView!
var imgData = [#imageLiteral(resourceName: "p1"),#imageLiteral(resourceName: "p2"),#imageLiteral(resourceName: "p3"),#imageLiteral(resourceName: "p4"),#imageLiteral(resourceName: "p5"),#imageLiteral(resourceName: "p6"),#imageLiteral(resourceName: "p7"),#imageLiteral(resourceName: "p8"),#imageLiteral(resourceName: "p1"),#imageLiteral(resourceName: "p2"),#imageLiteral(resourceName: "p3"),#imageLiteral(resourceName: "p4"),#imageLiteral(resourceName: "p5"),#imageLiteral(resourceName: "p6"),#imageLiteral(resourceName: "p7"),#imageLiteral(resourceName: "p8"),#imageLiteral(resourceName: "p1"),#imageLiteral(resourceName: "p2"),#imageLiteral(resourceName: "p3"),#imageLiteral(resourceName: "p4"),#imageLiteral(resourceName: "p5"),#imageLiteral(resourceName: "p6"),#imageLiteral(resourceName: "p7"),#imageLiteral(resourceName: "p8"),#imageLiteral(resourceName: "p1"),#imageLiteral(resourceName: "p2"),#imageLiteral(resourceName: "p3"),#imageLiteral(resourceName: "p4"),#imageLiteral(resourceName: "p5"),#imageLiteral(resourceName: "p6"),#imageLiteral(resourceName: "p7"),#imageLiteral(resourceName: "p8")]
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width
let img = imgData[indexPath.item]
// 335 is the width of my cell image
return CGSize(width: width/2-15, height: (img.size.height / 335) * width)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imgData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imgView.image = imgData[indexPath.row];
cell.imgView.contentMode = .scaleAspectFit
return cell
}
}
What you have mentioned can be achieved using flow layout — a layout class provided by UIKit.
This is what you are looking for:
https://www.raywenderlich.com/392-uicollectionview-custom-layout-tutorial-pinterest
I hope you can put some effort into reading this very simple post and follow each step carefully.
You are trying to fit non-square images into a square hole. When we do this something has to be given. In this case, I would recommend you make each of your cells a fixed height and set an aspect fill content mode on them so they fill the cell's frame completely. The downside is that some of the larger images will have part of them cut off. The other option is to use the aspect fit content mode which will scale the image to fit without losing a part of the image. This would leave you with the cell spacing again.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width
return CGSize(width: width/2-15, height: 100) // fixed height
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imgView.image = imgData[indexPath.row];
cell.imgView.contentMode = .scaleAspectFill // Set the scale mode to aspect fill
return cell
}
Alternatively, you can check out some open source libraries that might implement this for you. This one looks promising https://github.com/abdullahselek/ASCollectionView
You can check out a larger list of collection view open-source libraries here https://github.com/vsouza/awesome-ios#collection-view
If you want the cells to have different heights you need to calculate the correct height based on the aspect ratio of the image and the width that is fixed. You calculate the aspect ratio in this case by the height/width when our width is fixed and our height is dynamic. Then multiply that by the width of the cell of the image to get our dynamic cell height.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width / 2 - 15;
let img = imgData[indexPath.item]
return CGSize(width: width, height: (img.size.height / img.size.width) * width)
}
You should set your content mode to aspect fit in this case.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imgView.image = imgData[indexPath.row];
cell.imgView.contentMode = .scaleAspectFit // Set the scale mode to aspect fit
return cell
}
I'm trying to auto-resize the cell of a collection view. In the iPhone 8 the design is okay, but when I try it on other devices, especially with the iPhone SE, the cell is not autoresizing and often the cell is too big or too small. I'm working with:
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate {
If what you are trying to do is this
No matter the screen size, I always want 2 (or say n) items a row !
Then you need UICollectionViewFlowLayout
You can try the following piece of code
let itemSize = UIScreen.main.bounds.width/2 - 3
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0)
layout.itemSize = CGSize(width: itemSize, height: itemSize)
layout.minimumInteritemSpacing = 3
layout.minimumLineSpacing = 3
collectionView.collectionViewLayout = layout
In the above code , replace 2 on line:
let itemSize = UIScreen.main.bounds.width/2 - 3
with the number of items you want in a single row
You can place this code in viewDidLoad() or viewDidAppear()
This is happening due to the mismatch between your
EdgeInset
Cell Spacing
Width of your Cell
Solution:-
Add the UICollectionViewDelegateFlowLayout delegate and add these methods and update your values according to your requirement like :-
let edge : CGFloat = 10.0
let spacing : CGFloat = 10.0
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: edge, bottom: 0, right: edge)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return spacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let noOfColumn = 2
let collectionviewWidth = collectionView.frame.width
let bothEdge = CGFloat(edge + edge) // left + right
let excludingEdge = collectionviewWidth - bothEdge
let cellWidthExcludingSpaces = excludingEdge - ((noOfColumn-1) * spacing)
let finalCellWidth = cellWidthExcludingSpaces / noOfColumn
let height = finalCellWidth
return CGSize(width: finalCellWidth, height: height)
}
Add UICollectionViewDelegateFlowLayout method
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSize(width: value, height: value)
}
Try this it will work:
First you have to Add the UICollectionViewDelegateFlowLayout delegate and then use following delegate method it will work fine for me.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let padding: CGFloat = 10
let collectionViewSize = collectionView.frame.size.width - padding
return CGSize(width: collectionViewSize/2, height: collectionViewSize/2)
}
Xcode 10 / Swift 4
Implement the below method,, it is autoresized base on the screen you have.
// Collection View Layout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let screenWidth = UIScreen.main.bounds.width
return CGSize.init(width: (screenWidth-55) / 2, height: (screenWidth-55) / 2 * 1.481)
}