@kazukifish

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Swift5でカメラアプリを作りたい初心者

解決したいこと

Swiftでカメラアプリを作ろうとしています。
実機テストを行ったところ、カメラが動いている様子はあるのですが画面にカメラ画像が写りませんでした。
解決方法を教えていただけると嬉しいです。

発生している問題・エラー

特にエラーは出ていないのですが、画像の表示がうまくいきません。。。

該当するソースコード

import UIKit
import AVFoundation

class ViewController: UIViewController {

    let captureSession = AVCaptureSession()
    var captureDevice: AVCaptureDevice?
    var previewLayer: AVCaptureVideoPreviewLayer?
    let backgroundQueue = DispatchQueue.global()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // カメラの設定
        setupCamera()
        // 画像出力の設定
        setupPreviewLayer()
        // バックグラウンドでの実行
        backgroundQueue.async {
            self.startRunningCaptureSession()
            // デバッグ部分
            if self.captureSession.isRunning {
                print("runnnig")
            } else {
                print("not run")
            }
            //デバッグ部分
        }
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        previewLayer?.frame = view.layer.bounds
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        stopRunningCaptureSession()
    }
}
// メソッドの中身
extension ViewController {

    func setupCamera() {
        //Sessionに追加するメディアの品質を指定
        captureSession.sessionPreset = .photo
        //deviceの指定
        let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.back)
        let devices = deviceDiscoverySession.devices
        for device in devices {
            if device.position == .back {
                captureDevice = device
                //デバッグ部分
                print("find camera")
                print(device)
                //デバッグ部分
                break
            } else {
                print("device discovery error")
                continue
            }
        }
        //Inputの定義
        let deviceInput: AVCaptureDeviceInput
        do {
            deviceInput = try AVCaptureDeviceInput(device: captureDevice!)
            //デバッグ部分
            print("device can be set")
            print(deviceInput)
            //デバッグ部分
            if captureSession.canAddInput(deviceInput) {
                captureSession.addInput(deviceInput)
                //デバッグ部分
                print("device can be input")
                print(captureSession.inputs)
                //デバッグ部分
            } else {
                print("addInput error")
                return
            }
        } catch {
            print("deviceInput error")
            return
        }
    }
    //カメラの画像の出力
    func setupPreviewLayer() {
        // 1. previewLayerにcaptureSesionを追加
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        // 2. 画面
        previewLayer?.videoGravity = .resizeAspectFill
        previewLayer?.frame = view.layer.bounds
        // デバッグ部分
        print("Session in previewLayer: \(String(describing: previewLayer?.session))") //ここまで大丈夫そう
        // デバッグ部分
        // 3. Viewへの追加
        view.layer.addSublayer(previewLayer!)
    }
    // カメラ開始
    func startRunningCaptureSession() {
        captureSession.startRunning()
    }
    // カメラ停止
    func stopRunningCaptureSession() {
        captureSession.stopRunning()
        // プレビューレイヤーをビューの階層から取り除く
        previewLayer?.removeFromSuperlayer()
        // プレビューレイヤーを解放する
        previewLayer = nil
    }
}

該当するソースコード2

import UIKit
import AVFoundation

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.makeKeyAndVisible()
        window?.rootViewController = ViewController()
        print("ok")
        return true
    }
    //アプリがアクティブになった時に呼ばれるメソッド
    func applicationDidBecomeActive(_ application: UIApplication) {
        AVCaptureDevice.requestAccess(for: .video) { granted in
            if !granted {
                // カメラの使用が許可されなかった場合に処理を行う
                // 例: ユーザーにメッセージを表示して、カメラ機能を制限するなど
                print("カメラの使用が許可されませんでした。")
            }
        }
    }
}

自分で試したこと

  1. setupCamera()とsetupPreviewLayer()で設定を完了した後に、captureSession.startRunning()をバックグランドスレッドにて実行させました。
  2. infoにCamera Usage DescriptionとPhoto Library Usage Descriptionを追加しました。

初心者質問で申し訳ないです。。。

0 likes

1Answer

提示されたコードを実行してみましたが、問題なく画面にカメラ画像が写りました。

find camera
<AVCaptureFigVideoDevice: 0x102820200 [Back Camera][com.apple.avfoundation.avcapturedevice.built-in_video:0]>
device can be set
<AVCaptureDeviceInput: 0x280bd6ce0 [Back Camera]>
device can be input
[<AVCaptureDeviceInput: 0x280bd6ce0 [Back Camera]>]
Session in previewLayer: Optional(<AVCaptureSession: 0x28099c580 [AVCaptureSessionPresetPhoto]>)
runnnig

IMG_2144s.jpeg

Xcode:14.3.1
Target iOS:16.1
実機:iPhone8、iOS:16.1.2


新規プロジェクトで作りましたので、@mainが違います。sceneに実装しています。

AppDelegate.swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }

}
SceneDelegate.swift
import UIKit
import AVFoundation

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let _ = (scene as? UIWindowScene) else { return }
        
        AVCaptureDevice.requestAccess(for: .video) { granted in
            if !granted {
                // カメラの使用が許可されなかった場合に処理を行う
                // 例: ユーザーにメッセージを表示して、カメラ機能を制限するなど
                print("カメラの使用が許可されませんでした。")
            }
        }
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    }

}
0Like

Comments

  1. @kazukifish

    Questioner

    回答いただきありがとうございます!コード自体にそこまで大きな問題はなさそうとのことだったので、開発環境の方に問題があるのかなと考えています。調べてみると、権限をリクエストするようなアプリケーションはApple Developer Programに加入しなければ実機テストができないと出てきたので、そういうことなのかなと。。。

  2. @kazukifish

    Questioner

    プロジェクト作成時にinterfaceにSwiftUIを選択していたのですが、StoryBoardを選択することで実行することができました。

Your answer might help someone💌