Skip to content

Commit

Permalink
Enhance orientation handling in InteractivePreview and Preview classes
Browse files Browse the repository at this point in the history
Summary:

Improved the handling of orientation changes in the `InteractivePreview` and `Preview` classes to ensure proper resizing and layout adjustments of the `AVCaptureVideoPreviewLayer` during device rotation. These changes address issues where the preview layer would not correctly adapt to new geometry sizes, leading to misalignment.

Details:

1. `InteractivePreview`:
	- Added an `onChange` modifier to observe changes in the geometry size.
	- Updated the `layer` frame on the main dispatch queue to ensure proper resizing.

2. `Preview`:
	- Removed the direct assignment of `previewLayer.frame` in `updateUIViewController`.
	- Ensured the `previewLayer` is added to the view controller’s layer only if it hasn’t been added already.
	- Updated the `previewLayer.frame` on the main dispatch queue to ensure it resizes correctly when the view bounds change.

NOTE:

This will not affect the orientation of the session. To do that, application code must wrap the preview layer with handlers that call `aespaSession.common(.orientation(orientation: ...))`. I have some code that demonstrates this, but it's very specific to my app, so I couldn't cleanly extract a good example for the README. I can try to tackle that in a future commit.
  • Loading branch information
subelsky authored and enebin committed Jun 3, 2024
1 parent 88e3024 commit bde72fc
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 3 deletions.
5 changes: 5 additions & 0 deletions Sources/Aespa/View/InteractivePreview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ public struct InteractivePreview: View {
.opacity(focusFrameOpacity)
.animation(.spring(), value: focusFrameOpacity)
}
.onChange(of: geometry.size) { newSize in
DispatchQueue.main.async {
layer.frame = CGRect(origin: .zero, size: newSize)
}
}
}
}
}
Expand Down
9 changes: 6 additions & 3 deletions Sources/Aespa/View/Preview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ struct Preview: UIViewControllerRepresentable {

func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
previewLayer.videoGravity = gravity
uiViewController.view.layer.addSublayer(previewLayer)

previewLayer.frame = uiViewController.view.bounds
if previewLayer.superlayer == nil {
uiViewController.view.layer.addSublayer(previewLayer)
}
DispatchQueue.main.async {
self.previewLayer.frame = uiViewController.view.bounds
}
}

func dismantleUIViewController(_ uiViewController: UIViewController, coordinator: ()) {
Expand Down

3 comments on commit bde72fc

@derwaldgeist
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @subelsky I am facing a couple of orientation issues in my app (see #54 and #55). I now saw your comment about the orientation of the session. Could you maybe share how the orientation has to be handled by the surrounding view? Maybe this will help in my case. (I even implemented logic that auto-rotates images based off gyroscope data, but then realized that Aespa behaves differently on iPads as well).

@subelsky
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure thing! this is omitting some unrelated details, and probably not how I would design it from scratch, but it roughly evolved to look like this:

// CaptureImageView
GeometryReader { geometry in

                    InteractivePreviewWrapper(preview: viewModel.preview as! InteractivePreview)
                        .frame(width: geometry.size.width, height: geometry.size.height)
                        .edgesIgnoringSafeArea(.all)
                        .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
                            self.deviceOrientation = UIDevice.current.orientation
                            self.viewModel.updateOrientation(to: self.avCaptureVideoOrientation(for: self.deviceOrientation))
                        }
struct InteractivePreviewWrapper: View {
    let preview: InteractivePreview

    var body: some View {
        GeometryReader { geometry in
            preview
                .frame(width: geometry.size.width, height: geometry.size.height)
        }
    }
}
import Aespa
import AVFoundation

class CaptureImageViewModel: ObservableObject, ErrorableViewModel {
    var preview: some View {
        aespaSession.interactivePreview()
    }

    var aespaSession: AespaSession {
        AespaSessionManager.shared.aespaSession
    }

   // omitted lots of stuff unrelated to Aespa

    func updateOrientation(to orientation: AVCaptureVideoOrientation) {
        aespaSession.common(.orientation(orientation: orientation))
    }

@derwaldgeist
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Please sign in to comment.