0

我的 UICollectionView 有一个组合布局。这是创建布局的代码。

func createLayout() -> UICollectionViewLayout {
    let layout = UICollectionViewCompositionalLayout { [weak self] section, _ -> NSCollectionLayoutSection? in
        guard 
            let self = self,
            let sections = self.viewModel?.sections,
            let sectionData = sections[safe: section] else { return nil }

            switch sectionData {
            case .firstSection:
                return self.createFirstSectionSection()
            case .secondSection:
                return self.createSecondSection()
            case .buttons(_, let topSpacing):
                return self.createButtonsSection(topSpacing: topSpacing)
            }
        }

        let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                heightDimension: .estimated(108))

        let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize,
                                                                 elementKind: "header",
                                                                 alignment: .top)

        let config = UICollectionViewCompositionalLayoutConfiguration()
        config.boundarySupplementaryItems = [header]
        config.scrollDirection = .vertical
        config.interSectionSpacing = 0

        layout.configuration = config

        return layout
    }

    func createFirstSection() -> NSCollectionLayoutSection {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(144))

        let item = NSCollectionLayoutItem(layoutSize: itemSize, supplementaryItems: [borderItem])
        let group = NSCollectionLayoutGroup.vertical(layoutSize: itemSize, subitems: [item])

        group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 60, bottom: 0, trailing: 20)

        let layoutSection = NSCollectionLayoutSection(group: group)

        return layoutSection
    }

    func createSecondSection() -> NSCollectionLayoutSection {
        let borderItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
        let borderItem  = NSCollectionLayoutSupplementaryItem(layoutSize: borderItemSize, elementKind: "item-border-view", containerAnchor: NSCollectionLayoutAnchor(edges: .top))

        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(58))

        let item = NSCollectionLayoutItem(layoutSize: itemSize, supplementaryItems: [borderItem])
        let group = NSCollectionLayoutGroup.vertical(layoutSize: itemSize, subitems: [item])

        group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: hasCheckboxes ? 20 : 60, bottom: 0, trailing: 20)

        let layoutSection = NSCollectionLayoutSection(group: group)

        return layoutSection
    }

    func createButtonsSection(topSpacing: CGFloat) -> NSCollectionLayoutSection {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(41))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)

        let group = NSCollectionLayoutGroup.vertical(layoutSize: NSCollectionLayoutSize(widthDimension: itemSize.widthDimension, heightDimension: itemSize.heightDimension), subitems: [item])

        let section = NSCollectionLayoutSection(group: group)
        section.contentInsets = NSDirectionalEdgeInsets(top: topSpacing, leading: 60, bottom: 0, trailing: 20)

        return section
    }

我的模型如下所示:

enum Section {
    case firstSection(items: [FirstSectionItem])
    case secondSection(items: [SecondSectionItem])
    case buttons(cellViewModel: ButtonsCellViewModel, topSpacing: CGFloat)

    var items: [AnyHashable] {
        switch self {
        case .firstSection(let firstSectionItems):
            return firstSectionItems
        case .quotes(let secondSectionItems):
            return secondSectionItems
        case .buttons(let cellViewModel, _):
            return [cellViewModel]
        }
    }
}

// MARK: - Hashable

extension Section: Hashable {

    static func == (lhs: Section, rhs: Section) -> Bool {
        switch (lhs, rhs) {
        case (.firstSection(let leftItems), .firstSection(let rightItems)):
            return leftItems == rightItems
        case (.secondSection(let leftItems), .secondSection(let rightItems)):
            return leftItems == rightItems
        case (.buttons(let leftCellViewModel, let leftTopSpacing), .buttons(let rightCellViewModel, let rightTopSpacing)):
            return true
        default:
            return false
        }
    }

    func hash(into hasher: inout Hasher) {
        switch self {
        case .firstSection(let items):
            hasher.combine(items)
        case .secondSection(let items):
            hasher.combine(items)
        case .buttons(let cellViewModel, let topSpacing):
            hasher.combine("Same") // I use this to make sure that there is no difference in the buttons section. What I try to accomplish is that the buttons section (section at the bottom) does not animate out of screen to reload it's UI.
        }
    }
}

数据模型要复杂得多,但为了这个问题,我删除了一些我认为在这里不相关的东西,只会造成混乱。

使用 DiffableDataSource 重新加载 collectionView 如下所示:

func refreshUI() {
    guard let viewModel = viewModel else { return }

    let newDataSource = WorkApprovalDataSource(sections: viewModel.sections)

    var snapshot = NSDiffableDataSourceSnapshot<APIWorkApprovalSection, AnyHashable>()

    newDataSource.sections.forEach {
        snapshot.appendSections([$0])
        snapshot.appendItems($0.items, toSection: $0)
    }

    dataSource?.apply(snapshot, animatingDifferences: true)
}

关键是,我想要屏幕上的 3 个部分:

  • 第一部分:一些行/项目在彼此下方
  • 第二部分:与第一部分类似,但行/项目是可选的
  • 第三部分:按钮部分。此部分始终存在。它至少有一个按钮。此部分始终包含 1 个单元格:该单元格包含带有按钮的水平堆栈视图。但是,正如我所说,至少总是有 1 个按钮。通过选中/取消选中第 2 节中的行/项目,按钮部分中有一个额外的按钮。当没有选择行时——>没有额外的按钮。当检查额外的行时,按钮的标题会根据所选/检查的行数发生变化:例如-->“发送(5)”。当仅选中 4 行时,此标题需要更改为“发送 (4)”。如果未选择任何行,则应隐藏此按钮。

从一开始就重新加载这些部分,我就遇到了麻烦。它上下跳跃。检查第2节的行时,de按钮部分不可见,因为第2节的项目列表太大,例如第一次检查/选择一行,它会跳转。之后,如果按钮部分仍然不在屏幕上,选择和取消选择行没有问题,不会发生跳转。

但是:当我滚动到底部以使按钮部分可见,然后选择一行时,集合视图会滚动一点,以使按钮看不见。当我再次滚动视线中的按钮时,单元格中的数据看起来很好,因此重新加载“正确”发生。我想要的是按钮部分不会滚动出屏幕来重新加载 UI。我通过使 Hashable 协议始终对相同的文本进行哈希处理来处理这个问题,所以没有区别,对吧?按钮标题的变化及其可见性,我通过按钮的 cellViewModel 处理。所以这工作得很好。但是按钮一直滚动到看不见的地方以重新加载。而且我不知道是什么原因造成的。

我真的需要装饰物品和东西的组合布局,所以我不能放弃这个。

提前感谢您查看并发布一些建议/修复。

4

1 回答 1

0

我终于弄明白了。您必须将垂直滚动项的布局组的尺寸设置为,horizontal而不是vertical. 我已经阅读了几十篇没有提到这一点的教程,甚至在 Apple 文档中也没有。我在这个愚蠢的滚动事情上浪费了几天,甚至几周。所以结论:Diffable DataSources 工作得很好,它是以错误的方式配置的组合布局。

因此,不要为垂直滚动列表执行此操作:

let group = vertical(layoutSize: itemSize, subitems: [item])

你必须这样做:

let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemSize, subitems: [item])

于 2021-12-13T15:52:16.267 回答