17

I am trying to dynamically center the content of a UICollectionView, however none of the existing solutions take into account the fact that there might be more than one cell per row.

I have tried this center custom title in UINavigationBar? as well as both solutions here: UICollectionView vertically Centred, to no avail.

I am quite surprised iOS offers no way of doing this by default and even more that it is not possible to get the number of rows currently being displayed (which would've solved my issue as well). Also, I cannot get the size of the content (and not simply the size of the frame) which would also have helped me achieve the desired effect.

I have this:

 --------
|X X X X |
|X X X X |
|        |
|        |
 --------

But I want this:

 --------
|        |
|X X X X |
|X X X X |
|        |
 --------

Any help would be greatly appreciated,

Many thanks.

Community
  • 1
  • 1
Max Dum
  • 181
  • 1
  • 1
  • 8
  • 1
    I don't understand your problem. And what do you mean by how to "get" the number of rows? You tell the collection view the number of items, their size, etc... – Darko Nov 27 '15 at 20:28
  • @Darko: In order to center a group of cells I need to be able to know how many rows the collection view is displaying (which is different to the number of items as multiple items can be displayed in a row, depending on the screen size). Once I have the number of cells I can set the top inset to (collectionview frame height - total height occupied by the cells) / 2. I have added a visual representation to my question. – Max Dum Nov 28 '15 at 11:27
  • In regard to your visual representation - if you want version 2, why do you make the collection view so big? Just make it smaller and center it in the screen with auto layout. Apart from this (as jorf already answered) you could set the section insets. But in my opinion the insets are only useful if you want to change it during runtime. If the layout is fixed just make the collection view smaller. – Darko Nov 28 '15 at 13:01
  • @Darko because depending on the screen size I get a very different layout. On iphone 6s for 12 items, I get two rows of 6 cells. On an iphone 4s I get 6 rows of 2 items. Not sure I can account for that with auto layouts. – Max Dum Nov 28 '15 at 14:25
  • Ah, ok, now I understand your problem. Depending on the screen size you need another inset to make it look centered. Then you have to calculate it manually, but it should not be that hard. You know the available space, the number of items and the cell size. Just calculate the section inset from top. – Darko Nov 28 '15 at 14:31
  • If you want to add that as an answer I'll accept it, seems like the only viable way of doing this without subclassing UICollectionViewFlowLayout – Max Dum Nov 28 '15 at 15:33

7 Answers7

32

Not sure why you can't get the size of the content — UICollectionView is a UIScrollView subclass, hence has access to the contentSize property. If you only need to center the cells in case they are all visible without scrolling, you can simply do the following:

collectionView.contentInset.top = max((collectionView.frame.height - collectionView.contentSize.height) / 2, 0)

This will also work in cases when the number of cells grows so that not all of them are visible on the screen and you start using vertical scrolling — the top inset would be set to 0.

Alex Staravoitau
  • 2,222
  • 21
  • 27
4

Now I understand your problem. Depending on the screen size you need another inset to make it look centered. Then you have to calculate it manually, but it should not be that hard. You know the available space, the number of items and the cell size. Just calculate the section inset from top.

Darko
  • 9,655
  • 9
  • 36
  • 48
0

It's an old thread but I think the most obvious solution is lacking.

I would simple put the UICollectionView in a full view container and align the collection view in center with autolayout.

UICollectionView should take the optimal size to fit the cells and then stay centered.

esbenr
  • 1,356
  • 1
  • 11
  • 34
0

If using UICollectionView with compositional layout, the following solution will work:

private func makeFullHeightSection() -> NSCollectionLayoutSection {
  let group = NSCollectionLayoutGroup.vertical(
    layoutSize: .init(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0)),
    subitems: [
      .init(
        layoutSize: .init(
          widthDimension: .fractionalWidth(1.0),
          heightDimension: .fractionalHeight(1.0)
        )
      )
    ]
  )

  let section = NSCollectionLayoutSection(group: group)
  return section
}

In the fullscreen cell, center the content vertically (I'm using snapkit):

private func installConstraints() {
  stackView.snp.makeConstraints { make in
    make.directionalHorizontalEdges.equalToSuperview()
    make.centerY.equalToSuperview()
}

Set the collection view's contentInsetAdjustmentBehavior property as follows:

collectionView.contentInsetAdjustmentBehavior = .never

If for some reason you want to change between having full screen cells and not, after changing contentInsetAdjustmentBehavior, reset the contentOffset:

collectionView.contentOffset = CGPoint(x: 0, y: 0)
Andrew Stoddart
  • 408
  • 4
  • 9
-1

UICollectionView has a visibleCells property that returns an array. You can use the count property on NSArray to get the number of visible cells.

You should be able to implement - collectionView:layout:insetForSectionAtIndex: in your delegate implementation to set the top inset to something that will result in the centering you're shooting for.

You can also subclass UICollectionViewFlowLayout and implement your own layout logic if necessary.

i_am_jorf
  • 53,608
  • 15
  • 131
  • 222
  • See my second comment to Darko as to why this is not a viable solution. This assumes I have the same amount of cells for every row whatever the size of the screen. Although all cells are visible, they will not be displayed on a constant set of rows. – Max Dum Nov 28 '15 at 14:27
  • the problem is still exist for me can you help me please ? here is my question link https://stackoverflow.com/questions/49727912/how-to-use-center-vertically-cells-in-collection-view-in-paging-mode-in-swift-4 – Saeed Rahmatolahi Apr 18 '18 at 05:13
-1

If you have a tabBar do this

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {

    let navBarHeight       = self.navigationController?.navigationBar.frame.height
    let collectionViewHeight = (self.collectionView?.frame.height)! - navBarHeight!
    let itemsHeight        = self.collectionView?.contentSize.height

    let topInset = ( collectionViewHeight - itemsHeight! ) / 4

    return UIEdgeInsetsMake(topInset ,0, 0 , 0)
}

If not ignore the tabBar substraction and leave the rest...

Jesus Rodriguez
  • 2,571
  • 2
  • 22
  • 38
-1

Add this in viewDidLoad

let viewHeight: CGFloat = view.center.y
let contentHeight: CGFloat = collectionView.contentSize.height
let marginHeight: CGFloat = (viewHeight - contentHeight) / 2.0
self.collectionView.contentInset = UIEdgeInsets(top: marginHeight, left: 0, bottom:  0, right: 0)
Ahmed R.
  • 1,209
  • 1
  • 12
  • 22