ios UICollectionView 多个部分和标题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28102639/
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 Multiple Sections and Headers
提问by user3399235
I am struggling trying to do multiple sections in my collection view with a header for each section. I don't know Obj-C and I've found a good amount of tutorials for it, but haven't been able to figure out how to convert it into Swift.
我正在努力尝试在我的集合视图中做多个部分,每个部分都有一个标题。我不知道 Obj-C,我已经找到了大量的教程,但一直无法弄清楚如何将它转换为 Swift。
All my data is static so all I need is some sort of array or dictionary that I can use to create the multiple sections. I already have a collection view with 1 section working, so if you have any insight or code for multiple sections that'd be helpful.
我所有的数据都是静态的,所以我需要的是某种数组或字典,我可以用它来创建多个部分。我已经有一个包含 1 个部分的集合视图,所以如果您对多个部分有任何见解或代码会有所帮助。
I know how to set multiple sections using
我知道如何使用设置多个部分
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return sectionData.count
}
I think the main thing I need help with is implementing this func
我认为我需要帮助的主要事情是实现这个功能
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { }
and setting up the data!
并设置数据!
UICollectionView and UITableView are almost exactly the same, so if you know how to do multiple sections in a UITableView in Swift, your help is also appreciated
UICollectionView 和 UITableView 几乎完全相同,因此如果您知道如何在 Swift 中的 UITableView 中执行多个部分,也非常感谢您的帮助
回答by mkncode
The cellForItemAtIndexPath
function handles populating each section with cells, it does not handle sections or supplementaryViews, and therefore is not the main thing you need help with when it comes to creating section headers.
该cellForItemAtIndexPath
函数处理用单元格填充每个部分,它不处理部分或补充视图,因此在创建部分标题时不是您需要帮助的主要内容。
the method you need to implement is viewForSupplementaryElementOfKind
. Its signature is:
您需要实现的方法是viewForSupplementaryElementOfKind
. 它的签名是:
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {}
Assuming that your collectionView is working correctly for 1 section (you have properly filled out the body of your cellForItemAtIndexPath and your sectionData array properly reflects the number of sections you want to display), you should be able to implement section headers using the following pointers:
假设您的 collectionView 对 1 个部分正常工作(您已正确填写 cellForItemAtIndexPath 的主体,并且您的 sectionData 数组正确反映了您要显示的部分数量),您应该能够使用以下指针实现部分标题:
Along with cells, UICollectionView
also supports "supplementary" view objects, typically used for headers or footers. These Supplementary Views act very similarly to UICollectionViewCell
objects. In the same way that cellForItemAtIndexPath
handles cells, The viewForSupplementaryElementOfKind
function handles supplementary views.
与单元格一起,UICollectionView
还支持“补充”视图对象,通常用于页眉或页脚。这些补充视图的作用与UICollectionViewCell
对象非常相似。与cellForItemAtIndexPath
处理单元格的方式相同,该viewForSupplementaryElementOfKind
函数处理补充视图。
To implement it, you will need to first prepare your ViewController to do so. First edit your layout object to reflect an appropriate header size, that each header will adhere to:
要实现它,您需要首先准备您的 ViewController 来执行此操作。首先编辑您的布局对象以反映适当的标题大小,每个标题都将遵守:
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.headerReferenceSize = CGSize(width: self.view.frame.size.width, height: 30)
NOTE: I am using a UICollectionViewFlowLayout
注意:我正在使用 UICollectionViewFlowLayout
Next, if you haven't already done so, create a SectionHeader class that defines each section header object, so you can then register that class with your collectionView object like so:
接下来,如果您还没有这样做,请创建一个定义每个部分标题对象的 SectionHeader 类,这样您就可以像这样使用 collectionView 对象注册该类:
collectionView!.registerClass(SectionHeaderView.self, forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: "SectionHeaderView");
Here, the first and third argument passed in are the same as a UICollectionViewCell Class registration, the first argument in this method is the reference to the section header class you created. The third is the reuse identifier for the Supplementary View.
在这里,传入的第一个和第三个参数与 UICollectionViewCell 类注册相同,该方法中的第一个参数是对您创建的节头类的引用。第三个是补充视图的重用标识符。
The second argument is specific to Supplementary Views, this sets the kindof the SupplementaryView, which in this case is a header, the constant string provided by the UICollectionViewFlowLayout class UICollectionElementKindSectionHeader
is used for it. If you noticed the parameters on the viewForSupplementaryElementOfKind
, this kindis later passed in as the kind: String
parameter.
第二个参数是特定于 Supplementary Views 的,它设置了SupplementaryView的种类,在这种情况下是一个标题,UICollectionViewFlowLayout 类提供的常量字符串UICollectionElementKindSectionHeader
用于它。如果您注意到 上的参数viewForSupplementaryElementOfKind
,则稍后将这种类型作为kind: String
参数传入。
Fill in the body of your viewForSupplementaryElementOfKind
the same way you would for a cellForItemAtIndexPath function-- Using the dequeueReusableSupplementaryViewOfKind
method to create a SectionHeader object, then set any attributes as necessary (labels, colors, etc.) and finally return the header object.
以与viewForSupplementaryElementOfKind
cellForItemAtIndexPath 函数相同的方式填充您的主体——使用该dequeueReusableSupplementaryViewOfKind
方法创建一个 SectionHeader 对象,然后根据需要设置任何属性(标签、颜色等),最后返回标题对象。
Hope this helps!!
希望这可以帮助!!
Reference points:
参考点:
回答by Tarun
Define your UICollectionViewCell which will be your Header view of kind UICollectionElementKindSectionHeader - In my case I have two headers - OfferHeaderCell and APRHeaderCell defined as below:
定义您的 UICollectionViewCell ,它将是您的 UICollectionElementKindSectionHeader 类型的标题视图 - 在我的情况下,我有两个标题 - OfferHeaderCell 和 APRHeaderCell 定义如下:
verticalCollectionView.register(UINib(nibName: "OfferHeaderCell", bundle: nil), forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: "OfferHeaderCell")
verticalCollectionView.register(UINib(nibName: "APRHeaderCell", bundle: nil), forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: "APRHeaderCell")
Go ahead and return a header for each section and then set the size of the section header to have a size of zero in this UICollectionViewDelegateFlowLayout function
继续并为每个部分返回一个标题,然后在此 UICollectionViewDelegateFlowLayout 函数中将部分标题的大小设置为零
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if(section==0) {
return CGSize.zero
} else if (section==1) {
return CGSize(width:collectionView.frame.size.width, height:133)
} else {
return CGSize(width:collectionView.frame.size.width, height:100)
}
}
Important to define the viewForSupplementaryElementOfKind for two different sections as below:
为以下两个不同部分定义 viewForSupplementaryElementOfKind 很重要:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
var reusableview = UICollectionReusableView()
if (kind == UICollectionElementKindSectionHeader) {
let section = indexPath.section
switch (section) {
case 1:
let firstheader: OfferHeaderCell = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "OfferHeaderCell", for: indexPath) as! OfferHeaderCell
reusableview = firstheader
case 2:
let secondHeader: APRHeaderCell = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "APRHeaderCell", for: indexPath) as! APRHeaderCell
reusableview = secondHeader
default:
return reusableview
}
}
return reusableview
}
And lastly the Datasource,
最后是数据源,
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (section==2) {
return 2
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = verticalCollectionView.dequeueReusableCell(withReuseIdentifier: "ReviseOfferCell", for: indexPath)
cell.backgroundColor = UIColor.white
return cell
}
Note: Don't forgot to add UICollectionFlowLayout as below:
注意:不要忘记添加 UICollectionFlowLayout 如下:
// MARK: UICollectionViewDelegateFlowLayout
// MARK: UICollectionViewDelegateFlowLayout
extension MakeAnOfferController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.item == 0 {
return CGSize(width: self.view.frame.size.width, height: 626.0)
}
return CGSize()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if(section==0) {
return CGSize.zero
} else if (section==1) {
return CGSize(width:collectionView.frame.size.width, height:133)
} else {
return CGSize(width:collectionView.frame.size.width, height:100)
}
}
}
回答by lguerra10
Here is the code that worked for me
这是对我有用的代码
create the header cell. To do which i created a custom cell class and a nib to do the customization of the cell in the graphic editor
创建标题单元格。为此,我创建了一个自定义单元格类和一个笔尖来在图形编辑器中自定义单元格
In viewDidLoad add the following
在 viewDidLoad 添加以下内容
self.collectionView?.registerNib(UINib(nibName: "KlosetCollectionHeaderViewCell", bundle: nil), forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: "HeaderCell")
Then you add the delegate function
然后你添加委托功能
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> KlosetCollectionHeaderViewCell {
let headerCell = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "HeaderCell", forIndexPath: indexPath) as? KlosetCollectionHeaderViewCell
return headerCell!
}
This will put the HeaderCell in the SectionView of the PFCollectionView The controls that show in the cell you add them to the xib file as well as the outlets and actions
这会将 HeaderCell 放在 PFCollectionView 的 SectionView 单元格中显示的控件,您将它们添加到 xib 文件以及插座和操作
回答by Sean
After creating and registering custom header (and/or footers), you can easily specify different header (or footers for that matter) for different section. Here's an example:
创建和注册自定义页眉(和/或页脚)后,您可以轻松地为不同的部分指定不同的页眉(或页脚)。下面是一个例子:
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let section = indexPath.section
switch section {
case 0:
let userHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: userHeaderReuseIdentifier, for: indexPath) as! UserHeader
return userHeader
default:
let postHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: postHeaderReuseIdentifier, for: indexPath) as! PostHeader
return postHeader
}
case UICollectionElementKindSectionFooter:
let userFooter = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: userFooterReuseIdentifier, for: indexPath) as! UserFooter
return userFooter
default:
return UICollectionReusableView()
}
}
Make sure to specify correct number of sections, too:
确保指定正确的节数:
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
回答by Tiago Mendes
Here is the code to achieve UICollection multiple sections made programmatically using SnapKit
这是使用 SnapKit 以编程方式实现 UICollection 多个部分的代码
ViewController
视图控制器
import SnapKit
import UIKit
class SelectIconViewController: GenericViewController<SelectIconView>, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
weak var delegate: SpaceAddViewController?
struct Section {
var sectionName : String
var rowData : [String]
}
var sections : [Section]!
init(delegate: SpaceAddViewController) {
self.delegate = delegate
super.init()
}
public required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
contentView.closeButton.addTarget(self, action: #selector(self.back), for: .touchUpInside)
self.sections = [
Section(sectionName: "SPACES", rowData: ["Air Conditioner", "Apple HomePod"]),
Section(sectionName: "HOME APPLIANCES", rowData: ["Ceiling Fan", "Fan", "Desk Lamp", "Iron", "PC on Desk", "Plug", "Power Strip", "Lorem", "Lorem", "Lorem", "Lorem"]),
]
self.contentView.collectionView.dataSource = self
self.contentView.collectionView.delegate = self
self.contentView.collectionView.register(SelectIconHeaderViewCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: SelectIconHeaderViewCell.reuseId)
self.contentView.collectionView.register(SelectIconViewCell.self, forCellWithReuseIdentifier: SelectIconViewCell.reuseId)
}
@objc func back() {
self.dismiss(animated: true, completion: nil)
}
@objc func dismissKeyboard() {
view.endEditing(true)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return self.sections.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.sections[section].rowData.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: getTotalSpacing(), height: getTotalSpacing())
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width-40
return CGSize(width: screenWidth-80, height: 50)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
// MARK: Cells
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.contentView.collectionView.dequeueReusableCell(withReuseIdentifier: SelectIconViewCell.reuseId, for: indexPath as IndexPath) as! SelectIconViewCell
cell.initializeUI()
cell.createConstraints()
cell.setValues(iconName: "", label: self.sections[indexPath.section].rowData[indexPath.row])
return cell
}
// MARK: Header
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionView.elementKindSectionHeader:
let cell = self.contentView.collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: SelectIconHeaderViewCell.reuseId, for: indexPath) as! SelectIconHeaderViewCell
cell.initializeUI()
cell.createConstraints()
cell.setTitle(title: self.sections[indexPath.section].sectionName)
return cell
default: fatalError("Unexpected element kind")
}
}
func getTotalSpacing() -> CGFloat {
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
let numberOfItemsPerRow:CGFloat = 3
let spacingBetweenCells:CGFloat = 0
let sideSpacing:CGFloat = 20
return (screenWidth-(2 * sideSpacing) - ((numberOfItemsPerRow - 1) * spacingBetweenCells))/numberOfItemsPerRow
}
}
The View:
风景:
import UIKit
import SnapKit
class SelectIconView: GenericView {
private let contentView = UIView(frame: .zero)
private (set) var closeButton = UIButton(type: .system)
internal var collectionView: UICollectionView!
internal override func initializeUI() {
self.backgroundColor = Theme.Color.white
self.addSubview(contentView)
contentView.addSubview(closeButton)
if let image = UIImage(named: "icon_close") {
image.withRenderingMode(.alwaysTemplate)
closeButton.setImage(image, for: .normal)
closeButton.tintColor = Theme.Color.text
}
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.minimumInteritemSpacing = 0
collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
contentView.addSubview(collectionView)
collectionView.backgroundColor = Theme.Color.background
}
internal override func createConstraints() {
contentView.snp.makeConstraints { (make) in
make.top.equalTo(safeAreaLayoutGuide.snp.top).priority(750)
make.left.right.equalTo(self).priority(1000)
make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom)
}
closeButton.snp.makeConstraints { make in
make.right.equalTo(safeAreaLayoutGuide.snp.right).offset(-10)
make.top.equalTo(contentView.snp.top).offset(10)
make.height.equalTo(40)
make.width.equalTo(40)
}
collectionView.snp.makeConstraints { make in
make.top.equalTo(closeButton.snp.bottom).offset(20)
make.left.equalTo(safeAreaLayoutGuide.snp.left)
make.right.equalTo(safeAreaLayoutGuide.snp.right)
make.bottom.equalTo(contentView.snp.bottom)
}
}
}
The customized section Header
自定义部分标题
import UIKit
class SelectIconHeaderViewCell: UICollectionViewCell {
internal let mainView = UIView()
internal var title = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func initializeUI() {
self.backgroundColor = UIColor.clear
self.addSubview(mainView)
mainView.backgroundColor = UIColor.clear
mainView.addSubview(title)
title.text = "Pick nameA"
title.font = Theme.Font.body()
title.textAlignment = .left
title.textColor = Theme.Color.text
title.numberOfLines = 1
}
internal func createConstraints() {
mainView.snp.makeConstraints { (make) in
make.edges.equalTo(self)
}
title.snp.makeConstraints { (make) in
make.centerY.equalTo(mainView.snp.centerY)
make.leading.equalTo(mainView).offset(20)
make.trailing.equalTo(mainView).offset(-20)
}
}
func setTitle(title: String) {
self.title.text = title
}
static var reuseId: String {
return NSStringFromClass(self)
}
}
And the cell:
和细胞:
import UIKit
class SelectIconViewCell: UICollectionViewCell {
internal let mainView = UIView()
internal var iconImage = UIImageView()
internal var label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func initializeUI() {
self.backgroundColor = UIColor.clear
self.addSubview(mainView)
mainView.backgroundColor = UIColor.clear
mainView.layer.masksToBounds = true
mainView.layer.borderColor = Theme.Color.backgroundCell.cgColor
mainView.layer.borderWidth = 1.0
mainView.addSubview(iconImage)
iconImage.image = UIImage(named: "icons8-air-conditioner-100")
mainView.addSubview(label)
label.font = Theme.Font.footnote()
label.textAlignment = .center
label.textColor = Theme.Color.textInfo
label.numberOfLines = 1
}
internal func createConstraints() {
mainView.snp.makeConstraints { (make) in
make.edges.equalTo(self)
}
iconImage.snp.makeConstraints { (make) in
make.center.equalTo(mainView.snp.center)
make.width.height.equalTo(20)
}
label.snp.makeConstraints { (make) in
make.top.equalTo(iconImage.snp.bottom).offset(6)
make.leading.equalTo(mainView).offset(5)
make.trailing.equalTo(mainView).offset(-5)
}
}
func setValues(iconName: String, label: String) {
//self.iconImage.image = UIImage(named: iconName)
self.label.text = label
}
static var reuseId: String {
return NSStringFromClass(self)
}
}
回答by Glenn
@Tarun's answer worked a treat for me; I was missing collectionView(_:layout:referenceSizeForHeaderInSection:)
, which I needed since sometimes the data to be shown would be sorted and sometimes not.
@Tarun 的回答对我很有帮助;我错过了collectionView(_:layout:referenceSizeForHeaderInSection:)
,我需要它,因为有时要显示的数据会被排序,有时不会。
In addition, pinning the section header to the top of the screen (as in the table view Apple's Address Book app) was accomplished by adding the following to viewDidLoad()
in the UICollectionViewController:
此外,通过viewDidLoad()
在 UICollectionViewController 中添加以下内容,将部分标题固定到屏幕顶部(如 Apple 的地址簿应用程序中的表格视图):
if let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.sectionHeadersPinToVisibleBounds = true
}
回答by Shrawan
Worked solution for Swift-3
Swift-3 的有效解决方案
i)Create Custom Cell && corresponding xib
i) 创建自定义单元格 && 对应的 xib
class SectionHeaderView: UICollectionViewCell {
static let kReuseIdentifier = "SectionHeaderView"
@IBOutlet weak var invitationsSectionHeader: UILabel!
@IBOutlet weak var numberOfPerson: UILabel!
}
ii)Register Custom Collection View Cell for HeaderView
ii) 为 HeaderView 注册自定义集合视图单元
self.collectionView.register(UINib(nibName: SectionHeaderView.kReuseIdentifier, bundle: nil), forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: SectionHeaderView.kReuseIdentifier)
iii)Call delegate function to render Custom Header View.
iii) 调用委托函数来呈现自定义标题视图。
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let headerView: SectionHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: SectionHeaderView.kReuseIdentifier, for: indexPath) as! SectionHeaderView
return headerView
default:
return UICollectionReusableView()
}
}
iv)Mention Height of the Custom Header View
iv) 提及自定义标题视图的高度
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width:collectionView.frame.size.width, height:30)
}