【Swift】使い回しできるコードまとめ

`xcode 12.4`
`iOS 14.0`
これから`Swift`でアプリを作成するにあたり、使い回せそうなコードを自分の都合でまとめました。

# ライブラリ
`pod`の準備

```ターミナル
pod init

pod 'Firebase'
pod 'Firebase/Auth'
pod 'Firebase/Storage'
pod 'Firebase/Firestore'
pod 'SDWebImage'
pod "ViewAnimator"
pod 'ActiveLabel'

pod install
```
# Firebase/Authを利用したログイン機能の実装(匿名ログイン)
Firebaseで`GoogleServise-Info.plist`をダウンロードし、ファイル内に配置したら...

```AppDelegate.swift
import Firebase

class AppDelegate: UIResponder, UIApplicationDelegate {
//(中略)
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        FirebaseApp.configure() //<- 追加,firebaseauthを使うときに必要
        return true
  }
//(中略)
}
```

# firebase/storageを利用したプロフィール画像設定機能の実装
## カメラとアルバムの使用を許可するためのファイルの作成

```Model/checkModel.swift
import Foundation
import Photos

class CheckModel {
    
    func showCheckPermission() {
        PHPhotoLibrary.requestAuthorization { (status) in
            switch(status) {
            case .authorized:
                print("許可")
            case .denied:
                print("拒否")
            case .notDetermined:
                print("notDetermined")
            case .restricted:
                print("restricted")
            case .limited:
                print("limited")
            @unknown default: break
            }
        }
    }
}
```
## ログインボタンの実装

```Controller/LoginViewController.swift
import UIKit
import Firebase //<-追加
import FirebaseAuth //<-追加

class LoginViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    @IBOutlet weak var profileImageView: UIImageView! //<-storyboardと繋げる
    @IBOutlet weak var textField: UITextField! //<-storyboardと繋げる
    
    var sendDBModel = SendDBModel() //<-別途SendDBModelを作成 作成したModelを使用するためにインスタンス化が必要
    
    var urlString = String() //<-クラウドに保存した画像のurlを利用するための箱
    override func viewDidLoad() {
        super.viewDidLoad()

        //許可画面
        let checkModel = CheckModel()
        checkModel.showCheckPermission()
    }
    
    @IBAction func login(_ sender: Any) {
        //匿名ログイン
        Auth.auth().signInAnonymously { (result, error) in
            //データベースのデータがresultに入る
            //エラー処理
            if error != nil {
                return
            }
            //ユーザーが本当にいるのか
            let user = result?.user
            print(user.debugDescription)
            //次の画面遷移 -> 次のViewControllerへ
            let selectVC = self.storyboard?.instantiateViewController(identifier: "selectVC") as! selectRoomViewController
            //アプリ内に名前を保存する forkeyはキー値
            UserDefaults.standard.setValue(self.textField.text, forKey: "userName")
            //画像をクラウドストレージに送信する + データを圧縮する
            let data = self.profileImageView.image?.jpegData(compressionQuality: 0.01)
            //データをストレージに保存する クラス名.sendProfileImageData(data: data)->classのメソッドに入る
            self.sendDBModel.sendProfileImageData(data: data!)
            //画面遷移
            self.navigationController?.pushViewController(selectVC, animated: true)
        }
    }
}
```
## プロフィール画像をFirebaseのDBに保存してアプリ内で使用するためのModel

```Model/SendDBModel.swift
import Foundation
import FirebaseStorage

class SendDBModel {
    //送信機能を集約するクラス
    init () {
    }
    func sendProfileImageData(data:Data) {
        let image = UIImage(data:data)
        let profileImage = image!.jpegData(compressionQuality: 0.1)
        
        //保存先の決定
        //リファレンス パスを作り、フォルダを作成している UUIDで一意性のIDを作成し、uuid+dateでデータ名をつけている
        let imageRef = Storage.storage().reference().child("profileImage").child("\(UUID().uuidString + String(Date().timeIntervalSince1970)).jpg")
        //送信作業
        imageRef.putData(profileImage!, metadata: nil) { (metadata, error) in
            if error != nil {
                return
            }
            //firebaseのサーバーから画像のurlが帰ってくる
            imageRef.downloadURL { (url, error) in
                if error != nil {
                    return
                }
                //userImageというキー値で画像を保存
                UserDefaults.standard.setValue(url?.absoluteString, forKey: "userImage")
            }
        }
    }
}
```

## カメラとアルバムを使用するためのメソッド

```Controller/LoginViewController.swift
func doCamera() {
        let sourceType:UIImagePickerController.SourceType = .camera
        //カメラ利用可能かチェック
        if UIImagePickerController.isSourceTypeAvailable(.camera){
            let cameraPicker = UIImagePickerController()
            cameraPicker.allowsEditing = true
            cameraPicker.sourceType = sourceType
            cameraPicker.delegate = self
            self.present(cameraPicker, animated: true, completion: nil)
        }
    }
    func doAlbum() {
        let sourceType:UIImagePickerController.SourceType = .photoLibrary
        //アルバム利用可能かチェック
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){
            let cameraPicker = UIImagePickerController()
            cameraPicker.allowsEditing = true
            cameraPicker.sourceType = sourceType
            cameraPicker.delegate = self
            self.present(cameraPicker, animated: true, completion: nil)
        }
    }
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if info[.originalImage] as? UIImage != nil {
            let selectedImage = info[.originalImage] as! UIImage
            profileImageView.image = selectedImage
            picker.dismiss(animated: true, completion: nil)
        }
    }
```
## 画像選択部分をタップするとアラートが出現し、カメラかアルバムか選択できるようにする
タップ機能が使用できるように、`ImageView`の`User Interaction Enabled`に☑️をいれる。

```Controller/LoginViewController.swift
@IBAction func tapImageView(_ sender: Any) {
        let generater = UINotificationFeedbackGenerator()
        generater.notificationOccurred(.success)
        showAlert()
    }
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true, completion: nil)
    }
    //アラート
    func showAlert() {
        let alertController = UIAlertController(title: "選択", message: "どちらを使用しますか?", preferredStyle: .actionSheet)
        let action1 = UIAlertAction(title: "カメラ", style: .default) { (alert) in
            self.doCamera()
        }
        let action2 = UIAlertAction(title: "アルバム", style: .default) { (alert) in
            self.doAlbum()
        }
        let action3 = UIAlertAction(title: "キャンセル", style: .cancel)
        
        alertController.addAction(action1)
        alertController.addAction(action2)
        alertController.addAction(action3)
        self.present(alertController, animated: true, completion: nil)
    }
  //画面がタッチされたらキーボードを閉じる
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textField.resignFirstResponder()
    }
```
#　キーボードを出現させたり隠れさせたりする

```swift
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //キーボード
        //iphoneが持っている機能としてキーボードが呼ばれるとkeyboardwillshowが呼ばれる
        NotificationCenter.default.addObserver(self, selector: #selector(EditViewController.keyboardWillShow(_ :)), name: UIResponder.keyboardWillShowNotification, object: nil)
        //キーボードが隠れた時
        NotificationCenter.default.addObserver(self, selector: #selector(EditViewController.keyboardWillHide(_ :)), name: UIResponder.keyboardWillHideNotification, object: nil)
        if UserDefaults.standard.object(forKey: "userName") != nil {
            userName = UserDefaults.standard.object(forKey: "userName") as! String
        }
        if UserDefaults.standard.object(forKey: "userImage") != nil {
            userImageString = UserDefaults.standard.object(forKey: "userImage") as! String
        }
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.setNavigationBarHidden(true, animated: true)
    }
    
    @objc func keyboardWillShow(_ notification:NSNotification) {
        let keyboardHeight = ((notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as Any) as AnyObject).cgRectValue.height
        //y座標は左上から始まる
        //スクリーン全体の高さから、キーボードの高さと、テキストフィールドの高さを引いている
        textField.frame.origin.y = screenSize.height - keyboardHeight - textField.frame.height
        sendButton.frame.origin.y = screenSize.height - keyboardHeight - sendButton.frame.height
    }
    @objc func keyboardWillHide(_ notification:NSNotification) {
        textField.frame.origin.y = screenSize.height - textField.frame.height
        sendButton.frame.origin.y = screenSize.height - sendButton.frame.height
        //キーボードが下がっていく時間がduration
        //durationを渡してanimationを作成する
        guard let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else {return}
        UIView.animate(withDuration: duration) {
            let transform = CGAffineTransform(translationX: 0, y: 0)
            self.view.transform = transform
        }
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textField.resignFirstResponder()
    }
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
```