172 lines
6.2 KiB
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))
|
|
}
|
|
}
|