fix: 修复裁剪手势状态丢失 + 合并重复预览代码

- 为 MagnificationGesture/DragGesture 添加 .onEnded 处理器,
  新增 lastCropOffset/lastCropScale 累积变量,修复抬手后
  缩放/偏移从初始值重新开始的问题
- 提取 cropPreview(height:dynamicHeight:) 共享方法和
  cropGesture 计算属性,消除 iPhone/iPad 约 60 行重复代码
- resetCropState() 同步重置累积变量

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
empty
2026-02-07 21:07:59 +08:00
parent cfc39c75fc
commit 1556dfd167

View File

@@ -31,6 +31,8 @@ struct EditorView: View {
//
@State private var cropOffset: CGSize = .zero //
@State private var cropScale: CGFloat = 1.0 //
@State private var lastCropOffset: CGSize = .zero //
@State private var lastCropScale: CGFloat = 1.0 //
//
@State private var compatibilityMode: Bool = false
@@ -72,7 +74,7 @@ struct EditorView: View {
private var iPhoneLayout: some View {
ScrollView {
VStack(spacing: 20) {
cropPreviewSection
cropPreview(height: 360)
if let diagnosis = videoDiagnosis, !diagnosis.suggestions.isEmpty {
diagnosisSection(diagnosis: diagnosis)
@@ -97,7 +99,7 @@ struct EditorView: View {
HStack(alignment: .top, spacing: 24) {
//
VStack(spacing: 16) {
iPadCropPreviewSection
cropPreview(height: 500, dynamicHeight: true)
if let diagnosis = videoDiagnosis, !diagnosis.suggestions.isEmpty {
diagnosisSection(diagnosis: diagnosis)
@@ -124,80 +126,50 @@ struct EditorView: View {
.padding(DesignTokens.Spacing.xxl)
}
// MARK: - iPad
@ViewBuilder
private var iPadCropPreviewSection: some View {
GeometryReader { geometry in
let containerWidth = geometry.size.width
let containerHeight = min(500, geometry.size.width * 1.2)
ZStack {
if let player {
VideoPlayer(player: player)
.aspectRatio(videoNaturalSize, contentMode: .fit)
.scaleEffect(cropScale)
.offset(cropOffset)
.gesture(
SimultaneousGesture(
MagnificationGesture()
.onChanged { value in
cropScale = max(1.0, min(3.0, value))
},
DragGesture()
.onChanged { value in
cropOffset = value.translation
}
)
)
} else {
ProgressView()
// MARK: -
private var cropGesture: some Gesture {
SimultaneousGesture(
MagnificationGesture()
.onChanged { value in
cropScale = max(1.0, min(3.0, lastCropScale * value))
}
if selectedAspectRatio != .original {
CropOverlay(
aspectRatio: selectedAspectRatio,
containerSize: CGSize(width: containerWidth, height: containerHeight)
.onEnded { value in
lastCropScale = max(1.0, min(3.0, lastCropScale * value))
},
DragGesture()
.onChanged { value in
cropOffset = CGSize(
width: lastCropOffset.width + value.translation.width,
height: lastCropOffset.height + value.translation.height
)
}
}
.frame(width: containerWidth, height: containerHeight)
.clipShape(RoundedRectangle(cornerRadius: 16))
.background(Color.black.clipShape(RoundedRectangle(cornerRadius: 16)))
}
.frame(height: 500)
.onEnded { value in
lastCropOffset = CGSize(
width: lastCropOffset.width + value.translation.width,
height: lastCropOffset.height + value.translation.height
)
}
)
}
// MARK: -
// MARK: -
@ViewBuilder
private var cropPreviewSection: some View {
private func cropPreview(height: CGFloat, dynamicHeight: Bool = false) -> some View {
GeometryReader { geometry in
let containerWidth = geometry.size.width
let containerHeight: CGFloat = 360
let containerHeight = dynamicHeight ? min(height, geometry.size.width * 1.2) : height
ZStack {
//
if let player {
VideoPlayer(player: player)
.aspectRatio(videoNaturalSize, contentMode: .fit)
.scaleEffect(cropScale)
.offset(cropOffset)
.gesture(
SimultaneousGesture(
MagnificationGesture()
.onChanged { value in
cropScale = max(1.0, min(3.0, value))
},
DragGesture()
.onChanged { value in
cropOffset = value.translation
}
)
)
.gesture(cropGesture)
} else {
ProgressView()
}
//
if selectedAspectRatio != .original {
CropOverlay(
aspectRatio: selectedAspectRatio,
@@ -209,7 +181,7 @@ struct EditorView: View {
.clipShape(RoundedRectangle(cornerRadius: 16))
.background(Color.black.clipShape(RoundedRectangle(cornerRadius: 16)))
}
.frame(height: 360)
.frame(height: height)
}
// MARK: -
@@ -710,6 +682,8 @@ struct EditorView: View {
private func resetCropState() {
cropOffset = .zero
cropScale = 1.0
lastCropOffset = .zero
lastCropScale = 1.0
}
private func calculateCropRect() -> CropRect {