UICollectionView多个部分和标题

时间:2015-01-23 03:39:43

标签: ios swift ios8 uicollectionview

我正在努力尝试在我的集合视图中使用每个部分的标题执行多个部分。我不知道Obj-C,我已经找到了很多教程,但还是没能弄清楚如何将它转换成Swift。

我的所有数据都是静态的,所以我需要的是某种可用于创建多个部分的数组或字典。我已经有一个包含1个部分的集合视图,所以如果你有任何见解或代码可以帮助多个部分。

我知道如何使用

设置多个部分
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int   {
    return sectionData.count
}

我认为我需要帮助的主要是实现这个功能

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { }

并设置数据!

UICollectionView和UITableView几乎完全相同,所以如果你知道如何在Swift的UITableView中做多个部分,你的帮助也很感激

7 个答案:

答案 0 :(得分:15)

cellForItemAtIndexPath函数处理用单元格填充每个部分,它不处理部分或补充视图,因此在创建部分标题时,这不是您需要帮助的主要内容。

您需要实施的方法是viewForSupplementaryElementOfKind。它的签名是:

func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {}

假设你的collectionView在1个部分正常工作(你已经正确填写了cellForItemAtIndexPath的主体,你的sectionData数组正确地反映了你想要显示的部分的数量),你应该能够使用以下指针:

与单元格一起,UICollectionView还支持“补充”视图对象,通常用于页眉或页脚。这些补充视图与UICollectionViewCell对象的行为非常相似。与cellForItemAtIndexPath处理单元格的方式相同,viewForSupplementaryElementOfKind函数处理补充视图。

要实现它,您需要先准备ViewController才能执行此操作。首先编辑您的布局对象,以反映每个标题将遵循的适当标题大小:

 let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
 layout.headerReferenceSize = CGSize(width: self.view.frame.size.width, height: 30)

注意:我使用的是UICollectionViewFlowLayout

接下来,如果您还没有这样做,请创建一个SectionHeader类来定义每个节头对象,这样您就可以使用collectionView对象注册该类,如下所示:

  collectionView!.registerClass(SectionHeaderView.self, forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: "SectionHeaderView");

这里,传入的第一个和第三个参数与UICollectionViewCell类注册相同,此方法中的第一个参数是对您创建的节头类的引用。第三个是补充视图的重用标识符。

第二个参数特定于Supplementary Views,它设置SupplementaryView的,在这种情况下是一个头,使用由UICollectionViewFlowLayout类UICollectionElementKindSectionHeader提供的常量字符串为了它。如果您注意到viewForSupplementaryElementOfKind上的参数,则此种类稍后会作为kind: String参数传入。

以与对于cellForItemAtIndexPath函数相同的方式填充viewForSupplementaryElementOfKind的正文 - 使用dequeueReusableSupplementaryViewOfKind方法创建SectionHeader对象,然后根据需要设置任何属性(标签,颜色,等)并最终返回标题对象。

希望这会有所帮助!!

参考点:

https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UICollectionViewDataSource_protocol/index.html#//apple_ref/occ/intfm/UICollectionViewDataSource/

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewFlowLayout_class/index.html#//apple_ref/c/data/UICollectionElementKindSectionHeade

答案 1 :(得分:4)

以下是适用于我的代码

创建标题单元格。为此我创建了一个自定义单元格类和一个nib来在图形编辑器中自定义单元格

在viewDidLoad中添加以下内容

self.collectionView?.registerNib(UINib(nibName: "KlosetCollectionHeaderViewCell", bundle: nil), forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: "HeaderCell")

然后添加委托函数

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

这会将HeaderCell放在PFCollectionView的SectionView中 单元格中显示的控件,您将它们添加到xib文件以及出口和操作

答案 2 :(得分:2)

创建并注册自定义页眉(和/或页脚)后,您可以轻松地为不同的部分指定不同的页眉(或页脚)。这是一个例子:

    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: homeHeaderReuseIdentifier, for: indexPath) as! UserHeader
                return userHeader
            default:
                let postHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: sectionSpacingHeaderReuseIdentifier, for: indexPath) as! PostHeader
                return postHeader
            }
        case UICollectionElementKindSectionFooter:
            let userFooter = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: homeFooterReuseIdentifier, for: indexPath) as! UserFooter
            return userFooter
        default:
            return UICollectionReusableView()
        }
    }

确保也指定正确的部分数量:

    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 2
    }

答案 3 :(得分:1)

定义你的UICollectionViewCell,这将是你的Header视图类型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")

继续并为每个部分返回一个标题,然后在此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)
    }

}

重要的是为两个不同的部分定义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
}

最后是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
}

注意:不要忘记添加UICollectionFlowLayout,如下所示:

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

答案 4 :(得分:1)

这是使用SnapKit以编程方式实现UICollection多个部分的代码

enter image description here

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
    }

}

视图:

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

    }

}

自定义部分标题

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

}

单元格:

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

}

答案 5 :(得分:0)

Swift-3的工作解决方案

i)创建自定义单元格&amp;&amp;相应的xib

   class SectionHeaderView: UICollectionViewCell {
        static let kReuseIdentifier = "SectionHeaderView"
        @IBOutlet weak var invitationsSectionHeader: UILabel!
        @IBOutlet weak var numberOfPerson: UILabel!
 }

ii)为HeaderView注册自定义集合视图单元

 self.collectionView.register(UINib(nibName: SectionHeaderView.kReuseIdentifier, bundle: nil), forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: SectionHeaderView.kReuseIdentifier)

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)提及自定义标题视图的高度

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSize(width:collectionView.frame.size.width, height:30)
}

答案 6 :(得分:0)

@Tarun的回答对我很有效;我很想念collectionView(_:layout:referenceSizeForHeaderInSection:),因为有时要显示的数据会被排序,而有时却不会。

此外,通过将以下内容添加到UICollectionViewController的viewDidLoad()中,可以将部分标题固定在屏幕顶部(如苹果Apple View Book应用程序的表格视图所示):

if let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout {
    flowLayout.sectionHeadersPinToVisibleBounds = true
}