如何使用 UICollectionView 在单元格左侧创建部分以及标题?

Posted

技术标签:

【中文标题】如何使用 UICollectionView 在单元格左侧创建部分以及标题?【英文标题】:How to create sections along with headers on the left of cells with UICollectionView? 【发布时间】:2016-04-29 05:33:51 【问题描述】:

我不确定我的问题是否写得很好。如果您考虑改进它,那就去做吧:) 但我的意思是:

每个橙色框都是新节框的标题(宽度和高度是静态的)。 这将链接到带有部分的获取结果控制器 黄色单元格始终占据屏幕其余部分的宽度,并具有自动高度标注(取决于文本长度)。

请告诉我:

    使用UICollectionView 是否可以做到这一点? 如何为黄色单元格设置自动高度尺寸? 如何像在图像上一样浮动黄色

【问题讨论】:

【参考方案1】:

我认为你可以使用 UITableViewController 来做类似的事情。

这个想法是:

将单元格自定义为左右两部分。 section第一项的左边用于section header,注意CellViewContentViewClip Subviews不要勾选。 section最后一项的高度需要调整,不重叠。

这里是示例代码:

class ViewController: UITableViewController 

    struct Item 
        var height: CGFloat = 0
    

    var itemDic: [Int: [Item]]!

    override func viewDidLoad() 
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        itemDic = [0 : [Item(height: 10), Item(height: 30), Item(height: 50)],
                 1 : [Item(height: 20), Item(height: 10)],
                 2 : [Item(height: 40), Item(height: 30), Item(height: 20)]]
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    


    override func numberOfSectionsInTableView(tableView: UITableView) -> Int 
        return itemDic.count
    

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return itemDic[section]!.count
    

    let sectionHeaderHeight: CGFloat = 100

    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 

        let section = indexPath.section
        let row = indexPath.row
        let items = itemDic[section]!

        var height = items[row].height
        if (row == items.count - 1) 

            var total: CGFloat = 0
            for i in items 
                total += i.height + 10
            

            if(sectionHeaderHeight + 10 > total)
                height += (sectionHeaderHeight + 10 - total)
            
        

        return height + 10
    

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 

        let section = indexPath.section
        let row = indexPath.row
        let items = itemDic[section]!

        let cell = tableView.dequeueReusableCellWithIdentifier("Cell")!

        let orangeView = cell.viewWithTag(100)!
        let yellowView = cell.viewWithTag(101)!

        if(row == 0)
            let orangeFrame = orangeView.frame
            orangeView.frame = CGRect(origin: orangeFrame.origin, size: CGSize(width: orangeFrame.width, height: sectionHeaderHeight))
        

        let yellowFrame = yellowView.frame
        let height = items[row].height
        yellowView.frame = CGRect(origin: yellowFrame.origin, size: CGSize(width: yellowFrame.width, height: height))

        return cell
    

    override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 
        let orangeView = cell.viewWithTag(100)!
        let yellowView = cell.viewWithTag(101)!

        let orangeFrame = orangeView.frame

        print(orangeFrame)

        let yellowFrame = yellowView.frame

        print(yellowFrame)
    

更新: 其实上面的解决方案有一个bug,当滚动到顶部直到第一部分消失,然后你会看到它被重新渲染并且布局会被破坏。

有更好的方法来做到这一点。

在 headerView 中添加一个内部视图(橙色),它将在 tableCells 上呈现。记得设置 header > 0 的高度。在这种情况下,单元格只需要包含正确的部分(黄色)。 不过,您需要调整最后一项的高度。

示例代码:

class ViewController: UITableViewController 

    struct Item 
        var height: CGFloat = 0
    

    var itemDic: [Int: [Item]]!

    override func viewDidLoad() 
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        itemDic = [0 : [Item(height: 10), Item(height: 30), Item(height: 50), Item(height: 20)],
                 1 : [Item(height: 20), Item(height: 10)],
                 2 : [Item(height: 40), Item(height: 30), Item(height: 20), Item(height: 30)]]
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    


    override func numberOfSectionsInTableView(tableView: UITableView) -> Int 
        return itemDic.count
    

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return itemDic[section]!.count
    

    let sectionHeaderHeight: CGFloat = 100
    let innerViewOffset: CGFloat = 10
    override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat 

        // Must > 0
        return innerViewOffset
    

    override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? 
        let view = UIView(frame: CGRect())

        view.backgroundColor = UIColor.blueColor()

        let innerView = UIView(frame: CGRect(x: 0, y: innerViewOffset, width: 100, height: 100))
        innerView.backgroundColor = colorForSection(section)
        view.addSubview(innerView)

        return view
    

    func colorForSection(section: Int) -> UIColor 
        let colors = [UIColor.greenColor(), UIColor.redColor(), UIColor.purpleColor()]

        return colors[section]
    

    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 

        let section = indexPath.section
        let row = indexPath.row
        let items = itemDic[section]!

        var height = items[row].height
        if (row == items.count - 1) 

            var total: CGFloat = 0
            for i in items 
                total += i.height + 10
            

            if(sectionHeaderHeight + 10 > total)
                height += (sectionHeaderHeight + 10 - total)
            
        

        return height + 10
    

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 

        let cell = tableView.dequeueReusableCellWithIdentifier("Cell")!

        return cell
    

【讨论】:

如果我理解得很好......当我尝试估计部分中单元格的高度以确定部分中最后一个单元格的高度时,我会遇到唯一的麻烦。我说的对吗? 实际上这种方式有一个我不知道如何处理的错误,当滚动到顶部时,您会看到第一部分被重新渲染并且第一项被覆盖,这不是你的想。我想出一个更好的解决方案给你。查看编辑。【参考方案2】:

我不认为你可以做那个部分的标题。但是你可以用 collectionview 单元格来做。相应地调整其他单元格的大小。

【讨论】:

使用 fetched results controller 会不太舒服。

以上是关于如何使用 UICollectionView 在单元格左侧创建部分以及标题?的主要内容,如果未能解决你的问题,请参考以下文章

如何快速在uicollectionview中动态访问单元格属性

如何使用 UICollectionView 在单元格左侧创建部分以及标题?

如何使 UICollectionView 单元格可移动?

如何使用 UICollectionView 的 selectItem 在 collectionView 单元格上绘图?

如何在选择时将 UICollectionView 单元格扩展到屏幕的宽度?

如何禁用 UICollectionView 部分之间的单元格拖动?