bircastri
bircastri

Reputation: 2169

Set custom space for UICollectionViewCell for every rows

I build ViewController with UiCollectionView, and I created my custom View to display in every cell. This is the code on my controller to display, resize the cell. I need to have 3 cell for every row in UiCollectionView

class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource , UICollectionViewDelegateFlowLayout{
    @IBOutlet weak var collectionView: UICollectionView!
    var listaCategorie = [CategoryModel]()
    
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.listaCategorie.count
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

         let noOfCellsInRow = 3

         let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
         let totalSpace = flowLayout.sectionInset.left
             + flowLayout.sectionInset.right
            + 30
             + (flowLayout.minimumInteritemSpacing * CGFloat(noOfCellsInRow - 1))
         let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(noOfCellsInRow))
        return CGSize(width: 100, height: 130)
     }

    // UICollectionViewDelegateFlowLayout method

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
    insetForSectionAtIndex section: Int) -> UIEdgeInsets {
        let cellWidthPadding = collectionView.frame.size.width / 30
        let cellHeightPadding = collectionView.frame.size.height / 4
        return UIEdgeInsets(top: cellHeightPadding,left: cellWidthPadding, bottom: cellHeightPadding,right: cellWidthPadding)
    }
    
    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        
        var category = self.listaCategorie[indexPath.row];
        
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cella", for: indexPath) as! CustomCellViewsCategories
         
         var puntoLuce = self.listaCategorie[indexPath.row];
         
        cell.labelCategoryName.text = puntoLuce.description
        //cell.image.image = UIImage(named: "light-bulb-2.png");
         cell.backgroundColor = getUIColorFromRGBThreeIntegers(red: 63,green: 162,blue: 217);
         cell.layer.cornerRadius = 6
         cell.layer.masksToBounds = false;
         cell.layer.shadowColor = UIColor.black.cgColor
         cell.layer.shadowOffset = CGSize(width: 0, height: 0)
         cell.layer.shadowOpacity = 0.5
       

        
        //RECUPERO LA DIMENSIONE
        let noOfCellsInRow = 4

        //FINE RECUPERO DIMENSIONE
        
        
        if(puntoLuce.imageUrl != ""){
            let imageUrl:NSURL = NSURL(string: puntoLuce.imageUrl!)!
            
            DispatchQueue.global(qos: .userInitiated).async {
                let imageData:NSData = NSData(contentsOf: imageUrl as URL)!
                DispatchQueue.main.async {
                    let image = UIImage(data: imageData as Data)
                    cell.imageCategory.image = image
                }
            }
        }
        return cell
    }
    
    func getUIColorFromRGBThreeIntegers(red: Int, green: Int, blue: Int) -> UIColor {
        return UIColor(red: CGFloat(Float(red) / 255.0),
                       green: CGFloat(Float(green) / 255.0),
                       blue: CGFloat(Float(blue) / 255.0),
                       alpha: CGFloat(1.0))
    }
    

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.title = "ARRIVA ARRIVA"
        //on click su label temp
        let tap = UITapGestureRecognizer(target: self, action: #selector(HomeViewController.tapFunction))
        getCategoryList()
        collectionView.delegate = self // Unless you have already defined the delegate in IB
        collectionView.dataSource = self // Unless you have already defined the dataSource in IB
        self.collectionView.frame = self.collectionView.frame.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0))
    }
    
    @objc func tapFunction() {
           // handle label tap here
           print("click");
       }
    
    func getCategoryList(){
        var params = [
            "" : ""
        ]
        let postUrl = APIRequest(endPoint: "category_list")
        postUrl.sendRequest(parameters: params as! [String : String]) {
            responseObject, error in
            let user = CategoryModel(id: "0",
            description: "Tutti",
            imageUrl: "")
            self.listaCategorie.append(user)
            guard let responseObject = responseObject, error == nil else {
                print(error ?? "Unknown error")
                return
            }
            do{
                let messageData = try JSONDecoder().decode(ResponseCategoryModel.self, from: responseObject)
                var array = messageData.result
                for categoryModel in array {
                    let user = CategoryModel(id: "",
                                             description: categoryModel.categoryName,
                                             imageUrl: categoryModel.image)
                    self.listaCategorie.append(user)
                }
                print(array.count);
               
                DispatchQueue.main.async { // Correct
                    self.collectionView.reloadData()
                }
                
            }catch{
                print("errore durante la decodifica dei dati")
            }
    }
}

But this is the result:

enter image description here

As you can see from the photo there is too space from the 3 cells. There is a way to set minus space by cells?

EDIT I try to use the code on first response. THis is the result enter image description here

Upvotes: 0

Views: 1797

Answers (2)

jrturton
jrturton

Reputation: 119272

UICollectionViewCompositionalLayout will give you a layout that automatically adjusts to the collection view size. Remove all code you have relating to the flow layout and create a compositional layout in viewDidLoad:

// Cell will be the full height of the enclosing group
let cellHeight = NSCollectionLayoutDimension.fractionalHeight(1)
// Cell will be 1/3 width of the enclosing group
let cellWidth = NSCollectionLayoutDimension.fractionalWidth(0.333)
// The size of the cell
let size = NSCollectionLayoutSize(widthDimension: cellWidth, heightDimension: cellHeight)
// This item represents a single cell
let item = NSCollectionLayoutItem(layoutSize: size)
// The cell will be inset by these distances within the item
item.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)

// The group will be a fixed height
let groupHeight = NSCollectionLayoutDimension.absolute(130)
// The group will occupy the full available width
let groupWidth = NSCollectionLayoutDimension.fractionalWidth(1)
// The group will repeat to hold as many of the cells as it can in a horizontal row before wrapping
let group = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: groupWidth, heightDimension: groupHeight), subitems: [item])

// The actual section, which consists of a single group
let section = NSCollectionLayoutSection(group: group)
// The insets of the group from the edge of the collection view
section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)

// Create and assign the layout
let layout = UICollectionViewCompositionalLayout(section: section)
collectionView.collectionViewLayout = layout

I've tried to break it up into chunks so it makes sense, these layouts can take some time to wrap your head around.

It gives you the following portrait layout:

enter image description here

And in landscape:

enter image description here

If you want a fixed cell size, then use .absoluteWidth for the cell width, and add an interItemSpacing of .flexible to the group.

Using UICollectionViewFlowLayout you can achieve a very similar result with less code than you have in your question. With a plain project, the only collection view related code I had was this in viewDidLoad():

(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.sectionInset = .init(top: 10, left: 10, bottom: 10, right: 10)

Then this single flow layout delegate method:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    var width = (collectionView.bounds.width - 20) / 3
    width -= 10
    return CGSize(width: width, height: 130)
}

This gives you three columns per row in portrait or landscape.

Upvotes: 3

Ronak Patel
Ronak Patel

Reputation: 629

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, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let noOfColumn = 3
    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)
}

Upvotes: 0

Related Questions