【SwiftUI】フォトライブラリから画像を取得する【ImagePicker】

今回は SwiftUI アプリで、フォトライブラリから画像を選択し、Image として表示する方法について紹介します。

SwiftUI 独自の機能としては現時点(2021/02/12現在)では提供されていないので、今回も例に漏れず、UIViewControllerRepresentable プロトコルを継承し、UIKit のUIImagePickerViewController をラップした View を作成します。

【SwiftUI】フォトライブラリから画像を取得する【ImagePicker】

UIViewControllerRepresentable の継承

先ずは、UIViewControllerRepresentable プロトコルを継承し以下のメソッドを実装します。

  • makeUIViewController(context: UIViewControllerRepresentableContext) -> UIImagePickerController
  • updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext)

updateUIViewController の方は今回は使用しませんが実装しないとエラーとなります。

struct ImagePicker: UIViewControllerRepresentable {

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
    }
}

Coordinator内部クラス

続いてコアとなる内部クラス Coordinator を定義します。Coordinator には以下の2つのデリゲートを継承します。

  • UIImagePickerControllerDelegate
  • UINavigationControllerDelegate

また、親となる ImagePicker の参照を持たせます。

final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
    var parent: ImagePicker
        
    init(_ parent: ImagePicker) {
        self.parent = parent
    }
}

UIImagePickerController の初期化

続いて、makeUIViewController で初期化処理を行います。

var sourceType: UIImagePickerController.SourceType = .photoLibrary

func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        
    let imagePicker = UIImagePickerController()
    imagePicker.allowsEditing = false
    imagePicker.sourceType = sourceType
    imagePicker.delegate = context.coordinator
        
    return imagePicker
}

UIImagePickerController を生成し、編集不可設定、読み込み元のタイプ、デリゲートを指定します。

sourceType は予め ImagePicker 本体に保持させたものを渡しています。

画像取得後の処理

フォトライブラリ画面で画像をタップして選択した後の処理ですが、先程継承した UIImagePickerControllerDelegate の以下のメソッドを実装します。

imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
}

このメソッドで画像を受け取り、表示元の画面へ反映する処理を行いたいと思います。

事前に、ImagePicker に選択画像のバインディング変数と、画面を閉じるための環境変数を定義しておきます。

@Binding var selectedImage: UIImage?
@Environment(\.presentationMode) private var presentationMode

以下のように画像を取り出し、画面を閉じます。

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            
    if let image = info[.originalImage] as? UIImage {
        parent.selectedImage = image
    }
            
    parent.presentationMode.wrappedValue.dismiss()
}

ImagePicker 全体コード

ImagePicker 全体のコードとしては以下のようになります。

import UIKit
import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {
    
    var sourceType: UIImagePickerController.SourceType = .photoLibrary
    
    @Binding var selectedImage: UIImage?
    @Environment(\.presentationMode) private var presentationMode
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        
        let imagePicker = UIImagePickerController()
        imagePicker.allowsEditing = false
        imagePicker.sourceType = sourceType
        imagePicker.delegate = context.coordinator
        
        return imagePicker
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
        
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }
    
    final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        
        var parent: ImagePicker
        
        init(_ parent: ImagePicker) {
            self.parent = parent
        }
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            
            if let image = info[.originalImage] as? UIImage {
                parent.selectedImage = image
            }
            
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
}

ImagePicker の使用例

最後に、使用例として冒頭の動画のコードを紹介します。

struct ContentView: View {
    
    @State private var image: UIImage?
    @State var showingImagePicker = false
    
    var body: some View {
        VStack {
            if let uiImage = image {
                Image(uiImage: uiImage)
                    .resizable()
                    .frame(width: 200, height: 200)
                    .clipShape(Circle())
            } else {
                Image("noimage")
                    .resizable()
                    .frame(width: 200, height: 200)
                    .clipShape(Circle())
            }
            Spacer().frame(height: 32)
            Button(action: {
                showingImagePicker = true
            }) {
                Text("フォトライブラリから選択")
            }
        }
        .sheet(isPresented: $showingImagePicker) {
            ImagePicker(sourceType: .photoLibrary, selectedImage: $image)
        }
    }
}

ユーザー登録のあるアプリでプロフィール画像を選択する時などに使えそうですね。

なお、sourceType.camera を指定するとカメラが立ち上がり撮影した画像を反映することができます

ただし、事前に以下のように info.plist でカメラへのアクセス許可を確認するための設定を追加しておかないとクラッシュしてしまいますので注意してください。

以上

【おすすめの関連記事】

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です