1

I'm trying to develop an app with custom camera where the user can add filters or sticker (like in TextCamera app) and share in social feed. But I found my first problem.

I show the preview to the user with AVCaptureVideoPreviewLayer, take the photo and pass it to another view controller in a UiImageView but the second picture is bigger than first one.

I tried to resize the picture with this function:

    func resize(image: UIImage) -> UIImage {
       let size = image.size
       let newWidth = CGFloat(size.width)
       let newHeight = CGFloat(size.height - blackBottomTab.bounds.size.height)

       let newSize = CGSizeMake(newWidth,newHeight)
       let rect = CGRectMake(0, 0, newSize.width, newSize.height)

       UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
       image.drawInRect(rect)
       let newImage = UIGraphicsGetImageFromCurrentImageContext()
       UIGraphicsEndImageContext()

       return newImage
}

In this function I subtract the height of the black view (under the button) from the image height. But the result that I have is different (see the photo attached).

This is my preview with a black view under the button

This is the photo taken larger than preview one

I also tried to use Aspect Fit in Storyboard Image View of the second View Controller but the result is the same.

Where is my error? Thank you to everyone that help me!

Marco Nori
  • 11
  • 3

2 Answers2

0

I think that the AVCaptureVideoPreviewLayer frame is the same as the screen's frame (UIScreen.mainScreen().bounds), and you added the "Shoot Photo" black view on top of it. Instead, you should change the frame of the AVCaptureVideoPreviewLayer.

Your case (what I think):

enter image description here

Assuming that the green rectangle is the AVCaptureVideoPreviewLayer frame and the red one is the black view frame. So, it covers (on top) of the green rectangle.

Make them look like this:

enter image description here

Hope that helped.

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
  • is this the solution? :) if it's not, comment please – Ahmad F Oct 07 '16 at 11:35
  • No Ahmad, I also have this configuration in my storyboard. :( – Marco Nori Oct 09 '16 at 12:45
  • can you please provide the code that show how you initialize and setup the AVCaptureVideoPreviewLayer? – Ahmad F Oct 09 '16 at 12:55
  • func setupPreview() { // Configure previewLayer previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) previewLayer.frame = cameraPreview.bounds previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill cameraPreview.layer.addSublayer(previewLayer) } – Marco Nori Oct 09 '16 at 13:38
  • So, the cameraPreview.bounds is not equals to the screen size right? where do you call "setupPreview()"? in viewDidLoad? – Ahmad F Oct 09 '16 at 14:13
  • Yes and yes. Thank you for your help Ahmad! – Marco Nori Oct 09 '16 at 14:39
  • can you please try to call "setupPreview()" in viewDidAppear instead of viewDidLoad? you can discard this change even if it was a solution, I just wanna check something :) – Ahmad F Oct 09 '16 at 14:43
0

I had to solve a similar problem. As the question notes, there does not appear to be an easy way to detect the size of the video preview.

My solution is hinted at at the end of the answer to https://stackoverflow.com/a/57996039/10449843 which explains in detail how I take the snapshot and create the combined snapshot with the sticker.

Here is an elaboration of that hint.

While I use AVCapturePhotoCaptureDelegate to take the snapshot, I also use AVCaptureVideoDataOutputSampleBufferDelegate to sample the buffer once when the preview is first shown to detect the proportions of the snapshot.

            // The preview layer is displayed with aspect fill
            let previewLayer = AVCaptureVideoPreviewLayer(session: session)
            previewLayer.videoGravity = .resizeAspect
            previewLayer.frame = self.cameraView.bounds

Detect the size of the preview:

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        // Only need to do this once
        guard self.sampleVideoSize == true else {
            return
        }

        guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
            return
        }

        // Sample the size of the preview layer and then store it
        DispatchQueue.main.async {

            let width = CGFloat(CVPixelBufferGetWidth(pixelBuffer))
            let height = CGFloat(CVPixelBufferGetHeight(pixelBuffer))

            // Store the video size in a local variable
            // We don't care which side is which, we just need the
            // picture ratio to decide how to align it on different
            // screens.
            self.videoSize = CGSize.init(width: width, height: height)

            // Now we can set up filters and stickers etc
            ...

            // And we don't need to sample the size again
            self.sampleVideoSize = false
        }
        return
    }
Pacificana
  • 116
  • 9
  • Interesting but this one refers to another answer which refers as well to another answer... Quite lost as the corresponding questions have multiples answers well rated... My problem is that I have a square (1:1) AVCaptureVideoPreviewLayer and the picture I get is 1080x1920 but I just want a square crop of this image (only what the user was seeing) – iOS Flow Sep 03 '20 at 06:46