How to center align the cells of a UICollectionView?

RVN*_*RVN 96 ios uicollectionview

I am currently using UICollectionView for the user interface grid, and it works fine. However, I'd like to be enable horizontal scrolling. The grid supports 8 items per page and when the total number of items are, say 4, this is how the items should be arranged with horizontal scroll direction enabled:

0 0 x x
0 0 x x
Run Code Online (Sandbox Code Playgroud)

Here 0 -> Collection Item and x -> Empty Cells

有没有办法使它们居中对齐,如:

x 0 0 x
x 0 0 x
Run Code Online (Sandbox Code Playgroud)

这样内容看起来更干净?

以下安排也可能是我期待的解决方案.

0 0 0 0
x x x x
Run Code Online (Sandbox Code Playgroud)

rde*_*mar 79

我认为你可以通过实现这样的方式来实现单行外观:

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 100, 0, 0);
}
Run Code Online (Sandbox Code Playgroud)

您将不得不使用该数字来弄清楚如何将内容强制为一行.第一个0是顶边参数,如果要在屏幕中垂直居中,也可以调整那个参数.

  • 要动态计算insets:CGFloat leftInset =(collectionView.frame.size.width-40*[collectionView numberOfItemsInSection:section])/ 2; 40 =细胞的大小 (7认同)
  • 它实际应该是`返回UIEdgeInsetsMake(0,100,0,100);`.如果有额外的空间,集合视图将扩展临时间距,因此您需要确保LeftInset + CellWidth*Ncells + CellSpacing*(Ncells-1)+ RightInset = CollectionViewWidth (5认同)

Ale*_*shy 77

对于那些寻找一个解决方案中心对齐,动态宽的CollectionView细胞,因为我是,我最终修改天使的回答为左对齐版本创建一个中心对齐的子类UICollectionViewFlowLayout.

CenterAlignedCollectionViewFlowLayout

// NOTE: Doesn't work for horizontal layout!
class CenterAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let superAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
        // Copy each item to prevent "UICollectionViewFlowLayout has cached frame mismatch" warning
        guard let attributes = NSArray(array: superAttributes, copyItems: true) as? [UICollectionViewLayoutAttributes] else { return nil }

        // Constants
        let leftPadding: CGFloat = 8
        let interItemSpacing = minimumInteritemSpacing

        // Tracking values
        var leftMargin: CGFloat = leftPadding // Modified to determine origin.x for each item
        var maxY: CGFloat = -1.0 // Modified to determine origin.y for each item
        var rowSizes: [[CGFloat]] = [] // Tracks the starting and ending x-values for the first and last item in the row
        var currentRow: Int = 0 // Tracks the current row
        attributes.forEach { layoutAttribute in

            // Each layoutAttribute represents its own item
            if layoutAttribute.frame.origin.y >= maxY {

                // This layoutAttribute represents the left-most item in the row
                leftMargin = leftPadding

                // Register its origin.x in rowSizes for use later
                if rowSizes.count == 0 {
                    // Add to first row
                    rowSizes = [[leftMargin, 0]]
                } else {
                    // Append a new row
                    rowSizes.append([leftMargin, 0])
                    currentRow += 1
                }
            }

            layoutAttribute.frame.origin.x = leftMargin

            leftMargin += layoutAttribute.frame.width + interItemSpacing
            maxY = max(layoutAttribute.frame.maxY, maxY)

            // Add right-most x value for last item in the row
            rowSizes[currentRow][1] = leftMargin - interItemSpacing
        }

        // At this point, all cells are left aligned
        // Reset tracking values and add extra left padding to center align entire row
        leftMargin = leftPadding
        maxY = -1.0
        currentRow = 0
        attributes.forEach { layoutAttribute in

            // Each layoutAttribute is its own item
            if layoutAttribute.frame.origin.y >= maxY {

                // This layoutAttribute represents the left-most item in the row
                leftMargin = leftPadding

                // Need to bump it up by an appended margin
                let rowWidth = rowSizes[currentRow][1] - rowSizes[currentRow][0] // last.x - first.x
                let appendedMargin = (collectionView!.frame.width - leftPadding  - rowWidth - leftPadding) / 2
                leftMargin += appendedMargin

                currentRow += 1
            }

            layoutAttribute.frame.origin.x = leftMargin

            leftMargin += layoutAttribute.frame.width + interItemSpacing
            maxY = max(layoutAttribute.frame.maxY, maxY)
        }

        return attributes
    }
}
Run Code Online (Sandbox Code Playgroud)

CenterAlignedCollectionViewFlowLayout

  • 这给出了以下警告,虽然这很可能发生,因为流布局子类xxxx.CustomCollectionViewFlowLayout正在修改UICollectionViewFlowLayout返回的属性而不复制它们为了更正,我使用了来自[http://stackoverflow.com/a/36315664/1067951的代码]`guard let superArray = super.layoutAttributesForElementsInRect(rect)else {return nil} guard let attributes = NSArray(array:superArray,copyItems:true)as?[UICollectionViewLayoutAttributes] else {return nil}` (3认同)

Sie*_*ult 56

这里的顶级解决方案对我来说不起作用,所以我提出了这个应该适用于任何水平滚动集合视图与流布局和只有一个部分:

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
    {
    // Add inset to the collection view if there are not enough cells to fill the width.
    CGFloat cellSpacing = ((UICollectionViewFlowLayout *) collectionViewLayout).minimumLineSpacing;
    CGFloat cellWidth = ((UICollectionViewFlowLayout *) collectionViewLayout).itemSize.width;
    NSInteger cellCount = [collectionView numberOfItemsInSection:section];
    CGFloat inset = (collectionView.bounds.size.width - (cellCount * cellWidth) - ((cellCount - 1)*cellSpacing)) * 0.5;
    inset = MAX(inset, 0.0);
    return UIEdgeInsetsMake(0.0, inset, 0.0, 0.0);
    }
Run Code Online (Sandbox Code Playgroud)


mie*_*tus 34

动态计算insets很容易,这段代码总是以你的单元为中心:

NSInteger const SMEPGiPadViewControllerCellWidth = 332;

...

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{

    NSInteger numberOfCells = self.view.frame.size.width / SMEPGiPadViewControllerCellWidth;
    NSInteger edgeInsets = (self.view.frame.size.width - (numberOfCells * SMEPGiPadViewControllerCellWidth)) / (numberOfCells + 1);

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

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    [self.collectionView.collectionViewLayout invalidateLayout];
}
Run Code Online (Sandbox Code Playgroud)

  • 你有什么修改? (4认同)
  • @Siriss而不是SMEPGiPadViewControllerCellWidth你可以使用collectionViewLayout.itemSize.width,这可能是他修改的 (4认同)

kga*_*dis 20

与其他答案类似,这是一个动态答案,应该适用于静态大小的单元格.我做的一个修改是我把垫子放在两边.如果我不这样做,我遇到了问题.


Objective-C的

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {

    NSInteger numberOfItems = [collectionView numberOfItemsInSection:section];
    CGFloat combinedItemWidth = (numberOfItems * collectionViewLayout.itemSize.width) + ((numberOfItems - 1) * collectionViewLayout.minimumInteritemSpacing);
    CGFloat padding = (collectionView.frame.size.width - combinedItemWidth) / 2;

    return UIEdgeInsetsMake(0, padding, 0, padding);
}
Run Code Online (Sandbox Code Playgroud)

迅速

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
    let numberOfItems = CGFloat(collectionView.numberOfItems(inSection: section))
    let combinedItemWidth = (numberOfItems * flowLayout.itemSize.width) + ((numberOfItems - 1)  * flowLayout.minimumInteritemSpacing)
    let padding = (collectionView.frame.width - combinedItemWidth) / 2
    return UIEdgeInsets(top: 0, left: padding, bottom: 0, right: padding)
}
Run Code Online (Sandbox Code Playgroud)

另外,如果你仍然遇到问题,请确保您同时设置minimumInteritemSpacingminimumLineSpacing以相同的值,因为这些值似乎是相互关联的.

UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
flowLayout.minimumInteritemSpacing = 20.0;
flowLayout.minimumLineSpacing = 20.0;
Run Code Online (Sandbox Code Playgroud)


ber*_*ert 19

把它放到你的集合视图委托中.它考虑了比其他答案更多的基本流程布局设置,因此更通用.

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    NSInteger cellCount = [collectionView.dataSource collectionView:collectionView numberOfItemsInSection:section];
    if( cellCount >0 )
    {
        CGFloat cellWidth = ((UICollectionViewFlowLayout*)collectionViewLayout).itemSize.width+((UICollectionViewFlowLayout*)collectionViewLayout).minimumInteritemSpacing;
        CGFloat totalCellWidth = cellWidth*cellCount + spacing*(cellCount-1);
        CGFloat contentWidth = collectionView.frame.size.width-collectionView.contentInset.left-collectionView.contentInset.right;
        if( totalCellWidth<contentWidth )
        {
            CGFloat padding = (contentWidth - totalCellWidth) / 2.0;
            return UIEdgeInsetsMake(0, padding, 0, padding);
        }
    }
    return UIEdgeInsetsZero;
}
Run Code Online (Sandbox Code Playgroud)

swift版本(感谢g0ld2k):

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

    // Translated from Objective-C version at: http://stackoverflow.com/a/27656363/309736

        let cellCount = CGFloat(viewModel.getNumOfItemsInSection(0))

        if cellCount > 0 {
            let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
            let cellWidth = flowLayout.itemSize.width + flowLayout.minimumInteritemSpacing
            let totalCellWidth = cellWidth*cellCount + spacing*(cellCount-1)
            let contentWidth = collectionView.frame.size.width - collectionView.contentInset.left - collectionView.contentInset.right

            if (totalCellWidth < contentWidth) {
                let padding = (contentWidth - totalCellWidth) / 2.0
                return UIEdgeInsetsMake(0, padding, 0, padding)
            }
        }

        return UIEdgeInsetsZero
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 实际上在你的UICollectionViewDelegateFlowLayout中.谢谢,这适用于一行或多行的单元格.其他一些答案则没有. (3认同)
  • 将此转换为Swift,效果很好!可以使用Swift版本[这里](https://gist.github.com/g0ld2k/83f2e7c39ff0bc6b3299). (2认同)

小智 11

我对静态大小的集合视图单元的解决方案需要在左侧和右侧填充 -

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

    let flowLayout = (collectionViewLayout as! UICollectionViewFlowLayout)
    let cellSpacing = flowLayout.minimumInteritemSpacing
    let cellWidth = flowLayout.itemSize.width
    let cellCount = CGFloat(collectionView.numberOfItemsInSection(section))

    let collectionViewWidth = collectionView.bounds.size.width

    let totalCellWidth = cellCount * cellWidth
    let totalCellSpacing = cellSpacing * (cellCount - 1)

    let totalCellsWidth = totalCellWidth + totalCellSpacing

    let edgeInsets = (collectionViewWidth - totalCellsWidth) / 2.0

    return edgeInsets > 0 ? UIEdgeInsetsMake(0, edgeInsets, 0, edgeInsets) : UIEdgeInsetsMake(0, cellSpacing, 0, cellSpacing)
}
Run Code Online (Sandbox Code Playgroud)


Siô*_*iôn 8

我的应用程序中有一个使用UICollectionView&的标签栏UICollectionViewFlowLayout,其中一行单元格居中对齐.

要获得正确的缩进,请从宽度中减去所有单元格的总宽度(包括间距)UICollectionView,然后除以2.

[........Collection View.........]
[..Cell..][..Cell..]
                   [____indent___] / 2

=

[_____][..Cell..][..Cell..][_____]
Run Code Online (Sandbox Code Playgroud)

问题是这个功能 -

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;

之前被称为......

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;

...所以你不能迭代你的单元格来确定总宽度.

相反,你需要再次计算每个单元格的宽度,在我的情况下,我使用,[NSString sizeWithFont: ... ]因为我的单元格宽度由UILabel本身决定.

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    CGFloat rightEdge = 0;
    CGFloat interItemSpacing = [(UICollectionViewFlowLayout*)collectionViewLayout minimumInteritemSpacing];

    for(NSString * tag in _tags)
        rightEdge += [tag sizeWithFont:[UIFont systemFontOfSize:14]].width+interItemSpacing;

    // To center the inter spacing too
    rightEdge -= interSpacing/2;

    // Calculate the inset
    CGFloat inset = collectionView.frame.size.width-rightEdge;

    // Only center align if the inset is greater than 0
    // That means that the total width of the cells is less than the width of the collection view and need to be aligned to the center.
    // Otherwise let them align left with no indent.

    if(inset > 0)
        return UIEdgeInsetsMake(0, inset/2, 0, 0);
    else
        return UIEdgeInsetsMake(0, 0, 0, 0);
}
Run Code Online (Sandbox Code Playgroud)


Dal*_*ord 8

在Swift中,以下内容将通过在每个单元格的两侧应用正确数量的填充来均匀分布单元格.当然,您需要先了解/设置单元格宽度.

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

        var cellWidth : CGFloat = 110;

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

        return UIEdgeInsetsMake(0, edgeInsets, 60.0, edgeInsets);
}
Run Code Online (Sandbox Code Playgroud)


小智 8

Swift 2.0对我来说很好用!

 func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
        let edgeInsets = (screenWight - (CGFloat(elements.count) * 50) - (CGFloat(elements.count) * 10)) / 2
        return UIEdgeInsetsMake(0, edgeInsets, 0, 0);
    }
Run Code Online (Sandbox Code Playgroud)

其中: screenWight:基本上是我的集合的宽度(全屏宽度) - 我做了常量:让screenWight:CGFloat = UIScreen.mainScreen().bounds.width因为self.view.bounds每次显示600 - 来自SizeClasses 元素 -单元格数组 50 - 我的手动单元格宽度10 - 单元格 之间的距离


小智 6

您可以像这样使用https://github.com/keighl/KTCenterFlowLayout:

KTCenterFlowLayout *layout = [[KTCenterFlowLayout alloc] init];

[self.collectionView setCollectionViewLayout:layout];
Run Code Online (Sandbox Code Playgroud)