Files
suixinkan_uikit/suixinkan/UI/Home/HomeCollectionModels.swift

172 lines
6.2 KiB
Swift

//
// HomeCollectionModels.swift
// suixinkan
//
import Foundation
import UIKit
/// 首页 CollectionView 分区标识。
enum HomeCollectionSection: Hashable {
case workStatus
case locationReport
case quickActions
case store
case commonApps
}
/// 首页 CollectionView 条目标识;各区块使用独立 case,避免 Diffable 去重。
enum HomeCollectionItem: Hashable {
case workStatus
case locationReport
case quickActions
case store
case menu(HomeMenuItem)
}
/// 首页 CollectionView 布局与 snapshot 构建工具。
enum HomeCollectionLayoutBuilder {
private static let menuColumns = 3
private static let menuSpacing = AppSpacing.sm
private static let sectionSpacing = AppSpacing.sm
/// 构建 Compositional Layout,按当前分区类型返回 section 布局。
static func makeLayout(sections: [HomeCollectionSection]) -> UICollectionViewCompositionalLayout {
UICollectionViewCompositionalLayout { sectionIndex, environment in
let section = sectionIndex < sections.count ? sections[sectionIndex] : .commonApps
switch section {
case .workStatus:
return fullWidthSection(height: 84)
case .locationReport:
return fullWidthSection(height: 132)
case .quickActions:
return quickActionsSection(environment: environment)
case .store:
return fullWidthSection(height: 96)
case .commonApps:
return menuGridSection(environment: environment)
}
}
}
/// 根据 ViewModel 状态构建 Diffable Snapshot。
static func makeSnapshot(
showWorkBlock: Bool,
showStore: Bool,
storeItem: StoreItem?,
commonMenus: [HomeMenuItem]
) -> NSDiffableDataSourceSnapshot<HomeCollectionSection, HomeCollectionItem> {
var snapshot = NSDiffableDataSourceSnapshot<HomeCollectionSection, HomeCollectionItem>()
if showWorkBlock {
snapshot.appendSections([.workStatus, .locationReport, .quickActions])
snapshot.appendItems([.workStatus], toSection: .workStatus)
snapshot.appendItems([.locationReport], toSection: .locationReport)
snapshot.appendItems([.quickActions], toSection: .quickActions)
}
if showStore, storeItem != nil {
snapshot.appendSections([.store])
snapshot.appendItems([.store], toSection: .store)
}
let menus = commonMenus + [HomeMenuItem.moreFunctions]
snapshot.appendSections([.commonApps])
snapshot.appendItems(menus.map { HomeCollectionItem.menu($0) }, toSection: .commonApps)
return snapshot
}
/// 当前 snapshot 对应的分区顺序,供 layout 使用。
static func sectionOrder(
showWorkBlock: Bool,
showStore: Bool,
storeItem: StoreItem?
) -> [HomeCollectionSection] {
var sections: [HomeCollectionSection] = []
if showWorkBlock {
sections.append(contentsOf: [.workStatus, .locationReport, .quickActions])
}
if showStore, storeItem != nil {
sections.append(.store)
}
sections.append(.commonApps)
return sections
}
private static let horizontalInset = AppSpacing.screenHorizontalInset
private static func fullWidthSection(height: CGFloat) -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(height)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(height)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(
top: 0,
leading: horizontalInset,
bottom: sectionSpacing,
trailing: horizontalInset
)
return section
}
private static func menuGridSection(environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
let itemWidth = menuItemWidth(environment: environment)
let itemSize = NSCollectionLayoutSize(
widthDimension: .absolute(itemWidth),
heightDimension: .absolute(itemWidth)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(itemWidth)
)
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize,
repeatingSubitem: item,
count: menuColumns
)
group.interItemSpacing = .fixed(menuSpacing)
let section = NSCollectionLayoutSection(group: group)
section.interGroupSpacing = menuSpacing
section.contentInsets = NSDirectionalEdgeInsets(
top: 0,
leading: horizontalInset,
bottom: sectionSpacing,
trailing: horizontalInset
)
let headerSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(36)
)
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top
)
section.boundarySupplementaryItems = [header]
return section
}
private static func quickActionsSection(environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
fullWidthSection(height: menuItemWidth(environment: environment))
}
private static func menuItemWidth(environment: NSCollectionLayoutEnvironment) -> CGFloat {
let availableWidth = environment.container.effectiveContentSize.width - horizontalInset * 2
let totalSpacing = menuSpacing * CGFloat(menuColumns - 1)
return max(0, (availableWidth - totalSpacing) / CGFloat(menuColumns))
}
}