fix: 代码审查 P2 建议项修复(22 项体验优化)
EditorView (8 项): - 时长警告图标区分:分享警告改用 square.and.arrow.up - coverExtractionTask 竞态防护:新增 isViewActive 守卫 - sensoryFeedback 优化:缩放触觉仅在手势结束时触发 - iPad 右侧面板增加水平内边距 - 预设列表/兼容模式/AI 区域硬编码间距替换为 DesignTokens - 诊断按钮 padding 替换为 DesignTokens - generateButton 补充 accessibilityLabel PresetManager + RecentWorksManager (5 项): - iCloud 合并回写 + 防循环标志位 - iCloud 配额防护(>900KB 跳过写入) - QuotaViolation/AccountChange 事件处理 - EditingPreset 自定义 Codable(decodeIfPresent + 默认值) - RecentWork aspectRatio 枚举化 - 清理 saveToLocalOnly 死代码 ResultView + ProcessingView + HomeView (5 项): - ResultView animateIn 改用 structured concurrency - ProcessingView 阶段数提取为常量 - ProcessingView 脉冲动画去重 - HomeView 删除触觉升级为 .warning - HomeView Cancel 按钮本地化 LivePhotoCore + AppState (4 项): - coverImageURL 参数去重,内部从 exportParams 读取 - progress 回调钳位 min(pct, 1.0) - CropRect 新增 clamped() 归一化校验 - AppState 初始化错误增加 os.Logger 日志 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -82,13 +82,23 @@ public struct CropRect: Codable, Sendable, Hashable {
|
||||
/// 全画幅(不裁剪)
|
||||
public static let full = CropRect()
|
||||
|
||||
/// 返回值域限制在 [0, 1] 范围内的新 CropRect,确保 x+width <= 1, y+height <= 1
|
||||
public func clamped() -> CropRect {
|
||||
let clampedX = min(max(x, 0), 1)
|
||||
let clampedY = min(max(y, 0), 1)
|
||||
let clampedW = min(max(width, 0), 1 - clampedX)
|
||||
let clampedH = min(max(height, 0), 1 - clampedY)
|
||||
return CropRect(x: clampedX, y: clampedY, width: clampedW, height: clampedH)
|
||||
}
|
||||
|
||||
/// 转换为像素坐标
|
||||
public func toPixelRect(videoSize: CGSize) -> CGRect {
|
||||
CGRect(
|
||||
x: x * videoSize.width,
|
||||
y: y * videoSize.height,
|
||||
width: width * videoSize.width,
|
||||
height: height * videoSize.height
|
||||
let safe = clamped()
|
||||
return CGRect(
|
||||
x: safe.x * videoSize.width,
|
||||
y: safe.y * videoSize.height,
|
||||
width: safe.width * videoSize.width,
|
||||
height: safe.height * videoSize.height
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -521,10 +531,10 @@ public actor LivePhotoBuilder {
|
||||
public func buildResources(
|
||||
workId: UUID = UUID(),
|
||||
sourceVideoURL: URL,
|
||||
coverImageURL: URL? = nil,
|
||||
exportParams: ExportParams = ExportParams(),
|
||||
progress: (@Sendable (LivePhotoBuildProgress) -> Void)? = nil
|
||||
) async throws -> LivePhotoBuildOutput {
|
||||
let coverImageURL = exportParams.coverImageURL
|
||||
let assetIdentifier = UUID().uuidString
|
||||
let paths = try cacheManager.makeWorkPaths(workId: workId)
|
||||
|
||||
@@ -1070,7 +1080,7 @@ public actor LivePhotoBuilder {
|
||||
if let sampleBuffer = videoReaderOutput.copyNextSampleBuffer() {
|
||||
currentFrameCount += 1
|
||||
let pct = Double(currentFrameCount) / Double(frameCount)
|
||||
progress(pct)
|
||||
progress(min(pct, 1.0))
|
||||
videoWriterInput.append(sampleBuffer)
|
||||
} else {
|
||||
videoWriterInput.markAsFinished()
|
||||
@@ -1192,14 +1202,12 @@ public actor LivePhotoWorkflow {
|
||||
public func buildSaveValidate(
|
||||
workId: UUID = UUID(),
|
||||
sourceVideoURL: URL,
|
||||
coverImageURL: URL? = nil,
|
||||
exportParams: ExportParams = ExportParams(),
|
||||
progress: (@Sendable (LivePhotoBuildProgress) -> Void)? = nil
|
||||
) async throws -> LivePhotoWorkflowResult {
|
||||
let output = try await builder.buildResources(
|
||||
workId: workId,
|
||||
sourceVideoURL: sourceVideoURL,
|
||||
coverImageURL: coverImageURL,
|
||||
exportParams: exportParams,
|
||||
progress: progress
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user