如何在 UICollectionView 中居中行?
Posted
技术标签:
【中文标题】如何在 UICollectionView 中居中行?【英文标题】:How can I center rows in UICollectionView? 【发布时间】:2013-05-14 13:12:56 【问题描述】:我有一个带有随机单元格的UICollectionView
。有什么方法可以让我居中行吗?
默认情况下是这样的:
[ x x x x x x ]
[ x x x x x x ]
[ x x ]
这是所需的布局:
[ x x x x x ]
[ x x x x x ]
[ x x ]
【问题讨论】:
【参考方案1】:我不得不做这样的事情,但需要一个部分中的所有单元格。将UICollectionViewFlowLayout
扩展到中心单元非常简单。我做了一个豆荚:
https://github.com/keighl/KTCenterFlowLayout
【讨论】:
要让 KTCenterFlowLayout 工作,我必须在代码中明确设置单元格大小,即使它已经通过情节提要设置:let layout = KTCenterFlowLayout(); layout.itemSize = CGSizeMake(85, 85)
您也可以在界面生成器中将其设置为集合视图的布局。谢谢凯尔,这太棒了!
非常感谢,凯尔!为我节省了几个小时!
我的收藏视图想要完全相同的东西。如何使用 Objective-C 使用此布局?
我制作了它的 Swift 4 版本:***.com/a/47815971/4386668。谢谢@Kyle Truscott!【参考方案2】:
先介绍一下背景 - UICollectionView
与 UICollectionViewLayout
组合在一起,这决定了单元格在视图中的放置方式。这意味着集合视图非常灵活(您可以使用它创建几乎任何布局),但也意味着修改布局可能会有些混乱。
创建一个全新的布局类很复杂,因此您想尝试修改默认布局 (UICollectionViewFlowLayout
) 以获得居中对齐。为了使其更简单,您可能希望避免对流布局本身进行子类化。
这是一种方法(它可能不是最好的方法,但它是我能想到的第一种方法)- 将单元格分成两部分,如下所示:
[ x x x x x ] <-- Section 1
[ x x x x x ] <-- Section 1
[ x x ] <-- Section 2
这应该相当简单,只要您知道滚动视图的宽度以及每行可以容纳的单元格数。
然后,使用collectionView:layout:insetForSectionAtIndex:
委托方法设置第二部分的边距,使其看起来垂直居中。完成此操作后,您只需确保重新计算适当的部分分割/插图,以便支持纵向和横向。
这里有一个有点类似的问题 - How to center align the cells of a UICollectionView? - 详细介绍了插入方法,尽管它并没有完全尝试做和你一样的事情。
【讨论】:
好吧,我不知道这是不是最好的方法,但它确实有效。这就是我所需要的。谢谢。 我认为它可能是最简单的,就行数/不子类化布局而言。如果您最终要做更多的布局工作,您可能需要考虑这种方法。 你也可以继承UICollectionViewFlowLayout
而不是UICollectionViewLayout
。布局仍然是基于流的,只是进行了微小的更改,这样做可以为您完成大部分繁重的工作,您只需自定义所需的部分即可。
如果我有不同宽度的单元格,这种方法似乎使它更加复杂。您对我如何使用具有不同宽度的单元格来实现这一点有任何建议吗?谢谢【参考方案3】:
在 Swift 4.1 和 ios 11 中,根据您的需要,您可以选择以下两个完整实现中的一个来解决您的问题。
#1。以固定大小居中UICollectionViewCell
s
下面的实现展示了如何使用UICollectionViewLayout
的layoutAttributesForElements(in:)
和UICollectionViewFlowLayout
的itemSize
来使UICollectionView
的单元格居中:
CollectionViewController.swift
import UIKit
class CollectionViewController: UICollectionViewController
let columnLayout = FlowLayout(
itemSize: CGSize(width: 140, height: 140),
minimumInteritemSpacing: 10,
minimumLineSpacing: 10,
sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
)
override func viewDidLoad()
super.viewDidLoad()
title = "Center cells"
collectionView?.collectionViewLayout = columnLayout
collectionView?.contentInsetAdjustmentBehavior = .always
collectionView?.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return 7
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
return cell
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
collectionView?.collectionViewLayout.invalidateLayout()
super.viewWillTransition(to: size, with: coordinator)
FlowLayout.swift
import UIKit
class FlowLayout: UICollectionViewFlowLayout
required init(itemSize: CGSize, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero)
super.init()
self.itemSize = itemSize
self.minimumInteritemSpacing = minimumInteritemSpacing
self.minimumLineSpacing = minimumLineSpacing
self.sectionInset = sectionInset
sectionInsetReference = .fromSafeArea
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
let layoutAttributes = super.layoutAttributesForElements(in: rect)!.map $0.copy() as! UICollectionViewLayoutAttributes
guard scrollDirection == .vertical else return layoutAttributes
// Filter attributes to compute only cell attributes
let cellAttributes = layoutAttributes.filter( $0.representedElementCategory == .cell )
// Group cell attributes by row (cells with same vertical center) and loop on those groups
for (_, attributes) in Dictionary(grouping: cellAttributes, by: ($0.center.y / 10).rounded(.up) * 10 )
// Get the total width of the cells on the same row
let cellsTotalWidth = attributes.reduce(CGFloat(0)) (partialWidth, attribute) -> CGFloat in
partialWidth + attribute.size.width
// Calculate the initial left inset
let totalInset = collectionView!.safeAreaLayoutGuide.layoutFrame.width - cellsTotalWidth - sectionInset.left - sectionInset.right - minimumInteritemSpacing * CGFloat(attributes.count - 1)
var leftInset = (totalInset / 2 * 10).rounded(.down) / 10 + sectionInset.left
// Loop on cells to adjust each cell's origin and prepare leftInset for the next cell
for attribute in attributes
attribute.frame.origin.x = leftInset
leftInset = attribute.frame.maxX + minimumInteritemSpacing
return layoutAttributes
CollectionViewCell.swift
import UIKit
class CollectionViewCell: UICollectionViewCell
override init(frame: CGRect)
super.init(frame: frame)
contentView.backgroundColor = .cyan
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
预期结果:
#2。中心自动调整大小UICollectionViewCell
s
下面的实现展示了如何使用UICollectionViewLayout
的layoutAttributesForElements(in:)
、UICollectionViewFlowLayout
的estimatedItemSize
和UILabel
的preferredMaxLayoutWidth
来使UICollectionView
的单元居中:
CollectionViewController.swift
import UIKit
class CollectionViewController: UICollectionViewController
let array = ["1", "1 2", "1 2 3 4 5 6 7 8", "1 2 3 4 5 6 7 8 9 10 11", "1 2 3", "1 2 3 4", "1 2 3 4 5 6", "1 2 3 4 5 6 7 8 9 10", "1 2 3 4", "1 2 3 4 5 6 7", "1 2 3 4 5 6 7 8 9", "1", "1 2 3 4 5", "1", "1 2 3 4 5 6"]
let columnLayout = FlowLayout(
minimumInteritemSpacing: 10,
minimumLineSpacing: 10,
sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
)
override func viewDidLoad()
super.viewDidLoad()
title = "Center cells"
collectionView?.collectionViewLayout = columnLayout
collectionView?.contentInsetAdjustmentBehavior = .always
collectionView?.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return array.count
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
cell.label.text = array[indexPath.row]
return cell
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
collectionView?.collectionViewLayout.invalidateLayout()
super.viewWillTransition(to: size, with: coordinator)
FlowLayout.swift
import UIKit
class FlowLayout: UICollectionViewFlowLayout
required init(minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero)
super.init()
estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
self.minimumInteritemSpacing = minimumInteritemSpacing
self.minimumLineSpacing = minimumLineSpacing
self.sectionInset = sectionInset
sectionInsetReference = .fromSafeArea
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
let layoutAttributes = super.layoutAttributesForElements(in: rect)!.map $0.copy() as! UICollectionViewLayoutAttributes
guard scrollDirection == .vertical else return layoutAttributes
// Filter attributes to compute only cell attributes
let cellAttributes = layoutAttributes.filter( $0.representedElementCategory == .cell )
// Group cell attributes by row (cells with same vertical center) and loop on those groups
for (_, attributes) in Dictionary(grouping: cellAttributes, by: ($0.center.y / 10).rounded(.up) * 10 )
// Get the total width of the cells on the same row
let cellsTotalWidth = attributes.reduce(CGFloat(0)) (partialWidth, attribute) -> CGFloat in
partialWidth + attribute.size.width
// Calculate the initial left inset
let totalInset = collectionView!.safeAreaLayoutGuide.layoutFrame.width - cellsTotalWidth - sectionInset.left - sectionInset.right - minimumInteritemSpacing * CGFloat(attributes.count - 1)
var leftInset = (totalInset / 2 * 10).rounded(.down) / 10 + sectionInset.left
// Loop on cells to adjust each cell's origin and prepare leftInset for the next cell
for attribute in attributes
attribute.frame.origin.x = leftInset
leftInset = attribute.frame.maxX + minimumInteritemSpacing
return layoutAttributes
CollectionViewCell.swift
import UIKit
class CollectionViewCell: UICollectionViewCell
let label = UILabel()
override init(frame: CGRect)
super.init(frame: frame)
contentView.backgroundColor = .orange
label.preferredMaxLayoutWidth = 120
label.numberOfLines = 0
contentView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
contentView.layoutMarginsGuide.topAnchor.constraint(equalTo: label.topAnchor).isActive = true
contentView.layoutMarginsGuide.leadingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
contentView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor).isActive = true
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
预期结果:
【讨论】:
这是这里最全面的答案,应该点赞。【参考方案4】:如果有人有一个包含 2 列的 CollectionView,并且如果项目数是奇数,则最后一个项目应该居中对齐。然后用这个
DNLastItemCenteredLayout
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes *attribute in attributes)
NSInteger itemCount = [self.collectionView.dataSource collectionView:self.collectionView
numberOfItemsInSection:attribute.indexPath.section];
if (itemCount % 2 == 1 && attribute.indexPath.item == itemCount - 1)
CGRect originalFrame = attribute.frame;
attribute.frame = CGRectMake(self.collectionView.bounds.size.width/2-originalFrame.size.width/2,
originalFrame.origin.y,
originalFrame.size.width,
originalFrame.size.height);
return attributes;
【讨论】:
【参考方案5】:这可以通过从UICollectionViewFlowLayout
继承的(相对)简单的自定义布局来实现。以下是 Swift
中的示例:
/**
* A simple `UICollectionViewFlowLayout` subclass that would make sure the items are center-aligned in the collection view, when scrolling vertically.
*/
class UICollectionViewFlowCenterLayout: UICollectionViewFlowLayout
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]?
guard let suggestedAttributes = super.layoutAttributesForElementsInRect(rect) else return nil
guard scrollDirection == .Vertical else return suggestedAttributes
var newAttributes: [UICollectionViewLayoutAttributes] = []
/// We will collect items for each row in this array
var currentRowAttributes: [UICollectionViewLayoutAttributes] = []
/// We will use this variable to detect new rows when iterating over items
var yOffset:CGFloat = sectionInset.top
for attributes in suggestedAttributes
/// If we happen to run into a new row...
if attributes.frame.origin.y != yOffset
/*
* Update layout of all items in the previous row and add them to the resulting array
*/
centerSingleRowWithItemsAttributes(¤tRowAttributes, rect: rect)
newAttributes += currentRowAttributes
/*
* Reset the accumulated values for the new row
*/
currentRowAttributes = []
yOffset = attributes.frame.origin.y
currentRowAttributes += [attributes]
/*
* Update the layout of the last row.
*/
centerSingleRowWithItemsAttributes(¤tRowAttributes, rect: rect)
newAttributes += currentRowAttributes
return newAttributes
/**
Updates the attributes for items, so that they are center-aligned in the given rect.
- parameter attributes: Attributes of the items
- parameter rect: Bounding rect
*/
private func centerSingleRowWithItemsAttributes(inout attributes: [UICollectionViewLayoutAttributes], rect: CGRect)
guard let item = attributes.last else return
let itemsCount = CGFloat(attributes.count)
let sideInsets = rect.width - (item.frame.width * itemsCount) - (minimumInteritemSpacing * (itemsCount - 1))
var leftOffset = sideInsets / 2
for attribute in attributes
attribute.frame.origin.x = leftOffset
leftOffset += attribute.frame.width + minimumInteritemSpacing
【讨论】:
【参考方案6】:我已将 UICollectionViewFlowLayout
子类化 - 更改了我找到的代码 here 用于左对齐集合视图。
-
左对齐集合视图
对线阵列的属性进行分组
对于每一行:
计算右边的空间
为行的每个属性添加一半的空格
看起来像这样:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
NSArray *attributesForElementsInRect = [super layoutAttributesForElementsInRect:rect];
NSMutableArray *newAttributesForElementsInRect = [[NSMutableArray alloc] initWithCapacity:attributesForElementsInRect.count];
CGFloat leftMargin = self.sectionInset.left;
NSMutableArray *lines = [NSMutableArray array];
NSMutableArray *currLine = [NSMutableArray array];
for (UICollectionViewLayoutAttributes *attributes in attributesForElementsInRect)
// Handle new line
BOOL newLine = attributes.frame.origin.x <= leftMargin;
if (newLine)
leftMargin = self.sectionInset.left; //will add outside loop
currLine = [NSMutableArray arrayWithObject:attributes];
else
[currLine addObject:attributes];
if ([lines indexOfObject:currLine] == NSNotFound)
[lines addObject:currLine];
// Align to the left
CGRect newLeftAlignedFrame = attributes.frame;
newLeftAlignedFrame.origin.x = leftMargin;
attributes.frame = newLeftAlignedFrame;
leftMargin += attributes.frame.size.width + self.minimumInteritemSpacing;
[newAttributesForElementsInRect addObject:attributes];
// Center left aligned lines
for (NSArray *line in lines)
UICollectionViewLayoutAttributes *lastAttributes = line.lastObject;
CGFloat space = CGRectGetWidth(self.collectionView.frame) - CGRectGetMaxX(lastAttributes.frame);
for (UICollectionViewLayoutAttributes *attributes in line)
CGRect newFrame = attributes.frame;
newFrame.origin.x = newFrame.origin.x + space / 2;
attributes.frame = newFrame;
return newAttributesForElementsInRect;
希望它可以帮助某人:)
【讨论】:
【参考方案7】:我制作了 Swift 4 版本的 Kyle Truscott answer:
import UIKit
class CenterFlowLayout: UICollectionViewFlowLayout
private var attrCache = [IndexPath: UICollectionViewLayoutAttributes]()
override func prepare()
attrCache = [:]
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
var updatedAttributes = [UICollectionViewLayoutAttributes]()
let sections = self.collectionView?.numberOfSections ?? 0
var section = 0
while section < sections
let items = self.collectionView?.numberOfItems(inSection: section) ?? 0
var item = 0
while item < items
let indexPath = IndexPath(row: item, section: section)
if let attributes = layoutAttributesForItem(at: indexPath), attributes.frame.intersects(rect)
updatedAttributes.append(attributes)
let headerKind = UICollectionElementKindSectionHeader
if let headerAttributes = layoutAttributesForSupplementaryView(ofKind: headerKind, at: indexPath)
updatedAttributes.append(headerAttributes)
let footerKind = UICollectionElementKindSectionFooter
if let footerAttributes = layoutAttributesForSupplementaryView(ofKind: footerKind, at: indexPath)
updatedAttributes.append(footerAttributes)
item += 1
section += 1
return updatedAttributes
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
if let attributes = attrCache[indexPath]
return attributes
// Find the other items in the same "row"
var rowBuddies = [UICollectionViewLayoutAttributes]()
// Calculate the available width to center stuff within
// sectionInset is NOT applicable here because a) we're centering stuff
// and b) Flow layout has arranged the cells to respect the inset. We're
// just hijacking the X position.
var collectionViewWidth: CGFloat = 0
if let collectionView = collectionView
collectionViewWidth = collectionView.bounds.width - collectionView.contentInset.left
- collectionView.contentInset.right
// To find other items in the "row", we need a rect to check intersects against.
// Take the item attributes frame (from vanilla flow layout), and stretch it out
var rowTestFrame: CGRect = super.layoutAttributesForItem(at: indexPath)?.frame ?? .zero
rowTestFrame.origin.x = 0
rowTestFrame.size.width = collectionViewWidth
let totalRows = self.collectionView?.numberOfItems(inSection: indexPath.section) ?? 0
// From this item, work backwards to find the first item in the row
// Decrement the row index until a) we get to 0, b) we reach a previous row
var rowStartIDX = indexPath.row
while true
let prevIDX = rowStartIDX - 1
if prevIDX < 0
break
let prevPath = IndexPath(row: prevIDX, section: indexPath.section)
let prevFrame: CGRect = super.layoutAttributesForItem(at: prevPath)?.frame ?? .zero
// If the item intersects the test frame, it's in the same row
if prevFrame.intersects(rowTestFrame)
rowStartIDX = prevIDX
else
// Found previous row, escape!
break
// Now, work back UP to find the last item in the row
// For each item in the row, add it's attributes to rowBuddies
var buddyIDX = rowStartIDX
while true
if buddyIDX > totalRows - 1
break
let buddyPath = IndexPath(row: buddyIDX, section: indexPath.section)
if let buddyAttributes = super.layoutAttributesForItem(at: buddyPath),
buddyAttributes.frame.intersects(rowTestFrame),
let buddyAttributesCopy = buddyAttributes.copy() as? UICollectionViewLayoutAttributes
// If the item intersects the test frame, it's in the same row
rowBuddies.append(buddyAttributesCopy)
buddyIDX += 1
else
// Encountered next row
break
let flowDelegate = self.collectionView?.delegate as? UICollectionViewDelegateFlowLayout
let selector = #selector(UICollectionViewDelegateFlowLayout.collectionView(_:layout:minimumInteritemSpacingForSectionAt:))
let delegateSupportsInteritemSpacing = flowDelegate?.responds(to: selector) ?? false
// x-x-x-x ... sum up the interim space
var interitemSpacing = minimumInteritemSpacing
// Check for minimumInteritemSpacingForSectionAtIndex support
if let collectionView = collectionView, delegateSupportsInteritemSpacing && rowBuddies.count > 0
interitemSpacing = flowDelegate?.collectionView?(collectionView,
layout: self,
minimumInteritemSpacingForSectionAt: indexPath.section) ?? 0
let aggregateInteritemSpacing = interitemSpacing * CGFloat(rowBuddies.count - 1)
// Sum the width of all elements in the row
var aggregateItemWidths: CGFloat = 0
for itemAttributes in rowBuddies
aggregateItemWidths += itemAttributes.frame.width
// Build an alignment rect
// | |x-x-x-x| |
let alignmentWidth = aggregateItemWidths + aggregateInteritemSpacing
let alignmentXOffset: CGFloat = (collectionViewWidth - alignmentWidth) / 2
// Adjust each item's position to be centered
var previousFrame: CGRect = .zero
for itemAttributes in rowBuddies
var itemFrame = itemAttributes.frame
if previousFrame.equalTo(.zero)
itemFrame.origin.x = alignmentXOffset
else
itemFrame.origin.x = previousFrame.maxX + interitemSpacing
itemAttributes.frame = itemFrame
previousFrame = itemFrame
// Finally, add it to the cache
attrCache[itemAttributes.indexPath] = itemAttributes
return attrCache[indexPath]
【讨论】:
【参考方案8】:只需链接此流程布局。也可以居中、左、右对齐。
//
// CellAllignmentFlowLayout.swift
// UICollectionView
//
// Created by rajeshkumar Lingavel on 8/11/15.
// Copyright © 2015 rajeshkumar Lingavel. All rights reserved.
//
import UIKit
enum SZAlignment:Int
case Center,
left,
Right
class CellAllignmentFlowLayout: UICollectionViewFlowLayout
var alignment:SZAlignment!
var padding:CGFloat!
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]?
// NSArray *allAttributesInRect = [super
// layoutAttributesForElementsInRect:rect];
let allAttributesInRect:NSArray = super.layoutAttributesForElementsInRect(rect)!
var changedAttributes:NSArray = NSArray()
switch(alignment.rawValue)
case 0:
changedAttributes = alignCenter(allAttributesInRect)
case 1:
changedAttributes = alignLeft(allAttributesInRect)
case 2:
changedAttributes = alignRight(allAttributesInRect)
default:
assertionFailure("No Direction")
return changedAttributes as? [UICollectionViewLayoutAttributes]
private func alignCenter(allAttributesInRect:NSArray) -> NSArray
let numberOfSection:Int = (self.collectionView?.numberOfSections())!
let redefiendArray = NSMutableArray()
for i in 0 ..< numberOfSection
let thisSectionObjects = sectionObjects(allAttributesInRect, section: i)
let totalLines = numberOfLines(thisSectionObjects, section: i)
let lastrowObjects = lastRow(thisSectionObjects, numberOfRows: totalLines, section: i)
let lastRowObjectsRow = setMiddleTheLastRow(lastrowObjects)
let start = (thisSectionObjects.count - lastrowObjects.count)
for j in start..<thisSectionObjects.count
thisSectionObjects.replaceObjectAtIndex(j, withObject: lastRowObjectsRow.objectAtIndex(j - start))
redefiendArray.addObjectsFromArray(thisSectionObjects as [AnyObject])
return redefiendArray
private func alignLeft(allAttributesInRect:NSArray) -> NSArray
return allAttributesInRect;
private func alignRight(allAttributesInRect:NSArray) -> NSArray
return allAttributesInRect;
private func getTotalLenthOftheSection(section:Int,allAttributesInRect:NSArray) -> CGFloat
var totalLength:CGFloat = 0.0
totalLength = totalLength + (CGFloat (((self.collectionView?.numberOfItemsInSection(section))! - 1)) * padding)
for attributes in allAttributesInRect
if(attributes.indexPath.section == section)
totalLength = totalLength + attributes.frame.width
return totalLength
private func numberOfLines(allAttributesInRect:NSArray,section:Int)-> Int
var totalLines:Int = 0
for attributes in allAttributesInRect
if(attributes.indexPath.section == section)
if (attributes.frame.origin.x == self.sectionInset.left)
totalLines = totalLines + 1
return totalLines
private func sectionObjects(allAttributesInRect:NSArray,section:Int) -> NSMutableArray
let objects:NSMutableArray = NSMutableArray()
for attributes in allAttributesInRect
if(attributes.indexPath.section == section)
objects.addObject(attributes)
return objects
private func lastRow(allAttributesInRect:NSArray,numberOfRows:Int,section:Int) -> NSMutableArray
var totalLines:Int = 0
let lastRowArrays:NSMutableArray = NSMutableArray()
for attributes in allAttributesInRect
if(attributes.indexPath.section == section)
if (attributes.frame.origin.x == self.sectionInset.left)
totalLines = totalLines + 1
if(totalLines == numberOfRows)
lastRowArrays.addObject(attributes)
else
if(totalLines == numberOfRows)
lastRowArrays.addObject(attributes)
return lastRowArrays
private func setMiddleTheLastRow(lastRowAttrs:NSMutableArray)->NSMutableArray
let redefinedValues = NSMutableArray()
let totalLengthOftheView = self.collectionView?.frame.width
var totalLenthOftheCells:CGFloat = 0.0
totalLenthOftheCells = totalLenthOftheCells + (CGFloat (lastRowAttrs.count) - 1) * padding
for attrs in lastRowAttrs
totalLenthOftheCells = totalLenthOftheCells + attrs.frame.width
var initalValue = (totalLengthOftheView!/2) - (totalLenthOftheCells/2)
for i in 0..<lastRowAttrs.count
let changeingAttribute:UICollectionViewLayoutAttributes = lastRowAttrs[i] as! UICollectionViewLayoutAttributes
var frame = changeingAttribute.frame
frame.origin.x = initalValue
changeingAttribute.frame = frame
redefinedValues.addObject(changeingAttribute)
initalValue = initalValue + changeingAttribute.frame.width + padding
return redefinedValues;
【讨论】:
【参考方案9】:Swift 3.0 版本的类:
class UICollectionViewFlowCenterLayout: UICollectionViewFlowLayout
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
guard let suggestedAttributes = super.layoutAttributesForElements(in: rect) else return nil
guard scrollDirection == .vertical else return suggestedAttributes
var newAttributes: [UICollectionViewLayoutAttributes] = []
var currentRowAttributes: [UICollectionViewLayoutAttributes] = []
var yOffset:CGFloat = sectionInset.top
for attributes in suggestedAttributes
if attributes.frame.origin.y != yOffset
centerSingleRowWithItemsAttributes(attributes: ¤tRowAttributes, rect: rect)
newAttributes += currentRowAttributes
currentRowAttributes = []
yOffset = attributes.frame.origin.y
currentRowAttributes += [attributes]
centerSingleRowWithItemsAttributes(attributes: ¤tRowAttributes, rect: rect)
newAttributes += currentRowAttributes
return newAttributes
private func centerSingleRowWithItemsAttributes( attributes: inout [UICollectionViewLayoutAttributes], rect: CGRect)
guard let item = attributes.last else return
let itemsCount = CGFloat(attributes.count)
let sideInsets = rect.width - (item.frame.width * itemsCount) - (minimumInteritemSpacing * (itemsCount - 1))
var leftOffset = sideInsets / 2
for attribute in attributes
attribute.frame.origin.x = leftOffset
leftOffset += attribute.frame.width + minimumInteritemSpacing
【讨论】:
以上是关于如何在 UICollectionView 中居中行?的主要内容,如果未能解决你的问题,请参考以下文章
在 UICollectionView 中居中第一个 UICollectionViewCell