UICollectionView cell size - swift 3/4 - swift

I am working on Collection view.I want to display 3 rows in collection view . Each row having two cells.The problems is i can't able to fit the cells in a row.Please kindly refer my code which i tried.
import UIKit
class ProductsTableViewCell: UITableViewCell {
#IBOutlet weak var productsCollections: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
productsCollections.isScrollEnabled = false
productsCollections.dataSource = self as? UICollectionViewDataSource
productsCollections.delegate = self as? UICollectionViewDelegate
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension ProductsTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Product", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 10, left: 10, bottom: 0, right: 10)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return collectionView.frame.width/9
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return collectionView.frame.width
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let collectionViewWidth = collectionView.bounds.width
let collectionViewHeight = collectionView.bounds.height
return CGSize(width: collectionViewWidth/2.0 , height: collectionViewHeight/2.5)
}
[This is the output i got.][1]}
I want like this.totally 6 rows.Per row 2 cells i want to display

First of all, calculate a row height that you need. So it is depends on screen resolution.
for example :
1- Declare a variable
fileprivate var cellHeight: CGFloat
2- calculate it once in viewDidLoad()
self.cellHeight = yourContainerFrameHeight / 3
3- Remove minimumLineSpacingForSectionAt
4- Calculate cell size.
I'm not sure, but you should not use the size of the CollectionView here (in sizeForItemAt). Instead you can use a fixed UIView or screen sizes. Because we already identify it.
This example has self.contentView you must change with yours or screen size
And try this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
flowLayout.invalidateLayout()
let vMiddle = self.contentView.frame.size.width / 2.0
//this calculation for prevent next row shifts and fit rows
let cellWidth =
indexPath.row % 2 == 0 ? floor(vMiddle) : self.contentView.frame.size.width - floor(vMiddle)
//print("---------- cell width : \(cellWidth)")
return CGSize(width: cellWidth, height: self.cellHeight)
}

Related

Change vertical cell size space created with a XIB file

How can I change the space between the cells? It currently looks like this:
Nothing that I've tried works, this is my current setup:
and this is where I'm defining the cell:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// return products.count
return 3
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// This determines the size of the cell on the featured slider
return CGSize(width: UIScreen.main.bounds.width, height: 350)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FeaturedPostsCollectionViewCell", for: indexPath) as! FeaturedPostsCollectionViewCell
let product = products[indexPath.row]
cell.featuredImage.image = UIImage(named: "4")
cell.lbName.text = product.Name!
cell.lbDesc.text = product.desc!
return cell
}
I've looked around but can't find what is it that is giving it so much space...
I just found the solution, it happened that I had to fix the width to my preferred value:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 310, height: 350)
}

How can I programmatically insert UICollectionView into UITableViewCell ? [Swift]

Does anyone know how to integrate a collectionView into the top section of a tableView? I'm trying to get a top section/row that has horizontally scrolling images, and other section/rows below it to just be normal tableview rows. Like what you might see in an app like facebook messenger or tinder/bumble's matches/conversations page.
I understand how to produce UICollectionView and UITableView separately, but between the multiple functions/delegates/datasources, I can't figure out how to properly layer the code.
I am working programmatically rather than on storyboard. I know there are other posts on inserting UICollectionView in UITableViewCell, but they're either storyboard implementations, or on outdated versions of Swift - would be awesome if someone could do a run-through so that anyone looking in the future could also understand the logic even if some of the code becomes outdated.
declare collectionView in the TableViewCell swift file.
var viewPhotos: UICollectionView!
func reset(with cellData: CellData) {
self.data = cellData
viewPhotos.dataSource = self
viewPhotos.delegate = self
viewPhotos.reloadData()
}
in the init function or awakeFromNib check if photoCollectionView is initialized and add it to the cell.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
viewPhotos = UICollectionView(frame: self.bounds, collectionViewLayout: layout)
viewPhotos.collectionViewLayout = layout
viewPhotos.showsHorizontalScrollIndicator = false
viewPhotos.isPagingEnabled = true
viewPhotos.register(PhotoCollectionViewCell.self, forCellWithReuseIdentifier: PhotoCollectionViewCell.cellIdentifier)
Add UICollectionView Datasource and Delegate To the UITableViewCell
extension TableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,numberOfItemsInSection > section: Int) -> Int {
return photoUrls?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PhotoCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoCollectionViewCell.cellIdentifier, for: indexPath) as! PhotoCollectionViewCell
if let value = photoUrls {
cell.reset(with url: value[indexPath.row]);
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: kPhotoWidth, height: kPhotoHeight)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets.init(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout > collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
}
PhotoCollectionViewCell
create a PhotoCollectionViewCell derived from UICollectionViewCell
and add a UIImageView, and make all edge constraints to 0 to the
superview. load UIImageView based on photoUrl.

Swift - How to adjust UICollectionViewCell's ratio for different IOS device sizes

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
}
}

Changing size of selected UICollectionView Cell

I have a simple UICollectionViewController that returns X amount of cells. I want to have it so when a cell is selected, that specific selected cell will change it's size and become larger in height as all of the other cells stay the same. How do I achieve this? Here's my code:
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor(white: 0.90, alpha: 1.0)
collectionView?.register(PostCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.showsVerticalScrollIndicator = false
collectionView?.alwaysBounceVertical = true
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = (view.frame.width) * 9 / 16
return CGSize(width: view.frame.width, height: height + 50 + 50)
}
}
You can check to see if the indexPath from sizeForItemAt is selected. If it is, you return a different height otherwise return the standard height.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.performBatchUpdates(nil, completion: nil)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch collectionView.indexPathsForSelectedItems?.first {
case .some(indexPath):
return CGSize() // your selected height
default:
let height = (view.frame.width) * 9 / 16
return CGSize(width: view.frame.width, height: height + 50 + 50)
}
}

UICollectionViewLayout methods never get called

I am working with a UICollectionView and I am trying to use the UICollectionViewLayout methods to adjust the sizing of the cell however they are never being called. The methods are :
sizeForItemAtIndexPath, minimumInterimItemSpacingForSectionAtIndex, and minimumLineSpacingForSectionAtIndex
these 3 methods are never being called and I'm not sure where/ how to call them from. below is my code in Swift 3
import Foundation
import UIKit
class CollectionVC: UICollectionViewController {
var Sections = [SectionService]()
override func viewDidLoad() {
createServiceSections()
}
func createServiceSections(){
ServiceSections.append(ServiceSection(headerTitle: "1", items: 2, headerImage: UIImage(named: "Default"), stats: nil))
ServiceSections.append(ServiceSection(headerTitle: "2", items: 3, headerImage: UIImage(named: "Default"), stats: nil))
ServiceSections.append(ServiceSection(headerTitle: "3", items: 3, headerImage: UIImage(named: "Default"), stats: nil))
ServiceSections.append(ServiceSection(headerTitle: "4", items: 5, headerImage: UIImage(named: "Default"), stats: nil))
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return ServiceSections.count
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return ServiceSections[indexPath.section].items.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: "servicecell", for: indexPath) as! ServicesCellView
cell1.serviceLabel.text = "test"
cell1.serviceImage.image = ServiceSections[indexPath.section].headerImage
return cell1
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
print("size for item at indexPath")
var cellSize: CGSize
let collectionViewWidth = self.collectionView!.bounds.size.width
cellSize = CGSize(width: (collectionViewWidth * 0.48), height: (collectionViewWidth * 0.48))
return cellSize
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInterItemSpacingForSectionAtIndex section: Int) -> CGFloat {
print("min spacing section")
return 1
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
print("min line spacing section")
return 1
}
}
With help from Xinatanil, I was able to fix the issue. First, I had to change my class line to this
class CollectionVC: UICollectionViewController, UICollectionViewDelegateFlowLayout {
and then I had to change the methods declarations to Swift 3 syntax...
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var cellSize: CGSize
let collectionViewWidth = self.collectionView!.bounds.size.width
cellSize = CGSize(width: (collectionViewWidth * 0.48), height: (collectionViewWidth * 0.48))
return cellSize
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 1
}
I think, you haven't set collectionView delegate
Please try:
self.collectionView.delegate = self in viewDidLoad() method
Actually UICollectionViewDelegateFlowLayout inherits from UICollectionViewDelegate. So setting the delegate will be sufficient.
Happy Coding!