重构首页为单一集合视图并添加全部功能自定义
This commit is contained in:
165
suixinkan/UI/Home/HomeCollectionModels.swift
Normal file
165
suixinkan/UI/Home/HomeCollectionModels.swift
Normal file
@ -0,0 +1,165 @@
|
||||
//
|
||||
// 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: CGFloat = 15
|
||||
private static let menuAspectRatio: CGFloat = 1.3
|
||||
private static let sectionSpacing = AppSpacing.sm
|
||||
|
||||
/// 构建 Compositional Layout,按当前分区类型返回 section 布局。
|
||||
static func makeLayout(sections: [HomeCollectionSection]) -> UICollectionViewCompositionalLayout {
|
||||
UICollectionViewCompositionalLayout { sectionIndex, environment in
|
||||
guard sectionIndex < sections.count else { return nil }
|
||||
switch sections[sectionIndex] {
|
||||
case .workStatus:
|
||||
return fullWidthSection(height: AppSpacing.formRowHeight)
|
||||
case .locationReport:
|
||||
return fullWidthSection(height: 100)
|
||||
case .quickActions:
|
||||
return fullWidthSection(height: 72)
|
||||
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 availableWidth = environment.container.effectiveContentSize.width - horizontalInset * 2
|
||||
let totalSpacing = menuSpacing * CGFloat(menuColumns - 1)
|
||||
let itemWidth = max(0, (availableWidth - totalSpacing) / CGFloat(menuColumns))
|
||||
let itemHeight = itemWidth / menuAspectRatio
|
||||
|
||||
let itemSize = NSCollectionLayoutSize(
|
||||
widthDimension: .absolute(itemWidth),
|
||||
heightDimension: .absolute(itemHeight)
|
||||
)
|
||||
let item = NSCollectionLayoutItem(layoutSize: itemSize)
|
||||
let groupSize = NSCollectionLayoutSize(
|
||||
widthDimension: .fractionalWidth(1),
|
||||
heightDimension: .absolute(itemHeight)
|
||||
)
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user