ios UICollectionView 设置列数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14674986/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UICollectionView Set number of columns
提问by SNV7
I just started learning about UICollectionViews. I'm wondering if anyone knows how to specify the number of columns in a collectionview. The default is set to 3 (iPhone/portrait). I've looked at the documentation and can't seem to find a concise answer.
我刚开始学习 UICollectionViews。我想知道是否有人知道如何指定集合视图中的列数。默认设置为 3(iPhone/人像)。我查看了文档,似乎找不到简明的答案。
采纳答案by jhilgert00
CollectionViews are very powerful, and they come at a price. Lots, and lots of options. As omz said:
CollectionViews 非常强大,而且它们是有代价的。很多,很多选择。正如 omz 所说:
there are multiple ways you could change the number of columns
有多种方法可以更改列数
I'd suggest implementing the <UICollectionViewDelegateFlowLayout>
Protocol, giving you access to the following methods in which you can have greater control over the layout of your UICollectionView
, without the need for subclassing it:
我建议实施<UICollectionViewDelegateFlowLayout>
协议,让您可以访问以下方法,您可以在其中更好地控制 . 的布局UICollectionView
,而无需对其进行子类化:
collectionView:layout:insetForSectionAtIndex:
collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
collectionView:layout:minimumLineSpacingForSectionAtIndex:
collectionView:layout:referenceSizeForFooterInSection:
collectionView:layout:referenceSizeForHeaderInSection:
collectionView:layout:sizeForItemAtIndexPath:
collectionView:layout:insetForSectionAtIndex:
collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
collectionView:layout:minimumLineSpacingForSectionAtIndex:
collectionView:layout:referenceSizeForFooterInSection:
collectionView:layout:referenceSizeForHeaderInSection:
collectionView:layout:sizeForItemAtIndexPath:
Also, implementing the following method will force your UICollectionView to update it's layout on an orientation change: (say you wanted to re-size the cells for landscape and make them stretch)
此外,实现以下方法将强制您的 UICollectionView 在方向更改时更新其布局:(假设您想为横向调整单元格的大小并使其拉伸)
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration{
[self.myCollectionView.collectionViewLayout invalidateLayout];
}
Additionally, here are 2 really good tutorials on UICollectionViews
:
此外,这里有 2 个非常好的教程UICollectionViews
:
http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12
http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12
回答by Imanou Petit
With Swift 5 and iOS 12.3, you can use one the 4 following implementationsin order to set the number of items per row in your UICollectionView
while managing insets and size changes (including rotation).
在 Swift 5 和 iOS 12.3 中,您可以使用以下 4 个实现中的一个来设置每行中的项目数,UICollectionView
同时管理插入和大小更改(包括旋转)。
#1. Subclassing UICollectionViewFlowLayout
and using UICollectionViewFlowLayout
's itemSize
property
#1. 子类化UICollectionViewFlowLayout
和 usingUICollectionViewFlowLayout
的itemSize
属性
ColumnFlowLayout.swift:
ColumnFlowLayout.swift:
import UIKit
class ColumnFlowLayout: UICollectionViewFlowLayout {
let cellsPerRow: Int
init(cellsPerRow: Int, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
self.cellsPerRow = cellsPerRow
super.init()
self.minimumInteritemSpacing = minimumInteritemSpacing
self.minimumLineSpacing = minimumLineSpacing
self.sectionInset = sectionInset
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepare() {
super.prepare()
guard let collectionView = collectionView else { return }
let marginsAndInsets = sectionInset.left + sectionInset.right + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)
itemSize = CGSize(width: itemWidth, height: itemWidth)
}
override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext {
let context = super.invalidationContext(forBoundsChange: newBounds) as! UICollectionViewFlowLayoutInvalidationContext
context.invalidateFlowLayoutDelegateMetrics = newBounds.size != collectionView?.bounds.size
return context
}
}
CollectionViewController.swift:
CollectionViewController.swift:
import UIKit
class CollectionViewController: UICollectionViewController {
let columnLayout = ColumnFlowLayout(
cellsPerRow: 5,
minimumInteritemSpacing: 10,
minimumLineSpacing: 10,
sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
)
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.collectionViewLayout = columnLayout
collectionView?.contentInsetAdjustmentBehavior = .always
collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 59
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.backgroundColor = UIColor.orange
return cell
}
}
#2. Using UICollectionViewFlowLayout
's itemSize
method
#2. usingUICollectionViewFlowLayout
的itemSize
方法
import UIKit
class CollectionViewController: UICollectionViewController {
let margin: CGFloat = 10
let cellsPerRow = 5
override func viewDidLoad() {
super.viewDidLoad()
guard let collectionView = collectionView, let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout else { return }
flowLayout.minimumInteritemSpacing = margin
flowLayout.minimumLineSpacing = margin
flowLayout.sectionInset = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)
collectionView.contentInsetAdjustmentBehavior = .always
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
}
override func viewWillLayoutSubviews() {
guard let collectionView = collectionView, let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
let marginsAndInsets = flowLayout.sectionInset.left + flowLayout.sectionInset.right + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + flowLayout.minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)
flowLayout.itemSize = CGSize(width: itemWidth, height: itemWidth)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 59
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.backgroundColor = UIColor.orange
return cell
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
collectionView?.collectionViewLayout.invalidateLayout()
super.viewWillTransition(to: size, with: coordinator)
}
}
#3. Using UICollectionViewDelegateFlowLayout
's collectionView(_:layout:sizeForItemAt:)
method
#3. usingUICollectionViewDelegateFlowLayout
的collectionView(_:layout:sizeForItemAt:)
方法
import UIKit
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let inset: CGFloat = 10
let minimumLineSpacing: CGFloat = 10
let minimumInteritemSpacing: CGFloat = 10
let cellsPerRow = 5
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.contentInsetAdjustmentBehavior = .always
collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: inset, left: inset, bottom: inset, right: inset)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return minimumLineSpacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return minimumInteritemSpacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let marginsAndInsets = inset * 2 + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)
return CGSize(width: itemWidth, height: itemWidth)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 59
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.backgroundColor = UIColor.orange
return cell
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
collectionView?.collectionViewLayout.invalidateLayout()
super.viewWillTransition(to: size, with: coordinator)
}
}
#4. Subclassing UICollectionViewFlowLayout
and using UICollectionViewFlowLayout
's estimatedItemSize
property
#4. 子类化UICollectionViewFlowLayout
和 usingUICollectionViewFlowLayout
的estimatedItemSize
属性
CollectionViewController.swift:
CollectionViewController.swift:
import UIKit
class CollectionViewController: UICollectionViewController {
let items = [
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"Lorem ipsum dolor sit amet, consectetur.",
"Lorem ipsum dolor sit amet.",
"Lorem ipsum dolor sit amet, consectetur.",
"Lorem ipsum dolor sit amet, consectetur adipiscing.",
"Lorem ipsum.",
"Lorem ipsum dolor sit amet.",
"Lorem ipsum dolor sit.",
"Lorem ipsum dolor sit amet, consectetur adipiscing.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
"Lorem ipsum dolor sit amet, consectetur."
]
let columnLayout = FlowLayout(
cellsPerRow: 3,
minimumInteritemSpacing: 10,
minimumLineSpacing: 10,
sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
)
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.collectionViewLayout = columnLayout
collectionView?.contentInsetAdjustmentBehavior = .always
collectionView?.register(Cell.self, forCellWithReuseIdentifier: "Cell")
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
cell.label.text = items[indexPath.row]
return cell
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
collectionView?.collectionViewLayout.invalidateLayout()
super.viewWillTransition(to: size, with: coordinator)
}
}
FlowLayout.swift:
FlowLayout.swift:
import UIKit
class FlowLayout: UICollectionViewFlowLayout {
let cellsPerRow: Int
required init(cellsPerRow: Int = 1, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
self.cellsPerRow = cellsPerRow
super.init()
self.minimumInteritemSpacing = minimumInteritemSpacing
self.minimumLineSpacing = minimumLineSpacing
self.sectionInset = sectionInset
estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath) else { return nil }
guard let collectionView = collectionView else { return layoutAttributes }
let marginsAndInsets = collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + sectionInset.left + sectionInset.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
layoutAttributes.bounds.size.width = ((collectionView.bounds.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)
return layoutAttributes
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let superLayoutAttributes = super.layoutAttributesForElements(in: rect)!.map { import UIKit
class Cell: UICollectionViewCell {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
label.numberOfLines = 0
backgroundColor = .orange
contentView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
layoutIfNeeded()
label.preferredMaxLayoutWidth = label.bounds.size.width
layoutAttributes.bounds.size.height = contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
return layoutAttributes
}
// Alternative implementation
/*
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
label.preferredMaxLayoutWidth = layoutAttributes.size.width - contentView.layoutMargins.left - contentView.layoutMargins.right
layoutAttributes.bounds.size.height = contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
return layoutAttributes
}
*/
}
.copy() as! UICollectionViewLayoutAttributes }
guard scrollDirection == .vertical else { return superLayoutAttributes }
let layoutAttributes = superLayoutAttributes.compactMap { layoutAttribute in
return layoutAttribute.representedElementCategory == .cell ? layoutAttributesForItem(at: layoutAttribute.indexPath) : layoutAttribute
}
// (optional) Uncomment to top align cells that are on the same line
/*
let cellAttributes = layoutAttributes.filter({ - (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
float cellWidth = screenWidth / 3.0; //Replace the divisor with the column count requirement. Make sure to have it in float.
CGSize size = CGSizeMake(cellWidth, cellWidth);
return size;
}
.representedElementCategory == .cell })
for (_, attributes) in Dictionary(grouping: cellAttributes, by: { (func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let totalSpace = flowLayout.sectionInset.left
+ flowLayout.sectionInset.right
+ (flowLayout.minimumInteritemSpacing * CGFloat(numberOfItemsPerRow - 1))
let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(numberOfItemsPerRow))
return CGSize(width: size, height: size)
}
.center.y / 10).rounded(.up) * 10 }) {
guard let max = attributes.max(by: { func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let nbCol = 2
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let totalSpace = flowLayout.sectionInset.left
+ flowLayout.sectionInset.right
+ (flowLayout.minimumInteritemSpacing * CGFloat(nbCol - 1))
let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(nbCol))
return CGSize(width: size, height: size)
}
.size.height < .size.height }) else { continue }
for attribute in attributes where attribute.size.height != max.size.height {
attribute.frame.origin.y = max.frame.origin.y
}
}
*/
// (optional) Uncomment to bottom align cells that are on the same line
/*
let cellAttributes = layoutAttributes.filter({ extension UICollectionView {
func setItemsInRow(items: Int) {
if let layout = self.collectionViewLayout as? UICollectionViewFlowLayout {
let contentInset = self.contentInset
let itemsInRow: CGFloat = CGFloat(items);
let innerSpace = layout.minimumInteritemSpacing * (itemsInRow - 1.0)
let insetSpace = contentInset.left + contentInset.right + layout.sectionInset.left + layout.sectionInset.right
let width = floor((CGRectGetWidth(frame) - insetSpace - innerSpace) / itemsInRow);
layout.itemSize = CGSizeMake(width, width)
}
}
}
.representedElementCategory == .cell })
for (_, attributes) in Dictionary(grouping: cellAttributes, by: { (class CollectionViewCell: UICollectionViewCell {
@IBOutlet weak var label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.backgroundColor = UIColor.black
label.textColor = UIColor.white
}
}
class CollectionViewHeadCell: UICollectionViewCell {
@IBOutlet weak var label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.backgroundColor = UIColor.darkGray
label.textColor = UIColor.white
}
}
.center.y / 10).rounded(.up) * 10 }) {
guard let max = attributes.max(by: { let cellHeight: CGFloat = 100
let cellWidth: CGFloat = 100
class CustomCollectionViewLayout: UICollectionViewLayout {
private var numberOfColumns: Int!
private var numberOfRows: Int!
// It is two dimension array of itemAttributes
private var itemAttributes = [[UICollectionViewLayoutAttributes]]()
// It is one dimension of itemAttributes
private var cache = [UICollectionViewLayoutAttributes]()
override func prepare() {
if self.cache.isEmpty {
self.numberOfColumns = self.collectionView?.numberOfItems(inSection: 0)
self.numberOfRows = self.collectionView?.numberOfSections
// Dynamically change cellWidth if total cell width is smaller than whole bounds
/* if (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns) > cellWidth {
self.cellWidth = (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns)
}
*/
for row in 0..<self.numberOfRows {
var row_temp = [UICollectionViewLayoutAttributes]()
for column in 0..<self.numberOfColumns {
let indexPath = NSIndexPath(item: column, section: row)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
attributes.frame = CGRect(x: cellWidth*CGFloat(column), y: cellHeight*CGFloat(row), width: cellWidth, height: cellHeight)
row_temp.append(attributes)
self.cache.append(attributes)
}
self.itemAttributes.append(row_temp)
}
}
}
override var collectionViewContentSize: CGSize {
return CGSize(width: CGFloat(self.numberOfColumns)*cellWidth, height: CGFloat(self.numberOfRows)*cellHeight)
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttributes = [UICollectionViewLayoutAttributes]()
for attributes in cache {
if attributes.frame.intersects(rect) {
layoutAttributes.append(attributes)
}
}
return layoutAttributes
}
}
.size.height < .size.height }) else { continue }
for attribute in attributes where attribute.size.height != max.size.height {
attribute.frame.origin.y += max.frame.maxY - attribute.frame.maxY
}
}
*/
return layoutAttributes
}
}
Cell.swift:
细胞.swift:
let CellIdentifier = "CellIdentifier"
let HeadCellIdentifier = "HeadCellIdentifier"
class CollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {
init() {
let layout = CustomCollectionViewLayout()
super.init(frame: CGRect.zero, collectionViewLayout: layout)
self.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: CellIdentifier)
self.register(UINib(nibName: "CollectionViewHeadCell", bundle: nil), forCellWithReuseIdentifier: HeadCellIdentifier)
self.isDirectionalLockEnabled = true
self.dataSource = self
self.delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func updateCollectionView() {
DispatchQueue.main.async {
self.reloadData()
}
}
// MARK: CollectionView datasource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 20
}
override func numberOfItems(inSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let column = (indexPath as NSIndexPath).row
let row = (indexPath as NSIndexPath).section
if column == 0 {
let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell
cell.label.text = "\(row)"
return cell
}
else if row == 0 {
let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell
cell.label.text = "\(column)"
return cell
}
else {
let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! CollectionViewCell
cell.label.text = String(format: "%d", arguments: [indexPath.section*indexPath.row])
return cell
}
}
// MARK: CollectionView delegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let column = (indexPath as NSIndexPath).row
let row = (indexPath as NSIndexPath).section
print("\(column) \(row)")
}
}
回答by noob
I implemented UICollectionViewDelegateFlowLayout
on my UICollectionViewController
and override the method responsible for determining the size of the Cell. I then took the screen width and divided it with my column requirement. For example, I wanted to have 3 columns on each screen size. So here's what my code looks like -
我UICollectionViewDelegateFlowLayout
在 my 上实现UICollectionViewController
并覆盖了负责确定 Cell 大小的方法。然后我把屏幕宽度和我的列要求分开。例如,我想在每个屏幕尺寸上有 3 列。所以这就是我的代码的样子 -
class ViewController: UIViewController {
let collectionView = CollectionView()
override func viewDidLoad() {
collectionView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(collectionView)
self.view.backgroundColor = UIColor.red
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView]))
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView]))
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
collectionView.updateCollectionView()
}
}
回答by sgib
Expanding on noob's answer:
扩展菜鸟的答案:
let numberOfColumns: CGFloat = 3
This allows for any spacing between the cells. It assumes an Int
member variable called numberOfItemsPerRow
and also that all the cells are square and the same size. As noted in jhilgert00's answer we must also react to orientation changes, but now by using viewWillTransitionToSize
as willRotateToInterfaceOrientation
is depreciated.
这允许单元格之间的任何间距。它假定一个Int
名为的成员变量numberOfItemsPerRow
,并且所有的单元格都是正方形且大小相同。正如 jhilgert00 的回答中所指出的,我们还必须对方向变化做出反应,但现在使用viewWillTransitionToSize
aswillRotateToInterfaceOrientation
已折旧。
回答by fraxool
Here is the working code for Swift 3, to have a two-columns layout :
这是 Swift 3 的工作代码,具有两列布局:
if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
let horizontalSpacing = flowLayout.scrollDirection == .vertical ? flowLayout.minimumInteritemSpacing : flowLayout.minimumLineSpacing
let cellWidth = (collectionView.frame.width - max(0, numberOfColumns - 1)*horizontalSpacing)/numberOfColumns
flowLayout.itemSize = CGSize(width: cellWidth, height: cellWidth)
}
Feel free to change "nbCol" to your desired number of columns.
随意将“nbCol”更改为您想要的列数。
回答by HotJard
If you are lazy using delegate.
如果你懒惰使用委托。
let margin: CGFloat = 10
PS: Should be called after rotation too
PS:也应该在旋转后调用
回答by brianLikeApple
Updated to Swift 3:
更新到 Swift 3:
Instead of the flow layout, I prefer using custom layout for specific column number and row number. Because:
而不是流布局,我更喜欢为特定的列号和行号使用自定义布局。因为:
- It can be dragged horizontally if column number is very big.
- It is more acceptable logically because of using column and row.
- 如果列数很大,可以水平拖动。
- 由于使用列和行,因此在逻辑上更容易接受。
Normal cell and Header cell: (Add UILabel as a IBOutlet to your xib):
普通单元格和标题单元格:(将 UILabel 作为 IBOutlet 添加到您的 xib):
guard let collectionView = docsColl, let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
flowLayout.minimumInteritemSpacing = margin
flowLayout.minimumLineSpacing = margin
flowLayout.sectionInset = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)
Custom layout:
自定义布局:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let noOfCellsInRow = 2 //number of column you want
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let totalSpace = flowLayout.sectionInset.left
+ flowLayout.sectionInset.right
+ (flowLayout.minimumInteritemSpacing * CGFloat(noOfCellsInRow - 1))
let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(noOfCellsInRow))
return CGSize(width: size, height: size)
}
CollectionView:
收藏视图:
##代码##Use CollectionView from ViewController:
使用 ViewController 中的 CollectionView:
##代码##Finally you can have fancy CollectionView!
终于可以拥有精美的 CollectionView 了!
回答by omz
Because UICollectionView
is so flexible, there are multiple ways you could change the number of columns, depending on the kind of layout you use.
由于UICollectionView
非常灵活,您可以通过多种方式更改列数,具体取决于您使用的布局类型。
The UICollectionViewFlowLayout
(which is probably what you're working with) doesn't specify a number of columns directly (because it depends on the view size/orientation). The easiest way to change it would be to set the itemSize
property and/or minimumInteritemSpacing
/minimumLineSpacing
.
的UICollectionViewFlowLayout
(这可能是你正在使用的)不直接指定的列数(因为它取决于视图大小/方向)。更改它的最简单方法是设置itemSize
属性和/或minimumInteritemSpacing
/ minimumLineSpacing
。
回答by Jo?e Ws
Swift 3.0. Works for both horizontal and vertical scroll directions and variable spacing
斯威夫特 3.0。适用于水平和垂直滚动方向和可变间距
Specify number of columns
指定列数
##代码##Configure flowLayout
to render specified numberOfColumns
配置flowLayout
渲染指定numberOfColumns
回答by Sandeep Maurya
Updated to Swift 5+ iOS 13
更新到 Swift 5+ iOS 13
Collectionview Estimate Size must be none
集合视图估计大小必须为无
Declare margin for cell
声明单元格的边距
##代码##In viewDidLoad configure minimumInteritemSpacing
, minimumLineSpacing
, sectionInset
在 viewDidLoad 配置minimumInteritemSpacing
, minimumLineSpacing
,sectionInset
UICollectionViewDataSourcemethod sizeForItemAt
UICollectionViewDataSource方法sizeForItemAt