Как центрировать ячейки UICollectionView по горизонтали?

126

Я провел некоторое исследование, но не смог найти ни одного примера кода о том, как центрировать ячейки в UICollectionView по горизонтали.

вместо того, чтобы первая ячейка была похожа на эту X00 , я хочу, чтобы она была такой же, как эта 0X0 . есть ли способ добиться этого?

РЕДАКТИРОВАТЬ:

чтобы визуализировать то, что я хочу:

введите описание изображения здесь

Мне нужно, чтобы он выглядел как версия B, когда в CollectionView есть только один элемент. Когда у меня больше одного элемента, тогда он должен быть похож на версию A, но с большим количеством элементов.

На данный момент это похоже на версию A, когда у меня всего 1 элемент, и мне интересно, как я могу сделать его похожим на B.

Спасибо за помощь!

RaptoX
источник
Разве не проще позволить ячейке соответствовать ширине представления коллекции, а затем центрировать само представление коллекции внутри его родительского элемента?
Артур Геворкян
да, есть как минимум два способа сделать это, первый (быстрый) - сделать ячейку шириной всего экрана и центрировать ее дочернее представление. второй (справа) реализовать настраиваемый макет представления коллекции
sage444,
В конечном итоге из бэкэнда будет поступать больше ячеек, заполнение всей ширины - не лучшая идея
RaptoX
увеличения ширины достаточно, чтобы установить по центру
Кишор Кумар

Ответы:

231

Не рекомендуется использовать библиотеку, если ваша цель только в этом, например, для выравнивания по центру.

Лучше вы можете выполнить этот простой расчет в своей функции collectionViewLayout.

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

    let totalCellWidth = CellWidth * CellCount
    let totalSpacingWidth = CellSpacing * (CellCount - 1)

    let leftInset = (collectionViewWidth - CGFloat(totalCellWidth + totalSpacingWidth)) / 2
    let rightInset = leftInset

    return UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: rightInset)
}
Даршан Патель.
источник
2
@ DarshanPatel. Большое спасибо за ответ, я реализовал это, и строки оказались в центре, как и должно, но теперь проблема в том, что я не могу перейти к первому элементу. Когда я пытаюсь перейти к первому элементу, он возвращает меня к измененным UIEdgeInsets. Вы можете увидеть мое демонстрационное приложение github.com/anirudha-music/CollectionViewCenter
Anirudha Mahale
5
В Swift 3 новая сигнатура метода - collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int).
Камчатка
1
как вы можете получить cellWidth, возможно ли это с помощью метода cellForItemAt? Я попробовал, и он вернул ноль .. ширина ячейки для меня меняется в зависимости от размера экрана .. @DarshanPatel.
Крис
10
@DarshanPatel. Ваш ответ может иногда давать отрицательное значение на небольших устройствах. Подумайте об использовании максимальной проверки вашего leftInsetзначения следующим образом:let leftInset = max(0.0, (self.collectionView.bounds.width - CGFloat(totalCellWidth + totalSpacingWidth)) / 2)
Rhuari Glen
3
Если вы не подклассифицируете UICollectionViewController, убедитесь, что ваш класс соответствует UICollectionViewDelegateFlowLayout, иначе он не сработает
Саид Ир
59

Swift 5.1

func centerItemsInCollectionView(cellWidth: Double, numberOfItems: Double, spaceBetweenCell: Double, collectionView: UICollectionView) -> UIEdgeInsets {
    let totalWidth = cellWidth * numberOfItems
    let totalSpacingWidth = spaceBetweenCell * (numberOfItems - 1)
    let leftInset = (collectionView.frame.width - CGFloat(totalWidth + totalSpacingWidth)) / 2
    let rightInset = leftInset
    return UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: rightInset)
}

Swift 4.2

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

    let totalCellWidth = 80 * collectionView.numberOfItems(inSection: 0)
    let totalSpacingWidth = 10 * (collectionView.numberOfItems(inSection: 0) - 1)

    let leftInset = (collectionView.layer.frame.size.width - CGFloat(totalCellWidth + totalSpacingWidth)) / 2
    let rightInset = leftInset

    return UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: rightInset)

}

Swift 3

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

        let totalCellWidth = 80 * collectionView.numberOfItems(inSection: 0)
        let totalSpacingWidth = 10 * (collectionView.numberOfItems(inSection: 0) - 1)

        let leftInset = (collectionView.layer.frame.size.width - CGFloat(totalCellWidth + totalSpacingWidth)) / 2
        let rightInset = leftInset

        return UIEdgeInsetsMake(0, leftInset, 0, rightInset)

    }

не забудьте добавить протокол

UICollectionViewDelegateFlowLayout
Ахмед Сафади
источник
ограничение может быть @ashForIos
Ахмед Сафади
Ваш ответ отлично работает, когда есть ячейки, которые подходят для collectionView, когда количество ячеек увеличивается, ячейки находятся в центре, и вы не можете прокручивать до первых или последних ячеек.
NickCoder
2
@Vitalii 80 означает ширину ячейки и 10 пространство между ячейками, если вы прочитаете имя переменной, вы поймете, что оно означает: P
Ахмед Сафади,
Решение Swift 4.2 проще, СПАСИБО! Только не забудьте установить «80» на любую фактическую ширину объекта ячейки.
Джон Питтс,
25

Попробуйте это для Swift 4

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        let cellWidth : CGFloat = 165.0

        let numberOfCells = floor(self.view.frame.size.width / cellWidth)
        let edgeInsets = (self.view.frame.size.width - (numberOfCells * cellWidth)) / (numberOfCells + 1)

        return UIEdgeInsetsMake(15, edgeInsets, 0, edgeInsets)
    }

Добавьте свою cellWidth вместо 165.0

Оскар Кастельон
источник
1
это лучший ответ. С самой простой математикой. работает с любым количеством строк и столбцов
rickrvo
20

Я использую для этого KTCenterFlowLayout , и он отлично работает. Это настраиваемый подкласс, UICollectionViewFlowLayoutкоторый центрирует ячейки по вашему желанию. (Примечание: это нетривиальная задача, которую можно решить, разместив код, поэтому я связываюсь с проектом GitHub!)

TwoStraws
источник
Не удалось заставить работать от ИБ. Эта библиотека работала для меня как шарм. Просто установил под и поменял класс верстки в ИБ!
tagirkaZ
15

Версия ответа Даршана Пателя на языке C :

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(nonnull UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    CGFloat totalCellWidth = kItemWidth * self.dataArray.count;
    CGFloat totalSpacingWidth = kSpacing * (((float)self.dataArray.count - 1) < 0 ? 0 :self.dataArray.count - 1);
    CGFloat leftInset = (self.bounds.size.width - (totalCellWidth + totalSpacingWidth)) / 2;
    CGFloat rightInset = leftInset;
    UIEdgeInsets sectionInset = UIEdgeInsetsMake(0, leftInset, 0, rightInset);
    return sectionInset;
}
привлекательный
источник
Спасибо. Он отлично работает с одним рядом. но создавая проблему в нескольких строках. Я не могу добавить сюда URL, чтобы показать снимок экрана. , но вы можете добавить yynoalzg в крошечный URL-адрес. Вы получите представление. Золотое сечение имеет 4 записи. 4-й должен быть в новой строке. . но после этого метода он отображается так ... дайте мне знать, если какая-нибудь идея.
Hitarth
6

Немного изменив ответ @Safad Funy, это то, что сработало для меня в последней версии Swift и iOS. В этом случае я хотел, чтобы ширина ячеек составляла треть размера представления коллекции.

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

  let totalCellWidth = Int(collectionView.layer.frame.size.width) / 3 * collectionView.numberOfItems(inSection: 0)
  let totalSpacingWidth = (collectionView.numberOfItems(inSection: 0) - 1)

  let leftInset = (collectionView.layer.frame.size.width - CGFloat(totalCellWidth + totalSpacingWidth)) / 2
  let rightInset = leftInset

  return UIEdgeInsetsMake(0, leftInset, 0, rightInset)
}
MXV
источник
2
У меня это особенно сработало, когда есть только одна ячейка.
Дасога
6

Вы можете использовать это расширение (Swift 4).

Он может центрировать клетки, если у вас collectionViewесть layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize.

Он работает с любым размером ячеек и отлично работает, когда scrollDirection = .horizontal

public extension UICollectionView {
    func centerContentHorizontalyByInsetIfNeeded(minimumInset: UIEdgeInsets) {
        guard let layout = collectionViewLayout as? UICollectionViewFlowLayout,
            layout.scrollDirection == .horizontal else {
                assertionFailure("\(#function): layout.scrollDirection != .horizontal")
                return
        }

        if layout.collectionViewContentSize.width > frame.size.width {
            contentInset = minimumInset
        } else {
            contentInset = UIEdgeInsets(top: minimumInset.top,
                                        left: (frame.size.width - layout.collectionViewContentSize.width) / 2,
                                        bottom: minimumInset.bottom,
                                        right: 0)
        }
    }
}


final class Foo: UIViewController {
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        collectionView.centerContentHorizontalyByInsetIfNeeded(minimumInset: yourDefaultInset)
    }
}

Надеюсь, это вам поможет!

С. Мацепура
источник
1
Получение ошибки: Thread 1: EXC_BAD_ACCESS (code = 2, address = 0x118ffde58)
atulkhatri
4

Swift 4.2 (по горизонтали и вертикали). Это небольшое обновление кода Pink Panther и большое ему спасибо!


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
    let cellWidth: CGFloat = flowLayout.itemSize.width
    let cellHieght: CGFloat = flowLayout.itemSize.height
    let cellSpacing: CGFloat = flowLayout.minimumInteritemSpacing
    let cellCount = CGFloat(collectionView.numberOfItems(inSection: section))
    var collectionWidth = collectionView.frame.size.width
    var collectionHeight = collectionView.frame.size.height
    if #available(iOS 11.0, *) {
        collectionWidth -= collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right
        collectionHeight -= collectionView.safeAreaInsets.top + collectionView.safeAreaInsets.bottom
    }
    let totalWidth = cellWidth * cellCount + cellSpacing * (cellCount - 1)
    let totalHieght = cellHieght * cellCount + cellSpacing * (cellCount - 1)
    if totalWidth <= collectionWidth {
        let edgeInsetWidth = (collectionWidth - totalWidth) / 2

        print(edgeInsetWidth, edgeInsetWidth)
        return UIEdgeInsets(top: 5, left: edgeInsetWidth, bottom: flowLayout.sectionInset.top, right: edgeInsetWidth)
    } else {
        let edgeInsetHieght = (collectionHeight - totalHieght) / 2
        print(edgeInsetHieght, edgeInsetHieght)
        return UIEdgeInsets(top: edgeInsetHieght, left: flowLayout.sectionInset.top, bottom: edgeInsetHieght, right: flowLayout.sectionInset.top)

    }
}

Убедитесь, что ваш класс соответствует протоколу UICollectionViewDelegateFlowLayout

ЕВГЕНИЙ ДАНИЛОВ
источник
На самом деле ваш код отлично работает для меня, спасибо, чувак; )
steveSarsawa
4

Вот более новая версия Swift 5, которая также отлично работает, когда ячейки составляют более одной строки:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
    let cellWidth: CGFloat = flowLayout.itemSize.width
    let cellSpacing: CGFloat = flowLayout.minimumInteritemSpacing
    var cellCount = CGFloat(collectionView.numberOfItems(inSection: section))
    var collectionWidth = collectionView.frame.size.width
    var totalWidth: CGFloat
    if #available(iOS 11.0, *) {
        collectionWidth -= collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right
    }
    repeat {
        totalWidth = cellWidth * cellCount + cellSpacing * (cellCount - 1)
        cellCount -= 1
    } while totalWidth >= collectionWidth

    if (totalWidth > 0) {
        let edgeInset = (collectionWidth - totalWidth) / 2
        return UIEdgeInsets.init(top: flowLayout.sectionInset.top, left: edgeInset, bottom: flowLayout.sectionInset.bottom, right: edgeInset)
    } else {
        return flowLayout.sectionInset
    }
}

Пожалуйста , убедитесь , что ваш класс Соответствует к UICollectionViewDelegateFlowLayoutпротоколу.

Саид Ир
источник
3

Swift 4

extension ViewController: UICollectionViewDelegateFlowLayout {

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

        let cellWidth: CGFloat = 170.0 // Your cell width

        let numberOfCells = floor(view.frame.size.width / cellWidth)
        let edgeInsets = (view.frame.size.width - (numberOfCells * cellWidth)) / (numberOfCells + 1)

        return UIEdgeInsetsMake(0, edgeInsets, 0, edgeInsets)
    }

 }
Гарольдо Гондим
источник
2

Для людей, которые хотят добавить только отступы ( сверху, слева, снизу, справа ):

Добавить протокол UICollectionViewDelegateFlowLayout

В этом примере показано заполнение слева и справа 40.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {        
    return UIEdgeInsetsMake(0, 40, 0, 40)
}
chainstair
источник
2

SWIFT 4.2

private lazy var contentView: UICollectionView = {
        let layoutView: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
            layoutView.scrollDirection = .horizontal
            layoutView.minimumInteritemSpacing = 0
            layoutView.minimumLineSpacing = 5

        let collectionView: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: layoutView)
            collectionView.dataSource = self
            collectionView.delegate = self
            collectionView.showsHorizontalScrollIndicator = false
            collectionView.isPagingEnabled = true
            collectionView.registerCell(Cell.self)
            collectionView.backgroundColor = .clear
            collectionView.translatesAutoresizingMaskIntoConstraints = false
        return collectionView
    }()

//

extension CustomCollectionView: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        return CGSize(width: collectionView.frame.width*4/5, height: collectionView.frame.height)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        let cellWidth : CGFloat = collectionView.frame.width*4/5

        let numberOfCells = floor(collectionView.frame.width / cellWidth)
        let edgeInsets = (collectionView.frame.width - (numberOfCells * cellWidth)) / (numberOfCells + 1)

        return UIEdgeInsets(top: 0, left: edgeInsets, bottom: 0, right: edgeInsets)
    }
}
Ивген Подкорытов
источник
1

Вы можете попробовать мое решение, оно работает нормально,

func refreshCollectionView(_ count: Int) {
    let collectionViewHeight = collectionView.bounds.height
    let collectionViewWidth = collectionView.bounds.width
    let numberOfItemsThatCanInCollectionView = Int(collectionViewWidth / collectionViewHeight)
    if numberOfItemsThatCanInCollectionView > count {
        let totalCellWidth = collectionViewHeight * CGFloat(count)
        let totalSpacingWidth: CGFloat = CGFloat(count) * (CGFloat(count) - 1)
        // leftInset, rightInset are the global variables which I am passing to the below function
        leftInset = (collectionViewWidth - CGFloat(totalCellWidth + totalSpacingWidth)) / 2;
        rightInset = -leftInset
    } else {
        leftInset = 0.0
        rightInset = -collectionViewHeight
    }
    collectionView.reloadData()
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsetsMake(0, leftInset, 0, rightInset)
}
Анирудха Махале
источник
1

Принятая ответ правильный ответ , но если ваш totalCellWidthменьше , чем CollectionViewwidth, но только , чтобы защититься от этого вы можете сделать , как показано ниже.

if (leftInset > 0) {
     return UIEdgeInsetsMake(0, leftInset, 0, rightInset)
  } else {
     return UIEdgeInsetsMake(0, 10, 0, 10)
}
azwethinkweiz
источник
1

Этот код должен центрировать представление коллекции по горизонтали даже в Swift 4.0 без каких-либо изменений:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
    let cellWidth: CGFloat = flowLayout.itemSize.width
    let cellSpacing: CGFloat = flowLayout.minimumInteritemSpacing
    let cellCount = CGFloat(collectionView.numberOfItems(inSection: section))
    var collectionWidth = collectionView.frame.size.width
    if #available(iOS 11.0, *) {
        collectionWidth -= collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right
    }
    let totalWidth = cellWidth * cellCount + cellSpacing * (cellCount - 1)
    if totalWidth <= collectionWidth {
        let edgeInset = (collectionWidth - totalWidth) / 2
        return UIEdgeInsetsMake(flowLayout.sectionInset.top, edgeInset, flowLayout.sectionInset.bottom, edgeInset)
    } else {
        return flowLayout.sectionInset
    }
}

Убедитесь, что ваш класс соответствует UICollectionViewDelegateFlowLayoutпротоколу

Саид Ир
источник
0

В итоге я выбрал совершенно другой подход, о котором, думаю, стоит упомянуть.

Я установил ограничение для представления моей коллекции, чтобы оно было выровнено по центру по горизонтали. Затем я устанавливаю другое ограничение, определяющее ширину. Я создал выход для ограничения ширины внутри моего viewController, который содержит представление коллекции. Затем, когда мой источник данных изменяется и я обновляю представление коллекции, я подсчитываю количество ячеек и выполняю (очень похожий) расчет для сброса ширины.

let newWidth = (items.count * cellWidth) + (items.count * cellSpacing)

Затем я установил выход ограничения .constant равным результату расчета, а остальное делает автопрокладка.

Это может конфликтовать с UICollectionViewDelegateFlowLayout, но у меня это отлично сработало, чтобы создать представление коллекции с выравниванием по левому краю. Без делегата это работает только тогда, когда ячейки заполняют большую часть представления.

Брукс Дюбуа
источник
0

Общее решение для flowlayout, которое центрирует страницы, если они меньше ширины, и выравнивает по левому краю, если их больше.

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(nonnull UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    // Centering if there are fever pages
    CGSize itemSize = [(UICollectionViewFlowLayout *)collectionViewLayout itemSize];
    CGFloat spacing = [(UICollectionViewFlowLayout *)collectionViewLayout minimumLineSpacing];

    NSInteger count = [self collectionView:self numberOfItemsInSection:section];
    CGFloat totalCellWidth = itemSize.width * count;
    CGFloat totalSpacingWidth = spacing * ((count - 1) < 0 ? 0 : count - 1);
    CGFloat leftInset = (self.bounds.size.width - (totalCellWidth + totalSpacingWidth)) / 2;
    if (leftInset < 0) {
        UIEdgeInsets inset = [(UICollectionViewFlowLayout *)collectionViewLayout sectionInset];
        return inset;
    }
    CGFloat rightInset = leftInset;
    UIEdgeInsets sectionInset = UIEdgeInsetsMake(0, leftInset, 0, rightInset);
    return sectionInset;
}

Версия Swift (преобразованная из ObjC)

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    // Centering if there are fever pages
    let itemSize: CGSize? = (collectionViewLayout as? UICollectionViewFlowLayout)?.itemSize
    let spacing: CGFloat? = (collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing

    let count: Int = self.collectionView(self, numberOfItemsInSection: section)
    let totalCellWidth = (itemSize?.width ?? 0.0) * CGFloat(count)
    let totalSpacingWidth = (spacing ?? 0.0) * CGFloat(((count - 1) < 0 ? 0 : count - 1))
    let leftInset: CGFloat = (bounds.size.width - (totalCellWidth + totalSpacingWidth)) / 2
    if leftInset < 0 {
        let inset: UIEdgeInsets? = (collectionViewLayout as? UICollectionViewFlowLayout)?.sectionInset
        return inset!
    }
    let rightInset: CGFloat = leftInset
    let sectionInset = UIEdgeInsets(top: 0, left: Float(leftInset), bottom: 0, right: Float(rightInset))
    return sectionInset
}

Untitled-3.png

Питер Лапису
источник
Что такое границы в быстром коде? Я получаю сообщение об ошибке «Использование неразрешенных границ» идентификатора
Sachin Tanpure
Привет, в моем примере я реализовал метод внутри UIView, у которого есть границы, если вы реализуете его где-то еще, используйте соответствующие границы
Питер Лапису
0

У меня похожая ситуация в проекте, и я исправил ее, сославшись на UPCarouselFlowLayout

Я думаю, это поддерживает swift 5версию

https://github.com/ink-spot/UPCarouselFlowLayout/blob/master/UPCarouselFlowLayout/UPCarouselFlowLayout.swift

Посмотрите на реализацию кода в

override open func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint
Радж
источник
0

Самый простой способ - установить для размера оценки представления коллекции значение Нет в раскадровке или с помощью кода layout.estimatedItemSize = CGSize.zero

remykits
источник
0

Если есть место только для одной ячейки на группу, символы leading:и trailing:of .flexible(0)центрируют ячейку по горизонтали:

item.edgeSpacing = NSCollectionLayoutEdgeSpacing(
    leading: .flexible(0), top: nil,                                                     
    trailing: .flexible(0), bottom: nil
)
Bruno
источник
0

Я использовал этот код в своем проекте. Он центрирует collectionView по горизонтали и вертикали в обоих направлениях .horizontalи .verticalс использованием вставок раздела. Если задано, соблюдается интервал и исходная вставка раздела. Код для использования в делегате, UICollectionViewDelegateFlowLayoutчтобы у нас был доступ ко всем свойствам, которые нам нужно получить из UIcollectionViewили установить в раскадровке для повторного использования.

// original function of the delegate
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    // casting the layout as a UICollectionViewFlowLayout to have access to the properties of items for reusability - you could also link the real one from the storyboard with an outlet
    let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
    // getting all the properties we need
    let itemWidth = flowLayout.itemSize.width
    let itemHeight = flowLayout.itemSize.height
    let interSpacing = flowLayout.minimumInteritemSpacing
    let lineSpacing = flowLayout.minimumLineSpacing
    // getting the size of the collectionView
    let collectionWidth = collectionView.bounds.width
    let collectionHeight = collectionView.bounds.height
    // getting the direction to choose how to align the collection
    let direction = flowLayout.scrollDirection
    // you don't want to have an item greater than the collection
    guard (itemWidth < collectionWidth && direction == .vertical) || (itemHeight < collectionHeight && direction == .horizontal) else {
        print("Really?")
        return UIEdgeInsets(top: flowLayout.sectionInset.top, left: flowLayout.sectionInset.left, bottom: flowLayout.sectionInset.bottom, right: flowLayout.sectionInset.right)
    }
    // getting the number of item in the current section
    let totalItemCount = CGFloat(collectionView.numberOfItems(inSection: section))
    // setting number of item in a row to the max number of items that can fit in a row without spacing or to the number of items in the section if less than the max
    var itemCountInRow = totalItemCount < (collectionWidth / itemWidth).rounded(.towardZero) ? totalItemCount : (collectionWidth / itemWidth).rounded(.towardZero)
    // how many max row can we have
    var countOfRow = totalItemCount < (collectionHeight / itemHeight).rounded(.towardZero) ? totalItemCount : (collectionHeight / itemHeight).rounded(.towardZero)
    // calculating the total width of row by multiplying the number of items in the row by the width of item and adding the spacing multiplied by the number of item minus one
    var totalWidthOfRow:CGFloat {
        get{
            return (itemWidth * itemCountInRow) + (interSpacing * (itemCountInRow - 1))
        }
    }
    // calculating the total height of row by multiplying the number of row by the height of item and adding the spacing multiplied by the number of row minus one
    var totalHeightOfRow:CGFloat {
        get{
            return (itemHeight * countOfRow) + (lineSpacing * (countOfRow - 1))
        }
    }
    // first we set the inset to the default
    var edgeInsetLeft = flowLayout.sectionInset.left
    var edgeInsetTop = flowLayout.sectionInset.top

    if direction == .vertical {
        // while the width of row with original margin is greater than the width of the collection we drop one item until it fits
        while totalWidthOfRow > collectionWidth || ((collectionWidth - totalWidthOfRow) / 2) < flowLayout.sectionInset.left {
            // droping an item to fit in the row
            itemCountInRow -= 1
        }
        // calculating the number of rows in collectionView by dividing the number of items by the number of items in a row
        countOfRow = (totalItemCount / (itemCountInRow)).rounded(.up)
    } else {
        itemCountInRow = (totalItemCount / countOfRow).rounded(.up)
        // while the height of row with original marginis greater than the height of the collection we drop one row until it fits
        while totalHeightOfRow >= collectionHeight  || ((collectionHeight - totalHeightOfRow) / 2) < flowLayout.sectionInset.top  {
            // droping an item to fit in the row
            countOfRow -= 1
        }
    }
    edgeInsetLeft = max(flowLayout.sectionInset.left, (collectionWidth - totalWidthOfRow) / 2)
    edgeInsetTop = max(flowLayout.sectionInset.top, (collectionHeight - totalHeightOfRow) / 2)
    // we don't specially need insets where the items are overflowing
    let edgeInsetRight = direction == .vertical ? edgeInsetLeft : flowLayout.sectionInset.right
    let edgeInsetBottom = direction == .horizontal ? edgeInsetTop : flowLayout.sectionInset.bottom
    // returning the UIEdgeInsets
    return UIEdgeInsets(top: edgeInsetTop, left: edgeInsetLeft, bottom: edgeInsetBottom, right: edgeInsetRight)
}

Надеюсь, это кому-то поможет - он центрирует раздел, а не элементы внутри раздела, для большего мы должны создать подкласс UICollectionViewFlowLayoutили UICollectionViewLayoutкак пример мозаики от Apple.

doroboneko
источник
-4

Я думаю, вам нужно центрировать ячейку, поэтому вместо использования collectionView я, как UITableView, будет очень полезен. Просто используйте UIViewController и поместите два UIViews спереди и сзади и поместите UITableViewпосередине Надеюсь, это поможет

Миша
источник
Я добавил картинку, чтобы показать, что именно мне нужно, спасибо!
RaptoX