简体   繁体   中英

How to center cells inside collection view using collection view compositional layout

I have the following task at work: I need to implement the cinema view where I have a screen and a collection of seats where each row represents a section: 在此处输入图像描述<\/a>

var seats = [[1, 2, 3, 4, 5],
             [1, 2, 3, 4, 5],
            [1, 2, 3, 4, 5, 6],
            [1, 2, 3, 4, 5, 6],
            [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
            [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
]

Let each row, or at least each set of rows with the same number of items, be its own section, and give the layout a section provider function. The section provider function is given the section number along with an environment object. From that environment object, you can learn the width of the collection view. Now just give the group or section appropriate insets.

To illustrate, I was able to achieve a layout like this (three sections, number of items per section is the section index plus one):

在此处输入图片说明

And I won't keep you in suspense; I'll just show you the code for that layout. It isn't pretty; I used a bunch of hard-coded values. The idea was just to generate the layout so as to show you the general principles. But I'm sure you can see how to adapt this to your own situation:

let config = UICollectionViewCompositionalLayoutConfiguration()
config.scrollDirection = .vertical
let inter = 5 as CGFloat
config.interSectionSpacing = 5
let layout = UICollectionViewCompositionalLayout(sectionProvider: { ix, environment in
    let side = 30 as CGFloat
    let cellsz = NSCollectionLayoutSize(widthDimension: .absolute(side), heightDimension: .fractionalHeight(1))
    let cell = NSCollectionLayoutItem(layoutSize: cellsz)
    let groupsz = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(side))
    let count = ix+1
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupsz, subitems: [cell])
    group.interItemSpacing = .fixed(inter)
    let sec = NSCollectionLayoutSection(group: group)
    let width = environment.container.contentSize.width
    let inset = (width - CGFloat(count)*side - (CGFloat(count)-1)*inter) / 2.0
    sec.contentInsets = .init(top: 0, leading: inset, bottom: 0, trailing: 0)
    return sec
}, configuration: config)

That's probably not the only way; you could instead, I think, call NSCollectionLayoutGroup.custom and just lay out the cells manually. But I think this is not a bad solution.

I should also say that I'm not persuaded that a collection view is the best way to get the result you're trying to achieve. You might do better just to lay out the seat views yourself, directly, in code.

I have the same problem while implementing my own keyboard. My result: Keyboard images . This is how I did it without using constants (an improvement of Matt's awesome answer).

  private func generateKeyboardLayout() -> UICollectionViewLayout {

    let groupInset = 3 / UIScreen.main.scale
    let layout = UICollectionViewCompositionalLayout { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
      let item = NSCollectionLayoutItem(
        layoutSize: NSCollectionLayoutSize(
          widthDimension: .fractionalWidth(1.0),
          heightDimension: .fractionalHeight(1.0)))
      
      let group = NSCollectionLayoutGroup.horizontal(
        layoutSize: NSCollectionLayoutSize(
          widthDimension: .fractionalWidth(1/10),
          heightDimension: .fractionalHeight(1/3)),
        subitems: [item])
      group.contentInsets = NSDirectionalEdgeInsets(top: groupInset, leading: groupInset, bottom: groupInset, trailing: groupInset)
      
      let count = Keyboard.getItems(at: keyboardSection).count
      let containerWidth = layoutEnvironment.container.contentSize.width
      let groupWidthDimension = group.layoutSize.widthDimension.dimension
      let itemWidth = containerWidth * groupWidthDimension
      let inset = (containerWidth - CGFloat(count) * itemWidth) / 2.0
  
      let section = NSCollectionLayoutSection(group: group)
      section.contentInsets = .init(top: 0, leading: round(inset), bottom: 0, trailing: 0)
      section.orthogonalScrollingBehavior = .continuous
      return section
    }
    return layout
  }

count represents the number of items per row

let count = Keyboard.getItems(at: keyboardSection).count

containerWidth is the exact width of the view in CGFloat

let containerWidth = layoutEnvironment.container.contentSize.width

groupWidthDimension is the width of each group / keyboard tile including the applied insets

let groupWidthDimension = group.layoutSize.widthDimension.dimension

itemWidth gives you the exact size of each keyboard tile

let itemWidth = containerWidth * groupWidthDimension

inset (leading) is calculated based on the difference of the containerWidth and the sum of all the items' width and then halved

let inset = (containerWidth - CGFloat(count) * itemWidth) / 2.0
section.contentInsets = .init(top: 0, leading: round(inset), bottom: 0, trailing: 0)

You could use NSCollectionLayoutGroup.custom(layoutSize:)<\/code> where we can set the frame using NSCollectionLayoutGroupCustomItemProvider<\/code> .

var customItems = [NSCollectionLayoutGroupCustomItem]()
            for item in 0..<itemCount {
                let frame = CGRect(x: yourCalculatedMargin, y: yPosition, width: layoutSize.widthDimension.dimension, height: layoutSize.heightDimension.dimension)
                customItems.append(NSCollectionLayoutGroupCustomItem(frame: frame))
            }
            return customItems

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM