2
votes

I have a custom camera view in a UIViewController that does not take up the full screen. Beneath it is a collection view that fills up with little thumbnails of images taken with "X's" on then to allow the user to delete them before uploading the kept ones to firebase.

This all works lovely on IPhone 7 sized screens. However on the plus size phones, the preview layer of the custom camera view does not fill the entire UIView that the camera is placed in.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    let deviceSession = AVCaptureDeviceDiscoverySession(deviceTypes:
        [.builtInDuoCamera, .builtInTelephotoCamera, .builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .unspecified)

    for device in (deviceSession?.devices)! {
        if device.position == AVCaptureDevicePosition.back {
            do {
                let input = try AVCaptureDeviceInput(device: device)

                if captureSession.canAddInput(input) {
                    captureSession.addInput(input)

                    if captureSession.canAddOutput(sessionOutput) {
                        captureSession.addOutput(sessionOutput)

                        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
                        previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
                        previewLayer.connection.videoOrientation = .portrait

                        cameraView.layer.addSublayer(previewLayer)
                        cameraView.addSubview(takePhotoButton)

                        previewLayer.position = CGPoint(x: self.view.frame.width/2, y: self.cameraView.frame.height/2)
                        previewLayer.bounds = cameraView.frame

                        captureSession.startRunning()
                    }
                }
            } catch let avError {
                print(avError)
            }
        }
    }
}

Here is where I add the preview layer as a sublayer of the CameraView which is just a UIView added in the interface builder with constraints to allow space in the view for the collection view beneath it.

 previewLayer.position = CGPoint(x: self.cameraView.frame.width/2, y:   self.cameraView.frame.height/2)
 previewLayer.bounds = cameraView.frame

I don't understand why the preview layer added as a sublayer would behave any differently on an Iphone7 than an Iphone 7 Plus?? Especially because you can see on the Iphone 7 Plus, the grey around the preview layer which suggests that the CameraView is being constrained correctly, why would it's frame.width be different.

It seems that on the Plus size phone, the preview layer remains the same size as it would on a regular size phone, while the CameraView itself conforms to the larger phone screen correctly.

here is the View in question in the interface builder

here is the View in question in the interface builder

And this is what seems to happen on a plus size phone

And this is what seems to happen on a plus size phone

1
What if you try to add your subviews in viewDidLayoutSubviews() method of the controller instead of viewWillAppear()? This sounds like an Autolayout related issue. - 0xNSHuman
I will try that, do you suggest moving that entire block of code to viewDidLayoutSubviews()? Unfortunately I don't have a plus size phone, so will need to wait for testflight testers to know if it works. - Math Engel
I suggest you to set your previewLayer.position and previewLayer.bounds in viewDidLayoutSubviews(). Your issue is most likely that at the time viewWillAppear() executed, Autolayout didn't recalculate cameraView.frame for the actual device size, but uses the size you have set in your Storyboard. viewDidLayoutSubviews() method exists just for this purpose — use actual sizes recalculated for this particular device. - 0xNSHuman
You can try to launch it in iPhone 7 Plus simulator, it should display placeholder picture instead of capture preview. - 0xNSHuman
I posted a detailed answer. Check it out and let me know if it happens to solve your problem. - 0xNSHuman

1 Answers

1
votes

TL;DR: Set coordinates and frames of your views and layers in viewDidAppear() method.


At the time viewWillAppear() method executed, all view controller's subviews have frame value that you've set in your Storyboard. What you need is to use updated cameraView.frame value recalculated for the actual device size, to set your previewLayer.position and previewLayer.bounds properly. viewDidLayoutSubviews() method exists just for this purpose — at the time it executed, all views have actual sizes recalculated for the particular device app is running on.

viewDidLayoutSubviews() can be called multiple times though, which might lead to multiple instances of your views/layers created. There is another method in view controller's lifecycle which is called with autolayouted subviews just once — viewDidAppear().

So what you need is to move your previewLayer related code from viewWillAppear() to viewDidAppear().