如何水平居中UICollectionView细胞?

时间:2015-12-14 12:57:24

标签: ios swift

我做了一些研究,但是我找不到任何关于如何在UICollectionView中水平居中单元格的代码示例。

而不是像 X00 这样的第一个单元格,我希望它像 0X0 。有没有办法实现这个目标?

编辑:

想象我想要的东西:

enter image description here

当CollectionView中只有一个元素时,我需要它看起来像版本B.当我有多个元素时,它应该像版本A但有更多元素。

当我只有1个元素时,它看起来像版本A,我想知道如何让它看起来像B。

感谢您的帮助!

26 个答案:

答案 0 :(得分:172)

使用库不是一个好主意,如果你的目的只是这个,那就是居中对齐。

最好你可以在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)
}

答案 1 :(得分:41)

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

答案 2 :(得分:21)

尝试使用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

答案 3 :(得分:18)

我为此使用KTCenterFlowLayout,效果很好。它是UICollectionViewFlowLayout的自定义子类,可以根据需要使单元格居中。 (注意:通过发布一些代码来解决这个问题并不是一件小事,这就是我链接到GitHub项目的原因!)

答案 4 :(得分:13)

Darshan Patel's answer的目标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;
}

答案 5 :(得分:3)

您可以使用此扩展名(Swift 4)。

如果您collectionViewlayout.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)
    }
}

希望对您有帮助!

答案 6 :(得分:2)

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)
    }

 }

答案 7 :(得分:2)

对于只想添加填充顶部,左侧,底部,右侧)的人:

添加协议UICollectionViewDelegateFlowLayout

此示例显示了40左右的填充。

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {        
    return UIEdgeInsetsMake(0, 40, 0, 40)
}

答案 8 :(得分:1)

可接受的答案是正确的答案,但是如果您的totalCellWidth小于CollectionView的{​​{1}},但是为了防止这种情况,您可以执行以下操作。

width

答案 9 :(得分:1)

这是 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协议。

答案 10 :(得分:1)

如果每个组只能容纳一个单元格,则leading:中的trailing:.flexible(0)将使该单元格水平居中:

item.edgeSpacing = NSCollectionLayoutEdgeSpacing(
    leading: .flexible(0), top: nil,                                                     
    trailing: .flexible(0), bottom: nil
)

答案 11 :(得分:1)

如果您使用 Darshan Patel's 答案,您还可以在 UICollectionViewFlowLayout 子类中执行计算。

class Layout: UICollectionViewFlowLayout {

    override init() {
        super.init()
        scrollDirection = .horizontal
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError()
    }

    override func prepare() {
        super.prepare()

        guard let collectionView = collectionView,
            collectionView.numberOfSections != 0 else { return }
    
        minimumInteritemSpacing = 5
        itemSize = CGSize(width: 30, height: 30)

        sectionInsetReference = .fromSafeArea

        let numberOfItems = collectionView.numberOfItems(inSection: 0)

        let totalCellWidth = itemSize.width * CGFloat(numberOfItems)
        let totalSpacingWidth = minimumInteritemSpacing * CGFloat(numberOfItems - 1)

        let leftInset = (collectionView.bounds.maxX - CGFloat(totalCellWidth + totalSpacingWidth)) / 2

        sectionInset = UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: leftInset)
    }
}

答案 12 :(得分: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协议

答案 13 :(得分:1)

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)
    }
}

答案 14 :(得分: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)
}

答案 15 :(得分:0)

当数据更多时,上述答案有一个改进; 使用以下代码:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
      var totalCellsWidth:CGFloat = 0
        for index in 0..<numberOfCells{
            let width = viewModel.getCellWidth(at: index) // get cell width is to dynamically calculate the width of the cell according to the text
            totalCellsWidth += width
        }
        let cellSpacing:CGFloat = 8.0
        let totalSpacingWidth = cellSpacing * CGFloat(viewModel.teamsCount - 1)
       
        var leftInset = (collectionView.frame.width - (totalCellsWidth + totalSpacingWidth)) / 2
        if leftInset < 0{
            leftInset = 0
        }
        let rightInset = leftInset
        return UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: rightInset)
}

答案 16 :(得分:0)

我在项目中使用了该代码。它使用section的插入在.horizontal.vertical的水平和垂直方向上将collectionView居中。如果设置,则遵守节的间距和原始插图。 在委托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)
}

希望这对某人有帮助-它使部分而不是部分内部居中,更多的是我们必须将UICollectionViewFlowLayoutUICollectionViewLayout子类化为Apple的镶嵌示例。

答案 17 :(得分:0)

您必须在此处定义自定义 UICollectionViewFlowLayout,如下所示。

class CenterAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let superArray = super.layoutAttributesForElements(in: rect) else {  return nil  }
        guard let attributes = NSArray(array: superArray, 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
    }
}

然后在相关的viewController的viewDidLoad或者相关的方法中添加以下内容,

let layout = CenterAlignedCollectionViewFlowLayout()
layout.estimatedItemSize = CGSize(width: 200, height: 40)
self.collectionRateCustomer.collectionViewLayout = layout

不要忘记继承UICollectionViewDelegateFlowLayout

参考文献,equaleyes.comAlex Koshy's answer

答案 18 :(得分:0)

最简单的方法是在情节提要中或使用代码layout.estimatedItemSize = CGSize.zero将集合视图估计大小设置为“无”

答案 19 :(得分: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

答案 20 :(得分:0)

我从这里尝试了许多解决方案,但最终,最简单的解决方案效果最好

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: YOUR HEIGHT)
    }

p.s。界面生成器中没有插入

答案 21 :(得分:0)

用于页面布局的通用解决方案,如果页面宽度小于宽度,则居中,如果页面宽度大于页面,则向左对齐

- (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;
}

快速版本(从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

答案 22 :(得分:0)

我最终在这里采取了完全不同的方法,我认为值得一提。

我在集合视图上设置了一个约束,使其在中心水平对齐。然后,我设置另一个约束来指定宽度。我在保存集合视图的viewController内部创建了一个用于宽度约束的出口 。然后,当更改数据源并更新集合视图时,我将对单元格进行计数并进行(非常相似的)计算以重置宽度。

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

然后,将约束出口的.constant值设置为计算结果,其余部分将由自动布局完成。

这可能与`UICollectionViewDelegateFlowLayout'冲突,但这对我来说创建左对齐的集合视图非常合适。没有委托,它似乎只有在单元格填满视图的大部分时才起作用。

答案 23 :(得分:0)

Swift 4.2 (水平和垂直)。这是粉红豹代码的小升级,非常感谢他!


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 协议

答案 24 :(得分:-1)

对于 swift 5.2

我做了一些调整以满足我的要求

  1. 如果内容没有延伸到屏幕之外,则不滚动
  2. 如果内容没有延伸到屏幕外,则居中
  3. 如果内容确实延伸到屏幕之外,请确保从索引 0 开始并允许滚动,同时从边缘缩进 16(易于调整)
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
        if leftInset < 0 {
            collectionView.isScrollEnabled = true
            return UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
        }
        collectionView.isScrollEnabled = false
        return UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: rightInset)
    }

通过在视图控制器中执行此操作来使用它

extension BaseViewController : UICollectionViewDelegateFlowLayout {

然后你可以打电话

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return centerItemsInCollectionView(cellWidth: 70, numberOfItems: 3, spaceBetweenCell: 25, collectionView: collectionView)
}

请记住,如果您想为单元格设置自己的大小,您需要在故事板中将 collectionView 估计大小设置为无在故事板中将单元格大小设置为自定义强>

然后你可以打电话

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize.init(width:60, height: 60)
}

如果你正在努力获得方法来被调用,那么让你所有的代表都被倾听

extension BaseViewController : UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

显然不要忘记设置您的委托,简单地扩展它们不会成功。

override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.delegate = self
        collectionView.dataSource = self
...

答案 25 :(得分:-3)

我认为你需要居中单元格,所以不要使用collectionView,而是像UITableView一样非常有用。只需使用UIViewController并在前后放置两个UIViews,并在中间放置一个UITableView 希望这有帮助