0

I'm new to xcode, and I'm trying to make an app where you log in and access a camera, where you can adjust manual controls.

There's a login/signup view controller and it crashes at the segue to the second view controller for the camera and manual controls (the code is mostly based off the Apple sample code for this): when run on an iPhone it then gives an error * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key durationLabel.'*

I've checked the storyboard and connections and all the outlets and actions seem fine. I deleted and made new connections for whatever outlet the error said was not "coding compliant", but it keeps giving this error for a new outlet (it was "isoLabel before, now its "durationLabel"). What exactly is causing this? Sorry for formatting errors. Picture attached as well. Thanks for any help

import UIKit
import FirebaseAuth
import AVFoundation

private var CapturingStillImageContext = 0 //### iOS < 10.0=
private var LensPositionContext = 0
private var ExposureDurationContext = 0
private var ISOContext = 0
private var DeviceWhiteBalanceGainsContext = 0
private enum AVCamManualSetupResult: Int {
    case success
    case cameraNotAuthorized
    case sessionConfigurationFailed
}

extension AVCapturePhotoOutput: AVCapturePhotoOutputType {}
@objc(ViewController)
class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {

    @IBOutlet weak var manualHUD: UIView!

    @IBOutlet weak var focusHUD: UIView!
    @IBOutlet weak var positionLabel: UILabel!
    @IBOutlet weak var focusPositionSlider: UISlider!
    @IBOutlet weak var focusPositionValue: UILabel!


    @IBOutlet weak var exposureHUD: UIView!
    @IBOutlet weak var durationLabel: UILabel!
    @IBOutlet weak var exposureDurationSlider: UISlider!
    @IBOutlet weak var exposureDurationValue: UILabel!
    @IBOutlet weak var isoLabel: UILabel!

    @IBOutlet weak var exposureISOSlider: UISlider!
    @IBOutlet weak var exposureISOValue: UILabel!


    @IBOutlet weak var whitebalanceHUD: UIView!
    @IBOutlet weak var temperatureLabel: UILabel!
    @IBOutlet weak var whitebalanceTemperatureSlider: UISlider!
    @IBOutlet weak var temperatureValue: UILabel!
    @IBOutlet weak var tintLabel: UILabel!
    @IBOutlet weak var whitebalanceTintSlider: UISlider!
    @IBOutlet weak var tintValue: UILabel!



    @IBOutlet weak var previewView1: AVCamManualPreviewView!
    @IBOutlet weak var cameraButton: RoundButton!

    @IBOutlet weak var whiteImage: UIImageView!

    @IBOutlet weak var logOut: UIButton!

    @IBOutlet weak var switchSetting: UISegmentedControl!

    private let kExposureDurationPower = 5.0 

    private let kExposureMinimumDuration = 1.0/1000 

    // Utilities.
    private var setupResult: AVCamManualSetupResult = .success
    private var isSessionRunning: Bool = false
    private var backgroundRecordingID: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
    let device1 = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)

    var timer = Timer()

    dynamic var session: AVCaptureSession!
    dynamic var stillImageOutput: AVCapturePhotoOutput?
    var previewViewLayer: AVCaptureVideoPreviewLayer?
    var sessionQueue: DispatchQueue!


    override func viewDidLoad() {
        super.viewDidLoad()

        // Create the AVCaptureSession.

        self.session = AVCaptureSession()

        // Create a device discovery session

        // Setup the preview view.


        self.previewView1.session = self.session

        // Communicate with the session and other session objects on this queue.
        self.sessionQueue = DispatchQueue(label: "session queue", attributes: [])

        self.setupResult = .success


        switch AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) {
        case .authorized:
            // The user has previously granted access to the camera.
            break
        case .notDetermined:
            // The user has not yet been presented with the option to grant video access.
            // We suspend the session queue to delay session running until the access request has completed.

            self.sessionQueue.suspend()
            AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) {granted in
                if !granted {
                    self.setupResult = .cameraNotAuthorized
                }
                self.sessionQueue.resume()
            }
        default:
            // The user has previously denied access.
            self.setupResult = .cameraNotAuthorized
        }

        self.sessionQueue.async {
            self.configureSession()
        }

        self.focusHUD.isHidden = true
        self.exposureHUD.isHidden = true
        self.whitebalanceHUD.isHidden = true

        print(Auth.auth().currentUser?.email! as Any)
        session = AVCaptureSession()
        session!.sessionPreset = AVCaptureSessionPresetPhoto
        stillImageOutput = AVCapturePhotoOutput()

        let backCamera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
        if let input = try? AVCaptureDeviceInput(device: backCamera) {
            if (session?.canAddInput(input))! {
                session?.addInput(input)
                if (session?.canAddOutput(stillImageOutput))! {
                    session?.addOutput(stillImageOutput!)

                    previewViewLayer = AVCaptureVideoPreviewLayer(session: session)
                    previewViewLayer?.frame = previewView1.bounds
                    previewView1.layer.addSublayer(previewViewLayer!)
                    session?.startRunning()
                }
            } else {
                print("Error : captureSesssion.canAddInput")
            }
        } else {
            print("Unknown Error")
        }
}

private func addObservers() {
            self.addObserver(self, forKeyPath: "device1.lensPosition", options: .new, context: &LensPositionContext)
            self.addObserver(self, forKeyPath: "device1.exposureDuration", options: .new, context: &ExposureDurationContext)
            self.addObserver(self, forKeyPath: "device1.ISO", options: .new, context: &ISOContext)
            self.addObserver(self, forKeyPath: "device1.deviceWhiteBalanceGains", options: .new, context: &DeviceWhiteBalanceGainsContext)

            if #available(iOS 10.0, *) {
            } else {
                self.addObserver(self, forKeyPath: "stillImageOutput.capturingStillImage", options: .new, context: &CapturingStillImageContext)
            }

        }

private func removeObservers() {
            NotificationCenter.default.removeObserver(self)

            self.removeObserver(self, forKeyPath: "device1.lensPosition", context: &LensPositionContext)
            self.removeObserver(self, forKeyPath: "device1.exposureDuration", context: &ExposureDurationContext)
            self.removeObserver(self, forKeyPath: "device1.ISO", context: &ISOContext)
            self.removeObserver(self, forKeyPath: "device1.deviceWhiteBalanceGains", context: &DeviceWhiteBalanceGainsContext)

            if #available(iOS 10.0, *) {
            } else {
                self.removeObserver(self, forKeyPath: "stillImageOutput.capturingStillImage", context: &CapturingStillImageContext)
            }
        }

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

        self.sessionQueue.async {
            switch self.setupResult {
            case .success:
                // Only setup observers and start the session running if setup succeeded.
                self.addObservers()
                self.session?.startRunning()
                self.isSessionRunning = (self.session?.isRunning)!
            case .cameraNotAuthorized:
                DispatchQueue.main.async {
                    let message = NSLocalizedString("AVCamManual doesn't have permission to use the camera, please change privacy settings", comment: "Alert message when the user has denied access to the camera" )
                    let alertController = UIAlertController(title: "AVCamManual", message: message, preferredStyle: .alert)
                    let cancelAction = UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil)
                    alertController.addAction(cancelAction)
                    // Provide quick access to Settings.
                    let settingsAction = UIAlertAction(title: NSLocalizedString("Settings", comment: "Alert button to open Settings"), style: .default) {action in
                        if #available(iOS 10.0, *) {
                            UIApplication.shared.open(URL(string: UIApplicationOpenSettingsURLString)!)
                        } else {
                            UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
                        }
                    }
                    alertController.addAction(settingsAction)
                    self.present(alertController, animated: true, completion: nil)
                }
            case .sessionConfigurationFailed:
                DispatchQueue.main.async {
                    let message = NSLocalizedString("Unable to capture media", comment: "Alert message when something goes wrong during capture session configuration")
                    let alertController = UIAlertController(title: "AVCamManual", message: message, preferredStyle: .alert)
                    let cancelAction = UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil)
                    alertController.addAction(cancelAction)
                    self.present(alertController, animated: true, completion: nil)
                }
            }
        }
    }
    override func viewDidDisappear(_ animated: Bool) {
        self.sessionQueue.async {
            if self.setupResult == .success {
                self.session?.stopRunning()
                self.removeObservers()
            }
        }

        super.viewDidDisappear(animated)
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)

        let deviceOrientation = UIDevice.current.orientation

        if UIDeviceOrientationIsPortrait(deviceOrientation) || UIDeviceOrientationIsLandscape(deviceOrientation) {
            let previewLayer = self.previewView1.layer as! AVCaptureVideoPreviewLayer
            previewLayer.connection.videoOrientation = AVCaptureVideoOrientation(rawValue: deviceOrientation.rawValue)!
        }
    }

    override var prefersStatusBarHidden : Bool {
        return true
    }



@IBAction func changeHUD(_ sender: Any) {

    if switchSetting.selectedSegmentIndex == 0 {
        self.focusPositionSlider.isEnabled = true
        self.focusPositionSlider.isHidden = false
        self.focusHUD.isHidden = false
        self.exposureHUD.isHidden = true
        self.whitebalanceHUD.isHidden = true


    }
    if switchSetting.selectedSegmentIndex == 1 {
        self.exposureDurationSlider.isEnabled = true
        self.exposureDurationSlider.isHidden = false
        self.exposureISOSlider.isEnabled = true
        self.exposureISOSlider.isHidden = false
        self.exposureHUD.isHidden = false
        self.focusHUD.isHidden = true
        self.whitebalanceHUD.isHidden = true


    }
    else {
        self.whitebalanceTemperatureSlider.isEnabled = true
        self.whitebalanceTemperatureSlider.isHidden = false
        self.whitebalanceTintSlider.isEnabled = true
        self.whitebalanceTintSlider.isHidden = false
        self.focusHUD.isHidden = true
        self.exposureHUD.isHidden = true
        self.whitebalanceHUD.isHidden = false

    }

}

private func configureManualHUD() {
        // Manual focus controls


        self.focusPositionSlider.minimumValue = 0.0
        self.focusPositionSlider.maximumValue = 1.0
        self.focusPositionSlider.value = self.device1?.lensPosition ?? 0
// Use 0-1 as the slider range and do a non-linear mapping from the slider value to the actual device exposure duration
        self.exposureDurationSlider.minimumValue = 0
        self.exposureDurationSlider.maximumValue = 1
        let exposureDurationSeconds = CMTimeGetSeconds(self.device1?.exposureDuration ?? CMTime())
        let minExposureDurationSeconds = max(CMTimeGetSeconds(self.device1?.activeFormat.minExposureDuration ?? CMTime()), kExposureMinimumDuration)
        let maxExposureDurationSeconds = CMTimeGetSeconds(self.device1?.activeFormat.maxExposureDuration ?? CMTime())
        // Map from duration to non-linear UI range 0-1
        let p = (exposureDurationSeconds - minExposureDurationSeconds) / (maxExposureDurationSeconds - minExposureDurationSeconds) // Scale to 0-1
        self.exposureDurationSlider.value = Float(pow(p, 1 / kExposureDurationPower)) // Apply inverse power

        self.exposureISOSlider.minimumValue = self.device1?.activeFormat.minISO ?? 0.0
        self.exposureISOSlider.maximumValue = self.device1?.activeFormat.maxISO ?? 0.0
        self.exposureISOSlider.value = self.device1?.iso ?? 0.0
        self.exposureISOSlider.isEnabled = (self.device1?.exposureMode == .custom)

        // Manual white balance controls

        let whiteBalanceGains = self.device1?.deviceWhiteBalanceGains ?? AVCaptureWhiteBalanceGains()
        let whiteBalanceTemperatureAndTint = self.device1?.temperatureAndTintValues(forDeviceWhiteBalanceGains: whiteBalanceGains) ?? AVCaptureWhiteBalanceTemperatureAndTintValues()

        self.whitebalanceTemperatureSlider.minimumValue = 3000
        self.whitebalanceTemperatureSlider.maximumValue = 8000
        self.whitebalanceTemperatureSlider.value = whiteBalanceTemperatureAndTint.temperature


        self.whitebalanceTintSlider.minimumValue = -150
        self.whitebalanceTintSlider.maximumValue = 150
        self.whitebalanceTintSlider.value = whiteBalanceTemperatureAndTint.tint


    }


private func set(_ slider: UISlider, highlight color: UIColor) {
        slider.tintColor = color

        if slider === self.focusPositionSlider {
            self.positionLabel.textColor = slider.tintColor
            self.focusPositionValue.textColor = slider.tintColor
        } else if slider === self.exposureDurationSlider {
            self.durationLabel.textColor = slider.tintColor
            self.exposureDurationValue.textColor = slider.tintColor
        } else if slider === self.exposureISOSlider {
            self.isoLabel.textColor = slider.tintColor
            self.exposureISOValue.textColor = slider.tintColor
        } else if slider === self.whitebalanceTemperatureSlider {
            self.temperatureLabel.textColor = slider.tintColor
            self.temperatureValue.textColor = slider.tintColor
        } else if slider === self.whitebalanceTintSlider {
            self.tintLabel.textColor = slider.tintColor
            self.tintValue.textColor = slider.tintColor
        }
    }
@IBAction func sliderTouchBegan(_ slider: UISlider) {
        self.set(slider, highlight: UIColor(red: 0.0, green: 122.0/255.0, blue: 1.0, alpha: 1.0))
    }

@IBAction func sliderTouchEnded(_ slider: UISlider) {
        self.set(slider, highlight: UIColor.gray)
    }
private func configureSession() {
        guard self.setupResult == .success else {
            return
        }

        self.session?.beginConfiguration()

        self.session?.sessionPreset = AVCaptureSessionPresetPhoto


        DispatchQueue.main.async {
            self.configureManualHUD()
        }
    }

    @available(iOS 10.0, *)
    private func currentPhotoSettings() -> AVCapturePhotoSettings? {
        guard let stillImageOutput = self.stillImageOutput else {
            return nil
        }
        var photoSettings: AVCapturePhotoSettings? = nil

        if stillImageOutput.isLensStabilizationDuringBracketedCaptureSupported {
            let bracketedSettings: [AVCaptureBracketedStillImageSettings]
                bracketedSettings = [AVCaptureManualExposureBracketedStillImageSettings.manualExposureSettings(withExposureDuration: AVCaptureExposureDurationCurrent, iso: AVCaptureISOCurrent)]

            if !stillImageOutput.availableRawPhotoPixelFormatTypes.isEmpty {
                photoSettings = AVCapturePhotoBracketSettings(rawPixelFormatType: stillImageOutput.availableRawPhotoPixelFormatTypes[0].uint32Value, processedFormat: nil, bracketedSettings: bracketedSettings)
            } else {
                photoSettings = AVCapturePhotoBracketSettings(rawPixelFormatType: 0, processedFormat: [AVVideoCodecKey: AVVideoCodecJPEG], bracketedSettings: bracketedSettings)
            }

            (photoSettings as! AVCapturePhotoBracketSettings).isLensStabilizationEnabled = true
        } else {
            if !stillImageOutput.availableRawPhotoPixelFormatTypes.isEmpty {
                photoSettings = AVCapturePhotoSettings(rawPixelFormatType: stillImageOutput.availableRawPhotoPixelFormatTypes[0].uint32Value, processedFormat: [AVVideoCodecKey : AVVideoCodecJPEG])
            } else {
                photoSettings = AVCapturePhotoSettings()
            }

                photoSettings?.flashMode = stillImageOutput.supportedFlashModes.contains(AVCaptureFlashMode.auto.rawValue as NSNumber) ? .auto : .off

        }

        if !(photoSettings?.availablePreviewPhotoPixelFormatTypes.isEmpty ?? true) {
            photoSettings?.previewPhotoFormat = [kCVPixelBufferPixelFormatTypeKey as String: photoSettings!.availablePreviewPhotoPixelFormatTypes[0]]
        }

        photoSettings?.isAutoStillImageStabilizationEnabled = true


        photoSettings?.isHighResolutionPhotoEnabled = true

        return photoSettings
    }

    @IBAction func changeLensPosition(_ control: UISlider) {

        do {
            try self.device1!.lockForConfiguration()
            self.device1!.setFocusModeLockedWithLensPosition(control.value, completionHandler: nil)
            self.device1!.unlockForConfiguration()
        } catch let error {
            NSLog("Could not lock device for configuration: \(error)")
        }
    }

@IBAction func changeExposureDuration(_ control: UISlider) {

        let p = pow(Double(control.value), kExposureDurationPower) // Apply power function to expand slider's low-end range
        let minDurationSeconds = max(CMTimeGetSeconds(self.device1!.activeFormat.minExposureDuration), kExposureMinimumDuration)
        let maxDurationSeconds = CMTimeGetSeconds(self.device1!.activeFormat.maxExposureDuration)
        let newDurationSeconds = p * ( maxDurationSeconds - minDurationSeconds ) + minDurationSeconds; // Scale from 0-1 slider range to actual duration

        do {
            try self.device1!.lockForConfiguration()
            self.device1!.setExposureModeCustomWithDuration(CMTimeMakeWithSeconds(newDurationSeconds, 1000*1000*1000), iso: AVCaptureISOCurrent, completionHandler: nil)
            self.device1!.unlockForConfiguration()
        } catch let error {
            NSLog("Could not lock device for configuration: \(error)")
        }
    }
@IBAction func changeISO(_ control: UISlider) {

        do {
            try self.device1!.lockForConfiguration()
            self.device1!.setExposureModeCustomWithDuration(AVCaptureExposureDurationCurrent, iso: control.value, completionHandler: nil)
            self.device1!.unlockForConfiguration()
        } catch let error {
            NSLog("Could not lock device for configuration: \(error)")
        }
    }
private func setWhiteBalanceGains(_ gains: AVCaptureWhiteBalanceGains) {

        do {
            try self.device1!.lockForConfiguration()
            let normalizedGains = self.normalizedGains(gains)
            self.device1!.setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains(normalizedGains, completionHandler: nil)
            self.device1!.unlockForConfiguration()
        } catch let error {
            NSLog("Could not lock device for configuration: \(error)")
        }
    }
@IBAction func changeTemperature(_: AnyObject) {
        let temperatureAndTint = AVCaptureWhiteBalanceTemperatureAndTintValues(
            temperature: self.whitebalanceTemperatureSlider.value,
            tint: self.whitebalanceTintSlider.value
        )

        self.setWhiteBalanceGains(self.device1!.deviceWhiteBalanceGains(for: temperatureAndTint))
    }

    @IBAction func changeTint(_: AnyObject) {
        let temperatureAndTint = AVCaptureWhiteBalanceTemperatureAndTintValues(
            temperature: self.whitebalanceTemperatureSlider.value,
            tint: self.whitebalanceTintSlider.value
        )

        self.setWhiteBalanceGains(self.device1!.deviceWhiteBalanceGains(for: temperatureAndTint))
    }
private func normalizedGains(_ gains: AVCaptureWhiteBalanceGains) -> AVCaptureWhiteBalanceGains {
        var g = gains

        g.redGain = max(1.0, g.redGain)
        g.greenGain = max(1.0, g.greenGain)
        g.blueGain = max(1.0, g.blueGain)

        g.redGain = min(self.device1!.maxWhiteBalanceGain, g.redGain)
        g.greenGain = min(self.device1!.maxWhiteBalanceGain, g.greenGain)
        g.blueGain = min(self.device1!.maxWhiteBalanceGain, g.blueGain)

        return g
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }




@IBAction func pressCamera(_ sender: Any) {

        let settings = AVCapturePhotoSettings()
        let previewLayer = self.previewView1.layer as! AVCaptureVideoPreviewLayer
        let videoPreviewLayerVideoOrientation = previewLayer.connection.videoOrientation
        let photoOutputConnection = self.stillImageOutput?.connection(withMediaType: AVMediaTypeVideo)
        photoOutputConnection?.videoOrientation = videoPreviewLayerVideoOrientation

        let previewPixelType = settings.availablePreviewPhotoPixelFormatTypes.first!
        let previewFormat = [
            kCVPixelBufferPixelFormatTypeKey as String: previewPixelType,
            kCVPixelBufferWidthKey as String: 160,
            kCVPixelBufferHeightKey as String: 160
        ]
        settings.previewPhotoFormat = previewFormat
        stillImageOutput?.capturePhoto(with: settings, delegate: self)


        whiteImage.image = UIImage(named: "white")
        //bool = false
        timer = Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false)

}
    // called every time interval from the timer
    func delayedAction() {
        whiteImage.image = UIImage(named: "asfalt-light")
    }

    @IBAction func doLogout(_ sender: Any) {
        try! Auth.auth().signOut()
        performSegue(withIdentifier: "logout", sender: self)
    }
    //call back from take picture

func capture(_ stillImageOutput: AVCapturePhotoOutput,didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) {
        if let error = error {
            print("error occure : \(error.localizedDescription)")
        }

        if  let sampleBuffer = photoSampleBuffer,
            let previewBuffer = previewPhotoSampleBuffer,
            let dataImage =  AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer:  sampleBuffer, previewPhotoSampleBuffer: previewBuffer) {
            print(UIImage(data: dataImage)?.size as Any)

        }
        else {
            print("Unknown Error")
        }

}


}

storyboard picture

bob
  • 1
  • similair to [Same question for objective-c](https://stackoverflow.com/questions/3088059/what-does-this-mean-nsunknownkeyexception-reason-this-class-is-not-key-v) . did you set correc class in your storyboard? – Codus Jul 16 '17 at 00:21
  • yes, everything is in the correct class in the storyboard. – bob Jul 16 '17 at 02:25
  • @bob check this answer https://stackoverflow.com/questions/45067276/thread-1-signal-sigabrt-error/45069255#45069255 maybe can help you – Reinier Melian Jul 16 '17 at 03:13
  • thanks for all of your suggestions! for future reference the error finally went away when i deleted "@objc(ViewController)" at the top, right before the ViewController is declared. I have no idea why this worked though. – bob Jul 16 '17 at 06:16

0 Answers0