在集合视图上创建视图?

3

目前我有一个日历网格系统。 我正在尝试实现一个功能:在日历内显示事件块。 因此,我希望能够实现this,用户可以通过点击单元格在网格系统中的任何位置添加“未命名”块。 我最初的想法是在UICollectionView之外添加视图,但是当我滚动日历时,“未命名”块仍将存在并且仍将显示在屏幕上。 为了使其保持在集合视图流中,我需要在集合布局中添加一个单元格。建立网格系统时,我不得不制作一个自定义的UICollectionViewLayout子类,所以我没有使用UICollectionViewFlowLayout。 我还不太清楚如何在另一个单元格上方添加一个单元格,对于实现这个功能最好的方法有什么想法吗?


一切都在数据源中。在数据源中添加一个属性,然后在单元格类中处理该属性。 - GeneCode
“dataSource”是什么意思?“property”又是什么意思? - Daniel Jones
当你创建CollectionView(或TableView)时,你会有一个数据源对吧?(通常是用来显示数据的数组)。数据源就是模型,你的CollectionView就是视图,而代理中的代码则是控制器。因此,无论你在CollectionView中需要做什么,都需要在数组中进行准备。 - GeneCode
你需要在你的单元格内创建这个视图,并根据数据源(数组)中的标志来显示/隐藏它。 - GeneCode
您可以检查您的UI层次结构,使用单个垂直单元格来表示整个一天,每个“日”单元格都可以包含一个垂直的UIScrollView,在其中您可以更自由地定位您的“事件”块。这种方法的唯二缺点是部分损失单元格回收和需要在所有可见天数上同步垂直滚动。 - Olivier
1个回答

5

我最终自己找到了问题的答案,而且我能够在不使用大量内部滚动视图的情况下完成它。这就是我的做法。由于我已经有了我的网格系统,所以我必须向我的集合视图添加一个额外的部分。这个部分的项目数量取决于我的事件数据源数组,如下所示:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if section == Constants.numberOfSections {
        //custom event section
        return events.count
    }

    //calender grid items
    return Constants.numberOfColumns
}

然而,我为集合视图定制的布局需要更新单元格属性。如果我没有告诉它有一个新单元格添加进来,那么它要么会崩溃,因为找不到相应的网格成员,要么会将该单元格添加到网格中,就像它是网格的另一部分。因此,我必须在自定义布局类中更新单元格属性,然后手动计算单元格应该在布局中放置的位置。所以,基本上每次我添加一个事件单元格时,布局都要手动计算该单元格在网格中的位置。这是我在自定义布局子类中手动计算事件坐标的函数(相关部分在自定义事件注释中):

fileprivate func setCellAttributes(item: Int, section: Int) {
    // Build the UICollectionVieLayoutAttributes for the cell.
    let cellIndex = IndexPath(item: item, section: section)
    var cellWidth: Double = CELL_WIDTH
    var cellHeight: Double = CELL_HEIGHT
    var xPos: Double = 0
    var yPos = Double(section) * CELL_HEIGHT

    if section == collectionView!.numberOfSections - 1 {
        //custom event items
        let rect = getCustomEventRect(item: item)
        xPos = Double(rect.x)
        yPos = Double(rect.y)
        cellHeight = Double(rect.height)
        cellWidth = Double(rect.width)
    } else if item == 0 {
        //the y axis cells
        cellWidth = yAxisCellWidth
    } else {
        //all other cells
        xPos = calculateXPos(item: item)
    }

    let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex)
    cellAttributes.frame = CGRect(x: xPos, y: yPos, width: cellWidth, height: cellHeight)

    // Determine zIndex based on cell type.
    if section == 0 && item == 0 {
        //top left corner cell
        cellAttributes.zIndex = 5
    } else if section == 0 {
        //y axis cells
        cellAttributes.zIndex = 4
    } else if section == collectionView!.numberOfSections - 1 {
        //custom event cells
        cellAttributes.zIndex = 2
    } else if item == 0 {
        //top x axis cells
        cellAttributes.zIndex = 3
    }  else {
        //all background schedule cells
        cellAttributes.zIndex = 1
    }

    // Save the attributes.
    cellAttrsDictionary[cellIndex] = cellAttributes
}

此外,每次我在viewController中更新事件时,都需要更新我的布局中的事件。因此,我在我的viewController中实现了以下内容:

var events: [CustomEvent] = [] {
    didSet {
        if let layout = theCollectionView.collectionViewLayout as? ScheduleCollectionViewLayout {
            layout.events = events
        }
    }
}

当用户点击添加新事件时,我会相应地更新布局:

func removeEventCell(at indexPath: IndexPath) {
    let eventSection: Int = collectionView!.numberOfSections - 1
    let totalEventItems: Int = collectionView!.numberOfItems(inSection: eventSection)

    //decrementing all indexPaths above the deleted event cell, so the attribute dictionary will be up to date, when reloadSections is run by the collectionView.
    for item in 0..<totalEventItems where item > indexPath.item {
        let targetIndexPath = IndexPath(item: item - 1, section: eventSection)
        let cellAttr = cellAttrsDictionary[IndexPath(item: item, section: eventSection)]
        cellAttr?.indexPath = targetIndexPath
        cellAttrsDictionary[targetIndexPath] = cellAttr

    }

    let lastIndexPath = IndexPath(item: totalEventItems - 1, section: eventSection)
    cellAttrsDictionary.removeValue(forKey: lastIndexPath)
}

fileprivate func addEventCellAttributes(numOfEventsToAdd: Int) {
    for num in 1...numOfEventsToAdd {
        setCellAttributes(item: events.count - num, section: collectionView!.numberOfSections - 1)
    }
}

这是一种非常手工的计算位置的过程,但它可以使collectionView平稳运行,并允许在cell上方放置其他cells。我的回答总结是,我为自定义事件添加了一个新的部分,并且必须手动计算这些单元格的位置,而不是让它们随网格系统流动。我的交互式日历现在完美地工作着。如果您有困难甚至创建网格系统,我使用了这个教程:https://www.credera.com/blog/mobile-applications-and-web/building-a-multi-directional-uicollectionview-in-swift/

1
感谢您花时间解释您的解决方案。请点赞! - Nike Kov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接