SwiftUIでフォトライブラリ

#SwiftUIでフォトライブラリを表示する
ボタンをタップして、フォトライブラリを表示します。
フォトライブラリで写真を選択すると、全画面に表示します。

> Swiftを基礎から学ぶには
自著、工学社より発売中の「[まるごと分かるSwiftプログラミング](https://amzn.asia/d/7YPWJwl)」をお勧めします。変数、関数、フロー制御構文、データ構造はもちろん、構造体からクロージャ、エクステンション、プロトコル、クロージャまでを基礎からわかりやすく解説しています。

### 環境
Swift 5.3
Xcode 12.0.1
macOS 10.15.7

## フォトライブラリ
フォトライブラリをSwiftUIでラップした構造体を定義します。

````Swift:ImagePicker.swift
import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {
    var sourceType: UIImagePickerController.SourceType = .photoLibrary
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let imagePicker = UIImagePickerController()
        imagePicker.allowsEditing = false
        imagePicker.sourceType = sourceType
        return imagePicker
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {

    }
}
````

## 最初の画面
アプリの基本となる画面の構造体です。
ステート変数の基づいて、フォトライブラリをシート形式で表示します。

````Swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State private var image = UIImage()
    @State private var isShowPhotoLibrary = false
    
    var body: some View {
        VStack {
            Image(uiImage: self.image)
            Button(action: {
                self.isShowPhotoLibrary = true
            }, label: {
                Text("Photo Library")
                    .padding()
            })
        }
        .sheet(isPresented: $isShowPhotoLibrary, content: {
            ImagePicker(sourceType: .photoLibrary)
        })
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
````

## フォトライブラリの写真を選択する
`UIImagePickerControllerDelegate`プロトコルに準拠します。

````Swift:ImagePicker.swift
import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {
    // MARK: - Working with UIViewControllerRepresentable
    var sourceType: UIImagePickerController.SourceType = .photoLibrary
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let imagePicker = UIImagePickerController()
        imagePicker.allowsEditing = false
        imagePicker.sourceType = sourceType
        imagePicker.delegate = context.coordinator  // Coordinater to adopt UIImagePickerControllerDelegate Protcol.
        return imagePicker
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {

    }

    // MARK: - Using Coordinator to Adopt the UIImagePickerControllerDelegate Protocol
    @Binding var selectedImage: UIImage
    @Environment(\.presentationMode) private var presentationMode
    
    func makeCoordinator() -> Coordinator {
        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[UIImagePickerController.InfoKey.originalImage] as? UIImage {
                parent.selectedImage = image
            }
     
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
}
````

## 選択した写真を表示する
`Image`ビューにモディファイアを追加します。
画面遷移するタイミングで、選択した写真を`image`プロパティに設定します。

````Swift:ContentView.swift
struct ContentView: View {
    @State private var image = UIImage()
    @State private var isShowPhotoLibrary = false
    
    var body: some View {
        VStack {
            Image(uiImage: self.image)
                .resizable()
                .scaledToFill()
                .frame(minWidth: 0, maxWidth: .infinity)
                .edgesIgnoringSafeArea(.all)
            Button(action: {
                self.isShowPhotoLibrary = true
            }, label: {
                Text("Photo Library")
                    .padding()
            })
        }
        .sheet(isPresented: $isShowPhotoLibrary, content: {
            ImagePicker(sourceType: .photoLibrary, selectedImage: self.$image)
        })
    }
}
````