framework related errors

No such module ‘framework名’

症状

コンパイル時に、No such module ‘framework名’のエラー

原因

framework のサーチパスが設定されていない可能性がある。

対応

Build Settings >> Search Paths >> Framework Search Paths のパスを正しく設定する。

この時、TestFoundation.framework という名前の場合、TestFoundation.framework の1つ上のディレクトリを指定する。

例 Debug の iphonesimulator の場合
***/Build/Products/Debug-iphonesimulator/TestFoundation.framework
であれば、
***/Build/Products/Debug-iphonesimulator
になる。

framework の種類が、Debug/Release, iphoneos/iphonesimulator により、パスが異なってくるので、正しいパスを設定する。

Could not find module ‘フレームワーク名’ for target ‘arm64-apple-ios’; found: x86_64-apple-ios-simulator, x86_64

症状

Could not find module ‘フレームワーク名’ for target ‘arm64-apple-ios’; found: x86_64-apple-ios-simulator, x86_64

原因

framework のサーチパスが シミュレータ用にビルドされたフレームワークのパスに設定されている。

対応

Build Settings >> Search Paths >> Framework Search Paths のパスを正しく設定する。

この時、TestFoundation.framework という名前の場合、TestFoundation.framework の1つ上のディレクトリを指定する。

例 Debug の iphonesimulator の場合
***/Build/Products/Debug-iphoneos/TestFoundation.framework
であれば、
***/Build/Products/Debug-iphoneosになる。

framework の種類が、Debug/Release, iphoneos/iphonesimulator により、パスが異なってくるので、正しいパスを設定する。

Building for iOS, but the linked and embedded framework

症状

iOS 用でフレームワークを含むプロジェクトをコンパイル時に、次のエラー。

プロジェクトパス Building for iOS, but the linked and embedded framework ‘フレームワーク名.framework’ was built for iOS Simulator.

原因

iOS (実機)用にビルドしたが、埋め込まれたフレームワークが iOS シミュレータ用にビルドされている。

対応

Build Settings >> Search Paths >> Framework Search Paths のパスを正しく設定する。1つ上のエラーへの対応方法を参照。

signal SIGABORT

症状

dyld: Library not loaded: @rpath/TestFoundation.framework/TestFoundation

  Referenced from: /Users/uchukamen/Library/Developer/CoreSimulator/Devices/255ADD3E-7C65-4B7E-B2C6-A2F3458C7F13/data/Containers/Bundle/Application/8F3B69D4-05EE-4128-A34D-E85A6D7CEE96/CallFramework.app/CallFramework

  Reason: image not found

dyld: launch, loading dependent libraries

DYLD_SHARED_CACHE_DIR=/Users/uchukamen/Library/Developer/CoreSimulator/Caches/dyld/19G2021/com.apple.CoreSimulator.SimRuntime.iOS-14-0.18A372

DYLD_ROOT_PATH=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot

DYLD_LIBRARY_PATH=/Users/uchukamen/Library/Developer/Xcode/DerivedData/CallFramework-cbztckdabgiykdcxvrwnezepshbh/Build/Products/Debug-iphonesimulator:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/introspection

DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libBacktraceRecording.dylib:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Develo

原因

プロジェクトから、framework が削除されれた。

対応

プロジェクトに、framework を追加する。

関連情報

SwiftでFramework(ライブラリ)を作成、利用する方法

Xcodeでフレームワークを追加する方法【初心者向け】

Swiftの汎用的なモジュール(Framework)開発

独自のライブラリ(.framework)を組み込んで苦労した話

unable to find utility “xcodebuild”

Symptom

$ carthage update

*** Fetching iCimulator

*** Checking out iCimulator at “1.5”

*** xcodebuild output can be found in /var/folders/gm/t2l4p33d1r9gpqzscb8kdggw0000gn/T/carthage-xcodebuild.vQIFtO.log

A shell task (/usr/bin/xcrun xcodebuild -workspace /Users/uchukamen/Desktop/Swift/NegaViewer/NegaViewerV1.1.1/Carthage/Checkouts/iCimulator/iCimulator.xcworkspace CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES -list) failed with exit code 72:

xcrun: error: unable to find utility “xcodebuild”, not a developer tool or in PATH

Solution

xcode menu >> “xcode” >> preferences >> Location >> Comand Line Tools で値を設定

Check “Command Line Tools” is not “”.

A beautiful sea of clouds at Chichibu, Japan.

This video has been remixed from Chichi Live Camera photos by Python + OpenCV + Visual Studio Code on mac.

import os
import time
import datetime
import urllib.error
import urllib.request
import cv2

def download_file(url, dst_path):
    try:
        with urllib.request.urlopen(url) as web_file:
            data = web_file.read()
            with open(dst_path, mode='wb') as local_file:
                local_file.write(data)
    except urllib.error.URLError as e:
        print(e)

fourcc = cv2.VideoWriter_fourcc(* 'h264')
fps = 10
out = cv2.VideoWriter('out.mp4', fourcc, fps, (H,V))

startTime = datetime.datetime(yyyy, mm, dd, HH, MM, SS)
x = duration
for interval in range(x):
    t = startTime + datetime.timedelta(minutes=interval)
    print(datetime.datetime.strftime(t, "/%Y%m%d/%H%M%S.jpg"))
    tstr = datetime.datetime.strftime(t, "/%Y%m%d/%H%M%S.jpg")
    url = 'https://*********' + tstr
    dst_path = datetime.datetime.strftime(t, "images/%Y%m%d%H%M%S.jpg")
    if os.path.exists(dst_path):
        print("skip")
    else:
        download_file(url, dst_path)
        time.sleep(10)  # Don't rush to 

    img = cv2.imread(dst_path, cv2.IMREAD_COLOR)
    # 画像の大きさを取得
    # height, width, channels = img.shape[:3]
    # print("width: " + str(width))
    # print("height: " + str(height))
    out.write(img)

out.release()

Chichibu Live Camera

You can find out beautiful photos of Chichibu Live camera from here.

https://navi.city.chichibu.lg.jp/cloudview/

Anohana: The Flower We Saw That Day

Chichibu is also famous for the animation Anohana: The Flower We Saw That Day. It was on air in 2011, but still may anime fans are visiting Chichibu, the place of anime scenes.

Class QMacAutoReleasePoolTracker is implemented in both warning.

Environment

macOS Catalina 10.15.6

mac Mini

OpenCV 4.4.0

Python 3.8

Visual Studio Code V1.48.2

Symptom


When I execute the previous article, TimeLapse, the following warnings appear in the terminal.

objc[3865]: Class QMacAutoReleasePoolTracker is implemented in both /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore (0x10a1e0a48) and /usr/local/lib/python3.8/site-packages/cv2/.dylibs/QtCore (0x10f5fe700). One of the two will be used. Which one is undefined.

objc[3865]: Class QT_ROOT_LEVEL_POOL__THESE_OBJECTS_WILL_BE_RELEASED_WHEN_QAPP_GOES_OUT_OF_SCOPE is implemented in both /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore (0x10a1e0ac0) and /usr/local/lib/python3.8/site-packages/cv2/.dylibs/QtCore (0x10f5fe778). One of the two will be used. Which one is undefined.


objc[3865]: Class KeyValueObserver is implemented in both /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore (0x10a1e0ae8) and /usr/local/lib/python3.8/site-packages/cv2/.dylibs/QtCore (0x10f5fe7a0). One of the two will be used. Which one is undefined.


objc[3865]: Class RunLoopModeTracker is implemented in both /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore (0x10a1e0b38) and /usr/local/lib/python3.8/site-packages/cv2/.dylibs/QtCore (0x10f5fe7f0). One of the two will be used. Which one is undefined.

Workaround

$ pip uninstall opencv-python

$ pip install opencv-python-headless

Special Thanks To

https://qiita.com/LemniscaterN/items/dfcda303677ca2ebf049

https://pypi.org/project/opencv-python/

AV, In, Out, Session, Delegate

混乱するので、まとめ。

種別InputOutputDelegateDelegate
CameraAVCaptureDevice videoAVCapture
PhotoOutput
AVCapture
PhotoCaptureDelegate
photoOutput
LivePhotoAVCaptureDevice video, audioAVCapture
PhotoOutput
AVCapture
PhotoCaptureDelegate
capturePhoto
VideoAVCaptureDevice video, audioAVCapture
MovieFileOutput
AVCapture
FileOutputRecordingDelegate
fileOutput
Face
Detection
AVCaptureDevice videoAVCapture
VideoDataOutput
AVCapture
VideoDataOutputSampleBufferDelegate

ライブフォトのキャプチャと保存

オリジナル情報源

https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/capturing_still_and_live_photos/capturing_and_saving_live_photos

概要

ライブフォトは、キャプチャの直前と直後の瞬間のモーションとサウンドを含む写真です。アプリは、AVFoundationキャプチャシステムとAVCapturePhotoOutputクラスを使用して、ライブフォトをキャプチャして記録できます。

注意

すでにキャプチャセッション、入力設定、および写真撮影に慣れていない場合は、参照キャプチャセッションの設定とキャプチャとライブ写真。

ライブ写真キャプチャを有効にする

静止写真の場合、キャプチャセッションにはビデオ入力のみが必要ですが、ライブフォトにはサウンドが含まれているため、オーディオキャプチャデバイスをセッションに接続する必要もあります。

enum CameraError: Error {
    case configurationFailed
    // ... additional error cases ...
}

func configureSession() throws {
    captureSession.beginConfiguration()
    
    // ... add camera input and photo output ...
    
    guard let audioDevice = AVCaptureDevice.default(for: .audio),
          let audioDeviceInput = try? AVCaptureDeviceInput(device: audioDevice) else {
              throw CameraError.configurationFailed
    }
    
    if captureSession.canAddInput(audioDeviceInput) {
        captureSession.addInput(audioDeviceInput)
    } else {
        throw CameraError.configurationFailed
    }
    
    // ... configure photo output and start running ...
    
    captureSession.commitConfiguration()
}

ビデオに組み込みのカメラデバイスを既に使用しているため(「キャプチャセッションの設定」を参照)、デフォルトのオーディオキャプチャデバイスを使用できます。システムは、カメラの位置に最適なマイク構成を自動的に使用します。

Live Photosをキャプチャするには、キャプチャパイプラインの内部再構成が必要です。これには時間がかかり、進行中のキャプチャが中断されます。最初のライブ写真を撮影する前に、AVCapturePhotoOutputオブジェクトでライブ写真のキャプチャを有効にして、パイプラインが適切に構成されていることを確認してください。

let photoOutput = AVCapturePhotoOutput()

// Attempt to add the photo output to the session.
if captureSession.canAddOutput(photoOutput) {
    captureSession.sessionPreset = .photo
    captureSession.addOutput(photoOutput)
} else {
    throw CameraError.configurationFailed
}

// Configure the photo output's behavior.
photoOutput.isHighResolutionCaptureEnabled = true
photoOutput.isLivePhotoCaptureEnabled = photoOutput.isLivePhotoCaptureSupported

// Start the capture session.
captureSession.startRunning()

ライブ写真をキャプチャ

写真出力がLive Photosの準備ができたら、各ショットの静止画像またはLive Photoキャプチャを選択できます。ライブフォトをキャプチャするにはAVCapturePhotoSettingsオブジェクトを作成し、ライブフォトの静止画部分の形式を選択し、ライブフォトのムービー部分を書き込むためのURLを提供します。次に、キャプチャをトリガーするためにcapturePhoto(with:delegate:)を呼び出します:

let photoSettings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.hevc])
photoSettings.livePhotoMovieFileURL = // output url

// Shoot the Live Photo, using a custom class to handle capture delegate callbacks.
let captureProcessor = LivePhotoCaptureProcessor()
photoOutput.capturePhoto(with: photoSettings, delegate: captureProcessor)

ライブ写真の結果を処理する

ライブフォトは、写真アプリでは1つのアセットとしてユーザーに表示されますが、実際には、メインの静止画像と、前後の瞬間のモーションとサウンドを含むムービーファイルの別々のファイルで構成されています。キャプチャシステムは、それぞれが利用可能になるとすぐに、これらの結果を個別に配信します。

このphotoOutput(_:didFinishProcessingPhoto:error:)メソッドは、Live Photoの静止画部分をAVCapturePhotoオブジェクトとして配信します。静止画像と動画ファイルを一緒に保存する必要があるため、以下から示すように、画像ファイルのデータをAVCapturePhotoから抽出し、動画ファイルの記録が完了するまで保持することをお勧めします。(このメソッドを使用して、UIで静止画像がキャプチャされたことを示すこともできます。)

func photoOutput(_ output: AVCapturePhotoOutput,
                 didFinishProcessingPhoto photo: AVCapturePhoto,
                 error: Error?) {
    guard error != nil else {
        print("Error capturing Live Photo still: \(error!)");
        return
    }
    
    // Get and process the captured image data.
    processImage(photo.fileDataRepresentation())
}

このphotoOutput(_:didFinishProcessingLivePhotoToMovieFileAt:duration:photoDisplayTime:resolvedSettings:error:)メソッドは後で起動し、キャプチャのトリガー時に指定したURLに完全なムービーファイルが含まれていることを示します。ライブ写真の静止画と動画の両方の部分を取得したら、それらを一緒に保存できます。

func photoOutput(_ output: AVCapturePhotoOutput,
                 didFinishProcessingLivePhotoToMovieFileAt outputFileURL: URL,
                 duration: CMTime,
                 photoDisplayTime: CMTime,
                 resolvedSettings: AVCaptureResolvedPhotoSettings,
                 error: Error?) {
    
    guard error != nil else {
        print("Error capturing Live Photo movie: \(error!)");
        return
    }
    
    guard let stillImageData = stillImageData else { return }
    
    // Save Live Photo.
    saveLivePhotoToPhotosLibrary(stillImageData: stillImageData,
                                 livePhotoMovieURL: outputFileURL)
}

注意

キャプチャ後にLive Photosを表示するには、PHLivePhotoおよびPHLivePhotoViewを参照してください。

ライブ写真をフォトライブラリに保存する

PHAssetCreationRequestクラスを使用して、複数のファイルのメディアで構成される単一のPhotosアセットを作成します。LivePhotoの場合、静止画像とそのペアのビデオです。キャプチャした写真の保存と同様に、そのリクエストをPHPhotoLibrary変更ブロックでラップし、最初にアプリが写真にアクセスするユーザーの権限を持っていることを確認する必要があります。

func saveLivePhotoToPhotosLibrary(stillImageData: Data, livePhotoMovieURL: URL) {    PHPhotoLibrary.requestAuthorization { status in
        guard status == .authorized else { return }
        
        PHPhotoLibrary.shared().performChanges({
            // Add the captured photo's file data as the main resource for the Photos asset.
            let creationRequest = PHAssetCreationRequest.forAsset()
            creationRequest.addResource(with: .photo, data: stillImageData, options: nil)
            
            // Add the movie file URL as the Live Photo's paired video resource.
            let options = PHAssetResourceCreationOptions()
            options.shouldMoveFile = true
            creationRequest.addResource(with: .pairedVideo, fileURL: livePhotoMovieURL, options: options)
        }) { success, error in
            // Handle completion.
        }
    }
}

ヒント

このshouldMoveFileオプションを使用して、iOSがアプリのサンドボックスからシステムのフォトライブラリにムービーファイルを転送します。高価なデータのコピー操作は必要ありません。

ライブ写真の進行状況を追跡する

Live Photosをキャプチャすると、「写真キャプチャの進行状況の追跡」に示すプロセスに2つの追加ステップが追加されます。静止画の結果の配信(ステップ4)の後、写真出力はムービーキャプチャステータスを通知し(ステップ5)、ムービー結果を配信します(ステップ6) )。(最終クリーンアップはステップ7になります。)

capturePhoto()を呼び出した後の番号付きのLive Photoキャプチャプロセスステップの図。

ユーザーがシステムのカメラアプリでライブ写真をキャプチャすると、「ライブ」インジケーターが数秒間表示され、ビデオとオーディオがまだ記録されていることをユーザーに知らせます。アプリに同様のインターフェイスを提供するには、写真キャプチャデリゲートに次のメソッドを実装します。

複数のライブフォトキャプチャを同時に実行できるため、これらの方法を使用して、「処理中」のキャプチャの数を追跡し、その数がゼロに達した場合にのみインジケータを非表示にするのが最善です。

class LivePhotoCaptureProcessor: NSObject, AVCapturePhotoCaptureDelegate {
    // ... other PhotoCaptureDelegate methods and supporting properties ...
    
    // A handler to call when Live Photo capture begins and ends.
    var livePhotoStatusHandler: (Bool) -> () = { _ in }
    
    // A property for tracking in-progress captures and updating UI accordingly.
    var livePhotosInProgress = 0 {
        didSet {
            // Update the UI accordingly based on the value of this property
        }
    }
    
    // Call the handler when PhotoCaptureDelegate methods indicate Live Photo capture is in progress.
    func photoOutput(_ output: AVCapturePhotoOutput,
                     willBeginCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
        let capturingLivePhoto = (resolvedSettings.livePhotoMovieDimensions.width > 0 && resolvedSettings.livePhotoMovieDimensions.height > 0)
        livePhotoStatusHandler(capturingLivePhoto)
    }
    
    func photoOutput(_ output: AVCapturePhotoOutput,
                     didFinishRecordingLivePhotoMovieForEventualFileAt outputFileURL: URL,
                     resolvedSettings: AVCaptureResolvedPhotoSettings) {
        livePhotoStatusHandler(false)
    }
}

キャプチャした写真の保存

オリジナル情報源

https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/capturing_still_and_live_photos/saving_captured_photos

概要

AVCapturePhotoOutputで写真のキャプチャを完了すると、静止画像データだけでなく、カメラのメタデータとキャプチャでリクエストした補助画像(サムネイルや深度マップなど)を含むAVCapturePhotoオブジェクトを受け取ります。これらのタイプのデータをAVCapturePhotoから個別に取得するか、そのfileDataRepresentation()メソッドを呼び出して、AVCapturePhotoSettingsでその写真に要求したコーデックとファイル形式を使用して、Dataオブジェクトをディスクに書き込む準備をすることができます。

通常、写真をキャプチャした後、そのデータをユーザーのフォトライブラリに追加します。Photosフレームワークを使用してそうすることができます。

フォトライブラリの使用許可をリクエストする

iOSがカメラとマイクへのアクセスをユーザーに許可することでユーザーのプライバシーを保護するのと同じように、システムはユーザーにアプリにフォトライブラリへのアクセスを許可することも要求します。アプリの許可を取得するには:

  1. アプリケーションのInfo.plistファイル内のNSPhotoLibraryUsageDescription キーを含むように構成します。このキーの値は、フォトライブラリへのアクセスを要求するシステムアラートでユーザーに表示されるメッセージです。このメッセージを使用して、アプリにそのようなアクセスが必要な理由をユーザーに説明します。
  2. 承認を確認またはリクエストします。このPHPhotoLibrary requestAuthorization(_:)メソッドを使用して、ユーザーが最初にアプリのカメラ機能を開いたときなど、アプリに適切なタイミングでiOSにフォトライブラリへのアクセスを求めるアラートが表示されるようにします。(ユーザーが最初の写真を撮るまで待たないでください。許可の警告により、複数の写真を撮ることができなくなります。)

以下のコードは、アクセスを確認するための簡単なワークフローを示しています。

PHPhotoLibrary.requestAuthorization { status in
    guard status == .authorized else { return }
    
    // Use PHPhotoLibrary.shared().performChanges(...) to add assets.
}

作成リクエストを使用して写真アセットを追加する

このAVCapturePhoto fileDataRepresentation()メソッドは、写真キャプチャからのすべての画像データ、補助画像データ、およびメタデータを、ディスクに書き込む準備ができている単一のデータオブジェクトにパッケージ化します。そのデータを写真ライブラリに追加するには、PHPhotoLibraryおよびPHAssetCreationRequestクラスを使用します。

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    guard error == nil else { print("Error capturing photo: \(error!)"); return }

    PHPhotoLibrary.requestAuthorization { status in
        guard status == .authorized else { return }
        
        PHPhotoLibrary.shared().performChanges({
            // Add the captured photo's file data as the main resource for the Photos asset.
            let creationRequest = PHAssetCreationRequest.forAsset()
            creationRequest.addResource(with: .photo, data: photo.fileDataRepresentation()!, options: nil)
        }, completionHandler: self.handlePhotoLibraryError)
    }
}

注意

performChanges(_:completionHandler:)ブロックは、1つのアトミックアップデートではフォトライブラリに複数の変更を行うことができます。たとえば、新しく作成したアセットをアルバムに追加できます。または、ブラケットキャプチャを実行すると、複数のdidFinishProcessingPhoto結果を蓄積して、それらを一緒にフォトライブラリに保存できます。詳細については、Photosドキュメントを参照してください。

写真キャプチャの進行状況の追跡

オリジナル情報源

https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/capturing_still_and_live_photos/tracking_photo_capture_progress

概要

iOSデバイスのカメラで写真をキャプチャすることは、物理的なカメラメカニズム、画像信号処理、オペレーティングシステム、アプリを含む複雑なプロセスです。アプリがこのプロセスの多くの段階を無視して単純に最終結果を待つことは可能ですが、各ステップを監視することで、より応答性の高いカメラインターフェイスを作成できます。

capturePhoto(with:delegate:)を呼び出した後、デリゲートオブジェクトはプロセスの5つの主要なステップ(写真の設定によってはそれ以上)に沿って進むことができます。作成するキャプチャワークフローとキャプチャUIに応じて、デリゲートは次の手順の一部またはすべてを処理できます。

capturePhoto()を呼び出した後の番号付き写真キャプチャプロセスのステップの図。
  1. 設定完了
  2. 露出開始
  3. 露出完了
  4. 結果データ配信
  5. キャプチャ完了

キャプチャシステムは、このプロセスの各ステップでAVCaptureResolvedPhotoSettingsオブジェクトを提供します。同時に複数のキャプチャが進行している可能性があるため、解決された各写真設定オブジェクトには、写真の撮影に使用したAVCapturePhotoSettingsuniqueIDと一致するuniqueID値を持つ

キャプチャ設定を取得する

写真の設定を指定する場合、選択する設定の一部は自動であり、キャプチャシステムがキャプチャの瞬間に正確に決定するために残されます。たとえば、AVCaptureDevice.FlashMode.autoフラッシュモードを選択すると、カメラ自体がシーンの照明に基づいて、写真を露光するときにフラッシュを発光するかどうかを決定できます。

露光を開始する直前に、写真出力はデリゲートのメソッドを呼び出します。そのメソッドのパラメーターは、キャプチャの実際の設定を示します。たとえば、フラッシュモードを選択した場合、解決された設定オブジェクトは、現在のキャプチャでフラッシュが使用されているかどうかを通知します。この情報を使用して、UIでフラッシュが使用されたことを示すことができます。resolvedSettingsAVCaptureDevice.FlashMode.auto

露出開始の処理

露出時間が始まると、写真出力はデリゲートのphotoOutput(_:willBeginCaptureFor:)メソッドを呼び出します。従来の写真では、この瞬間はカメラのシャッターが開くことに相当します。このとき、シャッター音も自動的に鳴ります。

UIでこのメソッドに応答して、要求された写真が撮影されていることを示すシャッターアニメーションまたはその他のインジケーターを表示できます。

露出終了の処理

写真の出力は、露出時間が完了するとすぐにデリゲートのphotoOutput(_:didCapturePhotoFor:)メソッドを呼び出します。アプリに画像を提供する前に、システムはまだカメラデータを処理する時間を必要としますが、この瞬間を使用して、UIに露出が完了したことを表示できます。たとえば、メソッドでカメラのプレビューを非表示にし、willCapturePhotodidCapturePhotoメソッドでもう一度表示することで、シャッター効果をシミュレートできます。

写真結果の処理

写真出力にアプリで利用できる画像データがある場合、デリゲートのphotoOutput(_:didFinishProcessingPhoto:error:)メソッドを呼び出します。写真の設定によっては、写真の出力でこのメソッドが複数回呼び出される場合があります。

  • ブラケットキャプチャを要求した場合、このメソッドはブラケット内の露出ごとに(少なくとも)1回起動し、その露出の画像を提供します。
  • RAW形式と処理済み形式(HEIF / HEVCやJPEGなど)の両方でのキャプチャを要求した場合、このメソッドは(少なくとも)各形式に対して1回発生します。

たとえば、3つの露出ブラケットでRAW + HEIFキャプチャを要求すると、写真出力はデリゲートのdidFinishProcessingPhotoメソッドを6回呼び出し(2つのフォーマット×3つの露出)、6つのAVCapturePhotoオブジェクトを提供します。複数の結果を追跡するには、各写真のを解決済みの設定のphotoCountexpectedPhotoCount比較します。

注意

ライブ写真のキャプチャを要求すると、追加のデリゲートメソッドがそのプロセスに関するフィードバックを提供します。ライブ写真のキャプチャと保存を参照してください。

キャプチャが完了したらクリーンアップする

キャプチャに関するシステムのすべての作業が完了すると、写真出力はデリゲートのphotoOutput(_:didFinishCaptureFor:error:)メソッドを呼び出します。この瞬間を使用して、キャプチャプロセスのアプリの一部を終了できます。

  • キャプチャが複数の結果を予期している場合は、それらをphotoOutput(_:didFinishProcessingPhoto:error:)(およびphotoOutput(_:didFinishProcessingLivePhotoToMovieFileAt:duration:photoDisplayTime:resolvedSettings:error:))メソッドにキャッシュしてから、結果をローカルストレージに保存するか、didFinishCaptureメソッドのフォトライブラリに追加します。
  • キャプチャプロセスが他のリソースを管理している場合は、didFinishCaptureメソッドでそれらのリソースをクリーンアップします。キャプチャごとに個別の写真キャプチャデリゲートオブジェクトを使用する場合は、そのようなオブジェクトへの強い参照を削除するのによいタイミングです。

以下のコードは、複数の写真キャプチャデリゲートオブジェクトを管理する1つの方法を示しています。

class PhotoCaptureProcessor: NSObject, AVCapturePhotoCaptureDelegate {
    var completionHandler: () -> () = {}
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
        completionHandler()
    }
    // ... other delegate methods to handle capture results...
}

// Keep a set of in-progress capture delegates.
var capturesInProgress = Set<PhotoCaptureProcessor>()

func shootPhoto() {    
    // Make a new capture delegate for each capture and add it to the set.
    let captureProcessor = PhotoCaptureProcessor()
    capturesInProgress.insert(captureProcessor)
    
    // Schedule for the capture delegate to be removed from the set after capture.
    captureProcessor.completionHandler = { [weak self] in
        self?.capturesInProgress.remove(captureProcessor); return
    }
    
    self.photoOutput.capturePhoto(with: self.settingsForNextPhoto(), delegate: captureProcessor)
}

メディアキャプチャの承認の要求

オリジナル情報源

このページは、次の情報を日本語に翻訳したものです。お役に立てば幸いです。

Requesting Authorization for Media Capture on iOS

iOS 13.0以降

Xcode 11.1以降

AVFoundation

概要

iOSでは、ユーザーは各アプリがカメラとマイクにアクセスすることを明示的に許可する必要があります。アプリが初めてキャプチャシステムを使用できるようになる前に、iOSは、以下に示すように、アプリにカメラへのアクセスを許可するようユーザーに要求するアラートを表示します。iOSはこのアラートに対するユーザーの応答を記憶しているため、その後キャプチャシステムを使用しても、アラートが再び表示されることはありません。ユーザーは、[設定]> [プライバシー]でアプリの権限設定を変更できます。

メディアをキャプチャする前にアプリに権限があることを確認するには、以下の手順に従います。

アプリのinfo.plistファイルを構成する

iOSでは、システムがカメラまたはマイクの許可を要求したときにユーザーに表示する静的メッセージをアプリが提供する必要があります。

  • アプリがデバイスカメラを使用している場合は、アプリのInfo.plistファイルにNSCameraUsageDescriptionキーを含めます。
  • アプリがデバイスマイクを使用する場合は、アプリのInfo.plistファイルにNSMicrophoneUsageDescriptionキーを含めます。

キーごとに、アプリがメディアをキャプチャする必要がある理由をユーザーに説明するメッセージを提供します。これにより、ユーザーはアプリに許可を与えることに自信を持つことができます。

重要

アプリが承認を要求したとき、またはキャプチャデバイスを使用しようとしたときに、適切なキーがアプリのInfo.plistファイルに存在しない場合、システムはアプリを終了します。

キャプチャの承認を確認してリクエストする

キャプチャセッションを設定する前に、必ずAVCaptureDevice authorizationStatus(for:)メソッドをテストしてください。ユーザーがキャプチャ権限をまだ付与または拒否していない場合、承認ステータスはAVAuthorizationStatus.notDeterminedです。この場合、requestAccess(for:completionHandler:)メソッドを使用して、ユーザーにプロンプ​​トを表示するようにiOSに指示します。

switch AVCaptureDevice.authorizationStatus(for: .video) {
    case .authorized: // The user has previously granted access to the camera.
        self.setupCaptureSession()
    
    case .notDetermined: // The user has not yet been asked for camera access.
        AVCaptureDevice.requestAccess(for: .video) { granted in
            if granted {
                self.setupCaptureSession()
            }
        }
    
    case .denied: // The user has previously denied access.
        return

    case .restricted: // The user can't grant access due to restrictions.
        return
}

requestAccess(for:completionHandler:)メソッドは非同期です:あなたのアプリは、iOSが許可警告を示しながら実行し続けます。ユーザーが応答すると、システムは完了ハンドラーを呼び出します。完了ハンドラの成功パラメータがtrueの場合、キャプチャセッションのセットアップと開始に進むことができます。

注意

キャプチャを開始する前に requestAccess(for:completionHandler:)を呼び出しますが、アプリに適した時間にのみ行ってください。たとえば、写真やビデオの録画がアプリの主な焦点ではない場合は、ユーザーがアプリのカメラ関連機能を呼び出したときにのみカメラの権限を確認します。

キャプチャしたメディアを保存する前に承認を要求する

写真またはビデオをキャプチャした後、それらをユーザーのフォトライブラリに保存することができます。フォトライブラリにアクセスするには、ユーザーの権限も必要です(カメラとマイクの権限とは別に)。いつどのように許可を要求するかは、メディアの保存に使用する機能によって異なります。

  • ほとんどの写真およびビデオキャプチャワークフロー(Live PhotosやRAW形式のキャプチャを含む)では、PHPhotoLibraryおよびPHAssetCreationRequestクラスを使用します。これらのクラスにはPhotosライブラリへの読み取り/書き込みアクセス権が必要なため、Info.plistのNSPhotoLibraryUsageDescriptionキーを使用して、アクセスを要求するときにユーザーにメッセージを提供する必要があります。詳細については、キャプチャした写真の保存を参照してください。
  • アプリがムービーファイルをPhotosライブラリに保存するだけでよい場合、このUISaveVideoAtPathToSavedPhotosAlbum(_:_:_:_:)関数はPHPhotoLibraryより簡単な方法を提供します。この関数はライブラリへの書き込みアクセスのみを必要とするため、Info.plistのNSPhotoLibraryAddUsageDescriptionキーを使用して、フォトライブラリへの保存の許可を求めるときにユーザーにメッセージを提供します。

注意

UIImageクラスは写真出力に含まれる機能とメタデータをサポートしていないため、このUIImageWriteToSavedPhotosAlbum(_:_:_:_:)関数をAVCapturePhotoOutputでキャプチャした写真で使用することはお勧めしません。

キャプチャセッションのセットアップ

オリジナル情報源

https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/setting_up_a_capture_session

概要

AVCaptureSessionがiOSとMacOSの中のすべてのメディアキャプチャの基礎です。OSキャプチャインフラストラクチャとキャプチャデバイスへのアプリの排他的アクセス、および入力デバイスからメディア出力へのデータのフローを管理します。入力と出力の間の接続を構成するこよにより、キャプチャセッションの機能が定義されます。たとえば、下の図は、iPhoneのバックカメラとマイクを使用して、写真と動画の両方をキャプチャし、カメラプレビューを提供するキャプチャセッションを示しています。

詳細なキャプチャセッションアーキテクチャの例のブロック図:カメラとマイクの個別のAVCaptureDeviceInputオブジェクトは、AVCaptureSessionによって管理されるAVCaptureConnectionオブジェクトを介して、AVCapturePhotoOutput、AVCaptureMovieFileOutput、およびAVCaptureVideoPreviewLayerに接続します。
図1  サンプルのキャプチャセッションのアーキテクチャ

入力と出力をセッションに接続

すべてのキャプチャセッションには、少なくとも1つのキャプチャ入力とキャプチャ出力が必要です。キャプチャ入力(AVCaptureInputサブクラス)はメディアソースです。通常、iOSデバイスやMacに組み込まれているカメラやマイクなどの録音デバイスです。キャプチャ出力(AVCaptureOutputサブクラス)は、キャプチャ入力によって提供されるデータを使用して、画像や動画ファイルなどのメディアを生成します。

ビデオ入力にカメラを使用するには(写真またはムービーをキャプチャするため)、適切なAVCaptureDeviceを選択し、対応するAVCaptureDeviceInputを作成して、セッションに追加します。

captureSession.beginConfiguration()
let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera,
                                          for: .video, position: .unspecified)
guard
    let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!),
    captureSession.canAddInput(videoDeviceInput)
    else { return }
captureSession.addInput(videoDeviceInput)

注意

iOSには、カメラデバイスを選択する方法が他にもいくつかあります。詳細については、「キャプチャデバイスの選択」を参照してください。

次に、選択したカメラからキャプチャする予定のメディアの種類の出力を追加します。たとえば、写真のキャプチャを有効にするには、セッションにAVCapturePhotoOutputを追加します。

let photoOutput = AVCapturePhotoOutput()
guard captureSession.canAddOutput(photoOutput) else { return }
captureSession.sessionPreset = .photo
captureSession.addOutput(photoOutput)
captureSession.commitConfiguration()

セッションは複数の入力と出力を持つことができます。例えば:

  • 映画のビデオとオーディオの両方を記録するには、カメラとマイクの両方のデバイスに入力を追加します。
  • 同じカメラから写真とムービーの両方をキャプチャするには、AVCapturePhotoOutputAVCaptureMovieFileOutputの両方をセッションに追加します。

重要

セッションの入力または出力の変更前にbeginConfiguration()を呼び出し、変更後にcommitConfiguration()を呼ぶこと。

カメラのプレビューを表示

従来のカメラのビューファインダーのように、写真を撮ったり、ビデオ録画を開始したりする前に、ユーザーがカメラからの入力を確認できるようにすることが重要です。そのようなプレビューを提供するには、AVCaptureVideoPreviewLayerをキャプチャセッションに接続します。これにより、セッションが実行されているときはいつでも、カメラからのライブビデオフィードが表示されます。

AVCaptureVideoPreviewLayerコアアニメーションレイヤーであるため、他のCALayerサブクラスと同じように、インターフェイスで表示およびスタイル設定できます。以下に示すように、プレビューレイヤーをUIKitアプリに追加する最も簡単な方法は、UIViewサブクラスを定義することです。そのlayerClassAVCaptureVideoPreviewLayerです。

class PreviewView: UIView {
    override class var layerClass: AnyClass {
        return AVCaptureVideoPreviewLayer.self
    }
    
    /// Convenience wrapper to get layer as its statically known type.
    var videoPreviewLayer: AVCaptureVideoPreviewLayer {
        return layer as! AVCaptureVideoPreviewLayer
    }
}

次に、キャプチャセッションでプレビューレイヤーを使用するには、レイヤーのsessionプロパティを設定します。

self.previewView.videoPreviewLayer.session = self.captureSession

注意

アプリが複数のインターフェイスの向きをサポートしている場合は、プレビューレイヤーのconnectionキャプチャセッションを使用して、UIと一致するようにvideoOrientation を設定します。

キャプチャセッションを実行する

入力、出力、プレビューを構成したら、startRunning()を呼び出して、データを入力から出力に流します。

一部のキャプチャ出力では、メディアキャプチャを開始するために必要なのはセッションの実行だけです。たとえば、セッションにAVCaptureVideoDataOutputが含まれている場合、セッションが実行されるとすぐにビデオフレームの配信の受信を開始します。

他のキャプチャ出力では、最初にセッションの実行を開始し、次にキャプチャ出力クラス自体を使用してキャプチャを開始します。たとえば、写真アプリでは、セッションを実行するとビューファインダースタイルのプレビューが有効になりますが、このAVCapturePhotoOutput capturePhoto(with:delegate:)メソッドを使用して写真をスナップします。