#5 fix camera capture preview after shooting
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s

- Dismiss the camera fullScreenCover through the SwiftUI binding
  synchronously in the picker delegate before background file
  preparation, so UIImagePickerController no longer dismisses itself
  and desynchronizes the presentation state that the preview sheet
  waits on.
- Keep the archive-sync suspension reserved across the dismissal and
  surface an explicit error when the captured file fails to prepare.
- Add verify.sh invariants for the camera dismissal flow.
This commit is contained in:
wt committed 2026-08-29 02:29:29 +07:00
1 parent b54113f0e4
commit 5790f9b2f6
3 files changed
+44 -6

No files matched your search

+16 -1
View File
@@ -12,6 +12,13 @@ import UIKit
@MainActor
struct SystemPhotoCameraView: UIViewControllerRepresentable {
/// Called synchronously on the main thread inside
/// `didFinishPickingMediaWithInfo`, before any background preparation.
/// The host must flip its `isPresented` binding here so SwiftUI drives
/// the dismissal; letting UIImagePickerController dismiss itself leaves
/// the fullScreenCover state desynchronized and breaks the follow-up
/// preview presentation.
let onDismissRequest: () -> Void
let onMedia: (Result<CapturedCameraMedia, Error>) -> Void
let onCancel: () -> Void
@@ -20,7 +27,11 @@ struct SystemPhotoCameraView: UIViewControllerRepresentable {
}
func makeCoordinator() -> Coordinator {
Coordinator(onMedia: onMedia, onCancel: onCancel)
Coordinator(
onDismissRequest: onDismissRequest,
onMedia: onMedia,
onCancel: onCancel
)
}
func makeUIViewController(context: Context) -> UIImagePickerController {
@@ -42,14 +53,17 @@ struct SystemPhotoCameraView: UIViewControllerRepresentable {
) {}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
private let onDismissRequest: () -> Void
private let onMedia: (Result<CapturedCameraMedia, Error>) -> Void
private let onCancel: () -> Void
private var completed = false
init(
onDismissRequest: @escaping () -> Void,
onMedia: @escaping (Result<CapturedCameraMedia, Error>) -> Void,
onCancel: @escaping () -> Void
) {
self.onDismissRequest = onDismissRequest
self.onMedia = onMedia
self.onCancel = onCancel
}
@@ -60,6 +74,7 @@ struct SystemPhotoCameraView: UIViewControllerRepresentable {
) {
guard !completed else { return }
completed = true
onDismissRequest()
let mediaType = (info[.mediaType] as? String).flatMap { UTType($0) }
if mediaType?.conforms(to: .movie) == true,