如何居中alignmentUICollectionView的单元格?

我目前使用用户界面网格UICollectionView ,它工作正常。 不过,我想要启用水平滚动。 网格每页支持8个项目,当项目的总数是4时,这是项目应该如何安排水平滚动方向启用:

 0 0 xx 0 0 xx 

这里0 – >收集项目和x – >空单元格

有没有办法让他们中心alignment如下:

 x 0 0 x x 0 0 x 

这样的内容看起来更干净?

下面的安排也可能是我期待的解决scheme。

 0 0 0 0 xxxx 

我认为你可以通过执行这样的事情来实现单线外观:

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

你将不得不玩弄这个数字来弄清楚如何强制内容成一条线。 第一个0是顶边的参数,如果要将内容垂直居中在屏幕上,则可以调整该参数。

这里的顶级解决scheme对我来说并不适用,所以我想出了这个适用于任何水平滚动收集视图的stream布局和只有一个部分:

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

对于那些正在寻找中心alignmentdynamic宽度 collectionview单元的解决scheme,我最终修改了Angel的左alignment版本的答案,为UICollectionViewFlowLayout创build了一个中心alignment的子类。

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

CenterAlignedCollectionViewFlowLayout

dynamic计算插入很容易,这段代码将始终居中您的单元格:

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

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

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

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

与其他答案类似,这是一个dynamic的答案,应该适用于静态大小的单元格。 我做的一个修改是我把填充两边。 如果我没有这样做,我遇到了问题。


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

迅速

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

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

 UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout; flowLayout.minimumInteritemSpacing = 20.0; flowLayout.minimumLineSpacing = 20.0; 

我的解决scheme为静态大小的集合视图单元格,需要填充左侧和右侧,

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

我的应用程序中有一个标签栏,它使用UICollectionViewUICollectionViewFlowLayout ,一行单元格居中alignment。

为了得到正确的缩进,从UICollectionView的宽度中减去所有单元格的总宽度(包括空格),然后除以二。

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

问题是这个function –

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

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

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

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

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

 KTCenterFlowLayout *layout = [[KTCenterFlowLayout alloc] init]; [self.collectionView setCollectionViewLayout:layout]; 

不知道这是否是Xcode 5中的新function,但现在可以通过Interface Builder打开Size Inspector并在其中设置插入。 这将防止你不得不编写自定义代码来为你做这个,并应该大大提高你find正确的偏移的速度。

另一种方法是设置collectionView.center.x ,如下所示:

 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let collectionView = collectionView, layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { collectionView.center.x = view.center.x + layout.sectionInset.right / 2.0 } } 

在这种情况下,只有尊重适合我的权利embedded。

根据user3676011的答案,我可以build议更详细的一个小的更正。 这个解决scheme在Swift 2.0上效果很好。

 enum CollectionViewContentPosition { case Left case Center } var collectionViewContentPosition: CollectionViewContentPosition = .Left func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets { if collectionViewContentPosition == .Left { return UIEdgeInsetsZero } // Center collectionView content let itemsCount: CGFloat = CGFloat(collectionView.numberOfItemsInSection(section)) let collectionViewWidth: CGFloat = collectionView.bounds.width let itemWidth: CGFloat = 40.0 let itemsMargin: CGFloat = 10.0 let edgeInsets = (collectionViewWidth - (itemsCount * itemWidth) - ((itemsCount-1) * itemsMargin)) / 2 return UIEdgeInsetsMake(0, edgeInsets, 0, 0) } 

有一个问题

(CGFloat(elements.count)* 10))

在哪里应该增加-1提到。

这是我如何获得与固定的Interitem间距中心alignment的集合视图

 #define maxInteritemSpacing 6 #define minLineSpacing 3 @interface MyFlowLayout()<UICollectionViewDelegateFlowLayout> @end @implementation MyFlowLayout - (instancetype)init { self = [super init]; if (self) { self.minimumInteritemSpacing = 3; self.minimumLineSpacing = minLineSpacing; self.scrollDirection = UICollectionViewScrollDirectionVertical; } return self; } - (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect { NSArray *answer = [super layoutAttributesForElementsInRect:rect]; if (answer.count > 0) { NSMutableArray<NSMutableArray<UICollectionViewLayoutAttributes *> *> *rows = [NSMutableArray array]; CGFloat maxY = -1.0f; NSInteger currentRow = 0; //add first item to row and set its maxY [rows addObject:[@[answer[0]] mutableCopy]]; maxY = CGRectGetMaxY(((UICollectionViewLayoutAttributes *)answer[0]).frame); for(int i = 1; i < [answer count]; ++i) { //handle maximum spacing UICollectionViewLayoutAttributes *currentLayoutAttributes = answer[i]; UICollectionViewLayoutAttributes *prevLayoutAttributes = answer[i - 1]; NSInteger maximumSpacing = maxInteritemSpacing; NSInteger origin = CGRectGetMaxX(prevLayoutAttributes.frame); if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width) { CGRect frame = currentLayoutAttributes.frame; frame.origin.x = origin + maximumSpacing; currentLayoutAttributes.frame = frame; } //handle center align CGFloat currentY = currentLayoutAttributes.frame.origin.y; if (currentY >= maxY) { [self shiftRowToCenterForCurrentRow:rows[currentRow]]; //next row [rows addObject:[@[currentLayoutAttributes] mutableCopy]]; currentRow++; } else { //same row [rows[currentRow] addObject:currentLayoutAttributes]; } maxY = MAX(CGRectGetMaxY(currentLayoutAttributes.frame), maxY); } //mark sure to shift 1 row items if (currentRow == 0) { [self shiftRowToCenterForCurrentRow:rows[currentRow]]; } } return answer; } - (void)shiftRowToCenterForCurrentRow:(NSMutableArray<UICollectionViewLayoutAttributes *> *)currentRow { //shift row to center CGFloat endingX = CGRectGetMaxX(currentRow.lastObject.frame); CGFloat shiftX = (self.collectionViewContentSize.width - endingX) / 2.f; for (UICollectionViewLayoutAttributes *attr in currentRow) { CGRect shiftedFrame = attr.frame; shiftedFrame.origin.x += shiftX; attr.frame = shiftedFrame; } } @end 

使用Swift 3.0的kgaidis的Objective C工作版本:

 let flow = UICollectionViewFlowLayout() func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { let numberOfItems = collectionView.numberOfItems(inSection: 0) let combinedItemWidth:CGFloat = (CGFloat(numberOfItems) * flow.itemSize.width) + ((CGFloat(numberOfItems) - 1) * flow.minimumInteritemSpacing) let padding = (collectionView.frame.size.width - combinedItemWidth) / 2 return UIEdgeInsetsMake(0, padding, 0, padding) } 

这是你如何做到这一点,它工作正常

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

这是我的解决scheme,有几个假设:

  • 只有一个部分
  • 左右插槽是平等的
  • 细胞高度是一样的

随意调整,以满足您的需求。

可变单元格宽度的居中布局:

 protocol HACenteredLayoutDelegate: UICollectionViewDataSource { func getCollectionView() -> UICollectionView func sizeOfCell(at index: IndexPath) -> CGSize func contentInsets() -> UIEdgeInsets } class HACenteredLayout: UICollectionViewFlowLayout { weak var delegate: HACenteredLayoutDelegate? private var cache = [UICollectionViewLayoutAttributes]() private var contentSize = CGSize.zero override var collectionViewContentSize: CGSize { return self.contentSize } required init(delegate: HACenteredLayoutDelegate) { self.delegate = delegate super.init() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override func invalidateLayout() { cache.removeAll() super.invalidateLayout() } override func prepare() { if cache.isEmpty && self.delegate != nil && self.delegate!.collectionView(self.delegate!.getCollectionView(), numberOfItemsInSection: 0) > 0 { let insets = self.delegate?.contentInsets() ?? UIEdgeInsets.zero var rows: [(width: CGFloat, count: Int)] = [(0, 0)] let viewWidth: CGFloat = UIScreen.main.bounds.width var y = insets.top var unmodifiedIndexes = [IndexPath]() for itemNumber in 0 ..< self.delegate!.collectionView(self.delegate!.getCollectionView(), numberOfItemsInSection: 0) { let indexPath = IndexPath(item: itemNumber, section: 0) let cellSize = self.delegate!.sizeOfCell(at: indexPath) let potentialRowWidth = rows.last!.width + (rows.last!.count > 0 ? self.minimumInteritemSpacing : 0) + cellSize.width + insets.right + insets.left if potentialRowWidth > viewWidth { let leftOverSpace = max((viewWidth - rows[rows.count - 1].width)/2, insets.left) for i in unmodifiedIndexes { self.cache[i.item].frame.origin.x += leftOverSpace } unmodifiedIndexes = [] rows.append((0, 0)) y += cellSize.height + self.minimumLineSpacing } unmodifiedIndexes.append(indexPath) let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath) rows[rows.count - 1].count += 1 rows[rows.count - 1].width += rows[rows.count - 1].count > 1 ? self.minimumInteritemSpacing : 0 attribute.frame = CGRect(x: rows[rows.count - 1].width, y: y, width: cellSize.width, height: cellSize.height) rows[rows.count - 1].width += cellSize.width cache.append(attribute) } let leftOverSpace = max((viewWidth - rows[rows.count - 1].width)/2, insets.left) for i in unmodifiedIndexes { self.cache[i.item].frame.origin.x += leftOverSpace } self.contentSize = CGSize(width: viewWidth, height: y + self.delegate!.sizeOfCell(at: IndexPath(item: 0, section: 0)).height + insets.bottom) } } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var layoutAttributes = [UICollectionViewLayoutAttributes]() for attributes in cache { if attributes.frame.intersects(rect) { layoutAttributes.append(attributes) } } return layoutAttributes } override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { if indexPath.item < self.cache.count { return self.cache[indexPath.item] } return nil } } 

结果:

在这里输入图像说明