0

I am making a collage app for that I will be using multiple grid layouts.I am using UICollectionViewCompositionalLayout to create grids. One of my grids requires a heart shaped cell in between. Following is my code the HeartShaped item needs to be heartShape I've no idea how to do it at first I thought of using UIBeizerPath but item is not type of layer and if somehow I manage to do it inside cellForItem still I've many more grids coming and I will have to use "if" "else" again and again. Another problem I am facing my last group i.e the bottom group is not displaying.

Any help would be really appreciated.

                let topItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/3), heightDimension: .fractionalHeight(1)))
                
//                top items group
                let topGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1/4)), subitem: topItem, count: 3)
                topGroup.contentInsets = .init(top: 0, leading: 3, bottom: 0, trailing: 3)
                topGroup.interItemSpacing = .fixed(2)

//                middle right item
                let middleRightItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/4), heightDimension: .fractionalHeight(1)))

//                 middle left item
                let middleLeftItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/4), heightDimension: .fractionalHeight(1)))

//                Heart Shaped
                let heartShapedItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/2), heightDimension: .fractionalHeight(1)))

//                 middle three items group
                let middleGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1/3)), subitems: [middleRightItem,heartShapedItem,middleLeftItem])
                middleGroup.contentInsets = .init(top: 0, leading: 3, bottom: 0, trailing: 3)
                middleGroup.interItemSpacing = .fixed(2)

//            bottom three items group
                let bottomGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1/4)), subitem: topItem, count: 3)
                
                let finalGroup = NSCollectionLayoutGroup.vertical(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1)), subitems: [topGroup,middleGroup,bottomGroup])
                finalGroup.interItemSpacing = .fixed(2)
                
                let section = NSCollectionLayoutSection(group: finalGroup)
                
                return section

1 Answers1

0

A collection view layout defines the visual arrangement of the content in your collection - thus in your UICollectionViewCompositionalLayout you specify such parameters of your collection view like number of items in a row, sizes and offsets, orthogonal scrolling behavior, headers and decoration items, not how a particular cell looks like.

In cellForItem you could customize your cells including its layers - you could modify shapes there. Cells will be arranged in the collection view based on parameters of your UICollectionViewCompositionalLayout.

If you are using UICollectionViewDiffableDataSource, you could differentiate between different type of cells (i.e. cells with a heart shape and a circle shape) with a following code example:

func configureDataSource() {
    dataSource = DataSource(collectionView: collectionView) { [self] (collectionView: UICollectionView, indexPath: IndexPath, item: ViewCell) -> UICollectionViewCell? in
        switch item.type {
        case .heart:  return hearCell(for: indexPath)
        case .circle: return circleCell(for: indexPath)
        default:      return normalCell(for: indexPath)
        }
    }
}

In custom struct ViewCell I have an enum for CellType:

struct ViewCell: Hashable {
    var tag: Int
    var type: CellType
}

enum CellType { case heart, circle }

Regarding the problem with the bottom group - do you have enough items in your collection view to fill all the groups?

UPDATE:

The problem in your layout is with this line:

middleGroup.interItemSpacing = .fixed(2)

With it 3 rows in the middle group can't be placed in one row. You could remove it and achieve the same spacing by adding insets to the middle item:

heartShapedItem.contentInsets = .init(top: 0, leading: 2, bottom: 0, trailing: 2)

Since you are not using UICollectionViewDiffableDataSource, you could differentiate your cells by indexPath. So if you have just 1 section, your heartShape item will have indexPath.row of 4:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.reuseID, for: indexPath) as? MyCollectionViewCell else { return UICollectionViewCell() }

    cell.label.text = "\(indexPath.row)"
    cell.label.textAlignment = .center
    cell.backgroundColor = .green
    
    if indexPath == IndexPath(row: 4, section: 0) {
        cell.label.text = "HEART"
    }
    
    return cell
}

enter image description here

(in your original code there is not spacing between items in the bottom group, I don't know if it was done intentionally or not)

Viktor Gavrilov
  • 830
  • 1
  • 8
  • 13
  • yes I do have all enough items I am using topitem twice – Omair Khan Aug 15 '21 at 04:40
  • can you tell me how can I use enum with UICollectionViewCompositionalLayout sorry I am not very good with swift yet if you want I can share my code for flowlayout – Omair Khan Aug 15 '21 at 07:42
  • UICollectionViewCompositionalLayout could be used either with UICollectionViewDataSource (which you are using) or UICollectionViewDiffableDataSource (which I suggest to use). Enum I've shown in my answer is part of UICollectionViewDiffableDataSource and allows to differentiate cells not only by indexPath (like in your case with collectionView(_:cellForItemAt:)), but with custom item too. Custom item could have as many properties as you need, including enum for the cell type. – Viktor Gavrilov Aug 15 '21 at 07:56
  • dataSource = UICollectionViewDiffableDataSource
    (collectionView: collectionView!) { [self] (CollectionView, indexPath, image) -> CollectionViewCell This is my diffabledatasource I am really confused on how to use the enum sorry for bothering but I am not much experienced with swift. I loved the way diffable works I changed my code to diffable now but still not sure how to access the item using enum.
    – Omair Khan Aug 15 '21 at 12:53
  • You should add a property "type" to your imageModel (I've updated the code with an example for it). After that you could use a switch statement in your dataSource to differentiate between image types as shown in my answer – Viktor Gavrilov Aug 15 '21 at 13:05
  • I understand your point now. OK! I can't use type property inside imageModel. I have to use section enum because its a collage app now I am trying to make multiple sections inside enum but when I create switch inside UICollectionViewCompositionalLayout it throws error "Enum case 'main' is not a member of type 'UICollectionViewCompositionalLayout'" – Omair Khan Aug 15 '21 at 17:14
  • If you can't change imageModel, you could create a struct which will have a property for the imageModel, as well as enum for cell type. I am not sure I understand what are you trying to do with enum in UICollectionViewCompositionalLayout. – Viktor Gavrilov Aug 15 '21 at 17:21
  • With DiffableDataSource you are creating a snapshot which has sections and items, related to this sections. I suggest that your items should have a type property so you could create different cells (i.e. heart shape). In your UICollectionViewCompositionalLayout you specify visual arrangement of the cells in the section - like how many items do you want in group, which insets, etc. You could have an enum for CompositionalLayout too, but it will differentiate between sections, not items. – Viktor Gavrilov Aug 15 '21 at 17:25
  • First i need to differentiate between thee section each section is a collage user can choose one at time depending on the chosen collage grid will be displayed. I already created multiple layouts so now I want to display different layout for each section. If i assign a type property with image I am not sure which collagee user going to pick. – Omair Khan Aug 16 '21 at 04:30
  • 1
    Thank you very much Viktor it wasn't exactly what I wanted to do but you bought me really close to what I wanted to do and I did it with your help – Omair Khan Aug 17 '21 at 07:32