Swift5での正方形カメラ

#はじめに
　Swift5で正方形の写真を撮影するカメラの作り方を勉強してみました。
　iPhoneのWidthと同じ長さの正方形の写真をカメラロールに保存してみます。
##カメラアプリを作る２種類の方法
###・UIImagePickerController
　簡単にカメラで撮影するアプリが作れます。正方形で撮影するのは難しそう。
###・AVFoundation
　動画を撮影したり、いろいろできる賢いやつです。←今回はこちらです。

#実装方法
##あらかじめinfo.plistに二つの記述が必要です。
・Privacy - Camera Usage Description
　「カメラを使います」的な内容を記載
・Privacy - Photo Library Additions Usage Description
　「カメラロールに保存します」的な内容を記載
##ソースコード
```swift:ViewController.swift
import UIKit
import AVFoundation

class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {
    var device: AVCaptureDevice!
    var session: AVCaptureSession!
    var output: AVCapturePhotoOutput!
    var tempImage:UIImage!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        setPicture()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    //カメラ準備
    func setPicture(){
        //セッションを生成
        session = AVCaptureSession()
        //背面カメラを選択
        device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
        //背面カメラからキャプチャ入力生成
        guard let input = try? AVCaptureDeviceInput(device: device) else {
            print("例外発生")
            return
        }
        session.addInput(input)
        output = AVCapturePhotoOutput()
        session.addOutput(output)
        session.sessionPreset = .photo
        // プレビューレイヤーを生成
        let pvSize = self.view.frame.width
        let pvLayer = AVCaptureVideoPreviewLayer(session: session)
        pvLayer.frame = view.bounds
        pvLayer.frame = CGRect(x: 0, y: 90, width: pvSize, height: pvSize)
        pvLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        view.layer.addSublayer(pvLayer)
        // セッションを開始
        session.startRunning()
        // 撮影ボタンを生成
        let shutterBtn = UIButton()
        shutterBtn.setTitle("◯", for: .normal)
        shutterBtn.titleLabel?.font = UIFont.systemFont(ofSize: 74)
        shutterBtn.contentMode = .center
        shutterBtn.frame = CGRect(x: 0, y: 0, width: 80, height: 80)
        shutterBtn.layer.cornerRadius = 0.5 * shutterBtn.bounds.size.width
        shutterBtn.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
        shutterBtn.setTitleColor(UIColor.black, for: UIControl.State())
        shutterBtn.layer.position = CGPoint(x: view.frame.width / 2, y: self.view.bounds.size.height - 80)
        shutterBtn.addTarget(self, action: #selector(photoshot), for: .touchUpInside)
        view.addSubview(shutterBtn)
        //キャンセルボタンを生成
        let cancelBtn = UIButton()
        cancelBtn.setTitle("×", for: .normal)
        cancelBtn.titleLabel?.font = UIFont.systemFont(ofSize: 32)
        cancelBtn.contentMode = .center
        cancelBtn.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
        cancelBtn.setTitleColor(UIColor.white, for: UIControl.State())
        cancelBtn.layer.position = CGPoint(x: 20, y: 40)
        cancelBtn.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
        view.addSubview(cancelBtn)
    }
    //撮影
    @objc func photoshot(_ sender: AnyObject) {
        let settings = AVCapturePhotoSettings()
        settings.flashMode = .off
        settings.isAutoStillImageStabilizationEnabled = true
        output.capturePhoto(with: settings, delegate: self)
    }
    //撮影結果・再撮影・保存ボタンの表示
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        if error == nil {
            let outputImageView = UIImageView()
            let pvSize = self.view.frame.width
            outputImageView.frame = CGRect(x: 0, y: 90, width: pvSize, height: pvSize)
            outputImageView.image = UIImage(data: photo.fileDataRepresentation()!)?.croppingToCenterSquare()
            tempImage = UIImage(data: photo.fileDataRepresentation()!)?.croppingToCenterSquare()
            self.view.addSubview(outputImageView)
            session.stopRunning()
            //再撮影ボタン
            let retryBtn = UIButton()
            retryBtn.setTitle("再撮影", for:.normal)
            retryBtn.frame = CGRect(x:self.view.frame.width/2 - 150,y:self.view.frame.height - 115,width: 70,height: 70)
            retryBtn.addTarget(self, action: #selector(retryPhoto), for: .touchUpInside)
            view.addSubview(retryBtn)
            //保存ボタン
            let saveBtn = UIButton()
            saveBtn.setTitle("保存", for: .normal)
            saveBtn.frame = CGRect(x:self.view.frame.width/2 + 80,y:self.view.frame.height - 115,width: 70,height: 70)
            saveBtn.addTarget(self, action: #selector(savePhoto), for: .touchUpInside)
            view.addSubview(saveBtn)
        }
    }
    //再撮影
    @objc func retryPhoto(sender:UIButton){
        let subViews = view.subviews
        for subView in subViews {
            subView.removeFromSuperview()
        }
        setPicture()
    }
    //カメラロールへの保存
    @objc func savePhoto(sender:UIButton){
        UIImageWriteToSavedPhotosAlbum(tempImage, self, nil, nil)
        let subViews = view.subviews
        for subView in subViews {
            subView.removeFromSuperview()
        }
        setPicture()
    }
    //キャンセル
    @objc func cancelAction(sender:UIButton){
        //
    }
}

extension UIImage {
    func croppingToCenterSquare() -> UIImage {
        let cgImage = self.cgImage!
        var newWidth = CGFloat(cgImage.width)
        var newHeight = CGFloat(cgImage.height)
        if newWidth > newHeight {
            newWidth = newHeight
        } else {
            newHeight = newWidth
        }
        let x = (CGFloat(cgImage.width) - newWidth)/2
        let y = (CGFloat(cgImage.height) - newHeight)/2
        let rect = CGRect(x: x, y: y, width: newWidth, height: newHeight)
        let croppedCGImage = cgImage.cropping(to: rect)!
        return UIImage(cgImage: croppedCGImage, scale: self.scale, orientation: self.imageOrientation)
    }
}
```
#環境
・Xcode 10.2.1
・Swift 5
・iOS 12.3.1

#参考文献
[AVCapturePhotoSettings](https://developer.apple.com/documentation/avfoundation/avcapturephotosettings)
#おわりに
Mahalo