import SwiftUI import UIKit /// T-iPad-4 · Projects 大屏化的**纯布局决策** —— 列数 / 是否走网格 / sheet /// detents 全部收在本文件的纯函数里(单一判据点,仿 `LayoutPolicy` 先例), /// 视图内零散落条件、100% 单测。 /// /// **为何按 `UIUserInterfaceIdiom` 而非 `horizontalSizeClass`**(关键、易错): /// iPad 上 Projects 以**表单 sheet**(form sheet)呈现,其内部 /// `horizontalSizeClass` 恒为 **compact**(与 iPhone 横屏完全相同)。因此 /// size class 无法在「iPhone 横屏零回归」的前提下区分「iPhone」与「iPad 的 /// Projects 表单 sheet」—— 只有设备 idiom 能。故 Projects 自身的多列/卡片 /// 决策以 idiom+宽度为判据;根视图的 stack/split 决策仍由 `LayoutPolicy` /// (唯一 size-class 读取点)负责,两者正交、各自单点。 enum ProjectsGridLayout { /// iPhone / 极窄 iPad 的回退列数(现有单列布局)。 static let singleColumn = 1 /// iPad 下升到 2 列的最小可用宽度(表单 sheet 内宽 ~440–540pt 即两列)。 static let twoColumnMinWidth: CGFloat = 400 /// iPad 下升到 3 列的最小可用宽度(更宽的面板/横屏全宽时)。 static let threeColumnMinWidth: CGFloat = 760 /// iPhone(`.phone`,任何朝向/尺寸)→ 恒 `singleColumn`(现有单列 List, /// 字节级不变)。iPad(`.pad`)→ 按**可用宽度** 1–3 列。恒 `>= 1`(永不 /// 返回 0,防空网格)。 static func columnCount( availableWidth: CGFloat, idiom: UIUserInterfaceIdiom ) -> Int { guard idiom == .pad else { return singleColumn } if availableWidth >= threeColumnMinWidth { return 3 } if availableWidth >= twoColumnMinWidth { return 2 } return singleColumn } /// 容器选择:iPad → 多列网格;其它(iPhone)→ 现有单列 List(零回归)。 static func usesGrid(idiom: UIUserInterfaceIdiom) -> Bool { idiom == .pad } } /// T-iPad-4 · Projects sheet 的自适应 detents 决策(同一 idiom 判据)。 enum ProjectsSheetSizing { /// iPhone → `nil`:**不套 `.presentationDetents`**,保持现有默认全高 sheet /// (iPhone 字节级不变)。iPad → 卡片式 `[.medium, .large]`:不铺满大屏, /// 仍可上拉到大。 static func detents(idiom: UIUserInterfaceIdiom) -> Set? { idiom == .pad ? [.medium, .large] : nil } } // MARK: - 应用点(把「iPhone 不套、iPad 套 detents」收进一个 modifier) /// 条件套 `.presentationDetents` 的 modifier —— nil 时 `content` 原样透传, /// 故 iPhone 分支不引入任何 sheet 尺寸修饰符(零回归)。 private struct AdaptiveSheetDetents: ViewModifier { let detents: Set? func body(content: Content) -> some View { if let detents { content.presentationDetents(detents) } else { content } } } extension View { /// iPhone 透传(现有全高 sheet,字节级不变);iPad 套卡片式 detents。 func adaptiveProjectsSheetDetents(idiom: UIUserInterfaceIdiom) -> some View { modifier(AdaptiveSheetDetents(detents: ProjectsSheetSizing.detents(idiom: idiom))) } }