2

Of course with iOS 10, you now have to do THIS

enter image description here

to use the phone's camera. On first launch, the user gets a question such as,

enter image description here

and all is well.

BUT we have a client app that is a "camera app": when you launch the app it simply immediately launches the camera, when the app is running the camera is running and is shown fullscreen. The code to do so is the usual way, see below.

The problem is - the first launch of the app on a phone, the user is asked the question; user says yes. But then, the camera is just black on devices we have tried. It does not crash (as it would if you forget the plist item) but it goes black and stays black.

If the user quits the app and launches it again - it's fine, everything works.

What the heck is the workflow for a "camera app"? I can't see a good solution, but there must be one for the various camera apps out there - which immediately go to fullscreen camera when you launch the app.

class CameraPlane:UIViewController
    {
    ... 
    func cameraBegin()
        {
        captureSession = AVCaptureSession()
        captureSession!.sessionPreset = AVCaptureSessionPresetPhoto

        let backCamera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)

        var error: NSError?
        var input: AVCaptureDeviceInput!
        do {
            input = try AVCaptureDeviceInput(device: backCamera)
            } catch let error1 as NSError
                {
                error = error1
                input = nil
                }

        if ( error != nil )
            {
            print("probably on simulator? no camera?")
            return;
            }

        if ( captureSession!.canAddInput(input) == false )
            {
            print("capture session problem?")
            return;
            }

        captureSession!.addInput(input)

        stillImageOutput = AVCaptureStillImageOutput()
        stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

        if ( captureSession!.canAddOutput(stillImageOutput) == false )
            {
            print("capture session with stillImageOutput problem?")
            return;
            }

        captureSession!.addOutput(stillImageOutput)
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
                                        // or, AVLayerVideoGravityResizeAspect

        fixConnectionOrientation()

        view.layer.addSublayer(previewLayer!)
        captureSession!.startRunning()
        previewLayer!.frame = view.bounds
        }
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • You should initialise camera after getting the permissions, something like this `AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if(status == AVAuthorizationStatusAuthorized)` else `[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { [self performSelectorOnMainThread:@selector(initializeCamera) withObject:nil waitUntilDone:NO]; }];` – iphonic Oct 03 '16 at 16:31

2 Answers2

4

Note: it's likely OP's code was actually working correctly in terms of the new ISO10 permission string, and OP had another problem causing the black screen.


From the code you've posted, I can't tell why you experience this kind of behavior. I can only give you the code that is working for me.

This code also runs on iOS 9. Note that I am loading the camera in viewDidAppear to make sure that all constraints are set.

import AVFoundation

class ViewController : UIViewController {

    //the view where the camera feed is shown
    @IBOutlet weak var cameraView: UIView!

    var captureSession: AVCaptureSession = {
        let session = AVCaptureSession()
        session.sessionPreset = AVCaptureSessionPresetPhoto
        return session
    }()
    var sessionOutput = AVCaptureStillImageOutput()
    var previewLayer = AVCaptureVideoPreviewLayer()

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

        let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as? [AVCaptureDevice]
        guard let backCamera = (devices?.first {    $0.position == .back    }) else {
            print("The back camera is currently not available")
            return
        }

        do {
            let input = try AVCaptureDeviceInput(device: backCamera)
            if captureSession.canAddInput(input){
                captureSession.addInput(input)
                sessionOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

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

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

                    previewLayer.position = CGPoint(x: cameraView.frame.width / 2, y: cameraView.frame.height / 2)
                    previewLayer.bounds = cameraView.frame
                }
            }
        } catch {
            print("Could not create a AVCaptureSession")
        }
    }
}

If you want the camera to show fullscreen you can simply use view instead of cameraView. In my camera implementation the camera feed does not cover the entire view, there's still some navigation stuff.

Fattie
  • 27,874
  • 70
  • 431
  • 719
productioncoder
  • 4,225
  • 2
  • 39
  • 65
  • hi Slash. Thanks for that great example. Say - you're pretty sure that, if you TOTALLY UNINSTALL the app, and then install using your code approach - it does work? You don't get the "black screen" problem the FIRST time (only) you launch the app? Thanks! – Fattie Oct 03 '16 at 20:19
  • No, I just deleted the app, reinstalled it and when I open it, I don't get a black screen. Seems to work. – productioncoder Oct 03 '16 at 20:20
  • Got it - to be clear, when you do that: of course, iOS asks for the user's permission, right? – Fattie Oct 03 '16 at 20:34
  • Yes it asks for the permission. And if you click yes, you see the camera feed. If you click "No" then the view with the camera feed is black. So you still have to write some code to re-request the camera permissions if the user clicks denies access. – productioncoder Oct 03 '16 at 20:39
  • Brilliant, thank you. Can confirm your code is working great. Of course, it's entirely possible I screwed up something else - and my original code I could have left alone heh! :) BTW it's interesting you don't use sessionPreset (such as `captureSession!.sessionPreset = AVCaptureSessionPresetPhoto`). Do you find you get the "correct shape" no matter what device anyways? Interesting stuff. – Fattie Oct 03 '16 at 20:40
  • I haven't tested it extensively on many different devices. But maybe the image quality gets better if we set the sessionPreset. I've therefore updated the code. Thanks for the suggestion :) – productioncoder Oct 03 '16 at 20:51
  • Fair enough; I'm afraid I don't honestly know if it makes the image quality better or not! :) Anyways thanks again. – Fattie Oct 03 '16 at 20:53
3

What's happening is that on that first launch, you're activating the camera before it can check what the permissions are and display the appropriate UIAlertController. What you'd want to do is include this code inside an if statement to check the status of the camera permissions (AVAuthorizationStatus). Make sure that if it's not allowed to ask for permission before displaying the camera. See this question for more help.

Community
  • 1
  • 1