公開中のアプリ

[Swift/Xcode]初心者必見!iOSアプリ作成から公開まで徹底解説!#31「アプリテスト後の微調整編②」

今回でアプリを完成させたいと思います!

今回の修正は下記です!

①ローカル通知の文言の設定
②ボタンを長押しする時間をもう少し短くする

動作環境は下記の通りです!
Xcode Version 12.5 Swift version 5.4

ローカル通知の文言の設定

ローカル通知の時に表示されるテキストを考えたいと思います!

まぁ単純に「今日は貯金しましたか?」的な文言で良いと思ったんですけど、それだけだとちょっと寂しいので現在の貯金額も表示されるようにしようと思います!

貯金額の合計を格納してる変数はMainViewController.swiftの変数totalSavingsです。

この変数をNotificationViewController.swiftでも使えるようにstatic var totalSavingsに変更します。

staticにした変数を使う場合は、MainViewController.totalSavingsのように使います。

ではまず、NotificationViewController.swiftを修正します。

// 通知のタイトル
content.title = "今日は貯金しましたか?"

// 通知のサブタイトル(iOS10から使用可能)
content.subtitle = "現在の貯金額は\(MainViewController.totalSavings)円です!"

次にMainViewController.swiftの変数totalSavingsを全部MainViewController.totalSavingsに変更すればOKです!

※詳しい修正箇所は記事の最後にコード全部載せてるのでそちらで確認してください!

ボタンを長押しする時間をもう少し短くする

ボタンですが1秒だとやっぱり長いかなと感じたので0.5秒にしたいと思います!

修正箇所はMainViewController.swiftの二箇所です。

まずはタイマーを設定してる部分を修正します。

//タイマーの設定
func startTimer() {
    timer = Timer.scheduledTimer(
        timeInterval: 0.5, //タイマーの間隔を設定
        target: self,
        selector: #selector(self.timerCounter), //メソッドの設定
        userInfo: nil,
        repeats: false) //リピートするかどうか
}

次に長押し機能のメソッドの中です。
ここではボタンの色が変化する時間を同様に0.5秒にしました!

if sender.state == .began {
//realmの呼び出し
let realm = try! Realm()
//if文で条件分岐
if realm.objects(Btn365.self).filter("tagNum == \(sender.view!.tag)").first == nil {
    print("のー済み")
    print("ボタン押した")
    btnTag = sender.view!.tag
    UIView.animate(withDuration: 0.5) {
        //ボタンを押してる最中のボタンの色を変更
        self.buttonArray[sender.view!.tag - 1].backgroundColor = UIColor(hex: self.common.sub , alpha: 1)
    }
    startTimer()
}

まとめ

これでアプリは完成しました!

あとは公開するだけですね〜!

あとちょっとや〜!

ここまでの全ファイルコード全部

FirstViewController.swift

import UIKit
import EAIntroView

class FirstViewController: UIViewController, EAIntroDelegate {

//Commonクラスのインスタンス生成
let common = Common()

override func viewDidLoad() {
    super.viewDidLoad()
    //画像のサイズ的に両サイドが黒くなってしまうので背景色をオレンジ色に設定
    view.backgroundColor = UIColor(hex: self.common.main , alpha: 1)
    //ウォークスルーを実行
    walkThrough()
}

func walkThrough() {
    let page1 = EAIntroPage()
    let page2 = EAIntroPage()
    let page3 = EAIntroPage()
    page1.bgColor = UIColor(hex: self.common.main , alpha: 1)
    page2.bgColor = UIColor(hex: self.common.main , alpha: 1)
    page3.bgColor = UIColor(hex: self.common.main , alpha: 1)

    //ipadの場合の処理
    if UIDevice.current.userInterfaceIdiom == .pad {
        page1.bgImage = UIImage(named: "ipad_1")
        page2.bgImage = UIImage(named: "ipad_2")
        page3.bgImage = UIImage(named: "ipad_3")
    } else {
        page1.bgImage = UIImage(named: "iphone_1")
        page2.bgImage = UIImage(named: "iphone_2")
        page3.bgImage = UIImage(named: "iphone_3")
    }
    let introView = EAIntroView(frame: self.view.bounds, andPages: [page1, page2, page3])
    introView?.skipButton.setTitle("スキップ", for: UIControl.State.normal)
    //スキップボタン欲しいならここで実装!
    introView?.delegate = self
    introView?.show(in: self.view, animateDuration: 1.0)
}

//スキップした時としなかった時で実装内容を分岐する事ができるメソッド。
func introDidFinish(_ introView: EAIntroView!, wasSkipped: Bool) {
        if(wasSkipped) {
            //ここでもUserDefaultsで分岐している。trueの場合、メニュー画面の使い方からの遷移なのでdismissで戻る。
            if UserDefaults.standard.bool(forKey: "WalkThrough") == true {
                dismiss(animated: true, completion: nil)
            } else {
                //false(初回起動時)の場合、初回起動時での遷移なのでメイン画面へ
                let MainViewController = MainViewController.init()
                let NV = UINavigationController.init(rootViewController: MainViewController)
                NV.modalPresentationStyle = .fullScreen
                present(NV, animated: true, completion: nil)
                //UserDefaultsにtrueを保持させる
                UserDefaults.standard.set(true, forKey: "WalkThrough")
            }
            
        } else {
            if UserDefaults.standard.bool(forKey: "WalkThrough") == true {
                dismiss(animated: true, completion: nil)
            } else {
                let MainViewController = MainViewController.init()
                let NV = UINavigationController.init(rootViewController: MainViewController)
                NV.modalPresentationStyle = .fullScreen
                present(NV, animated: true, completion: nil)
                //UserDefaultsにtrueを保持させる
                UserDefaults.standard.set(true, forKey: "WalkThrough")
            }
        }
    }

}

MainViewController.swift

import UIKit
import AudioToolbox
import RealmSwift
 
class MainViewController: UIViewController {
 
//スクロールビュー
let scrollView = UIScrollView()
//ボタンを表示用のビュー
let inView: UIView = UIView()
//スタックビュー縦用
var stackV: UIStackView = UIStackView()
//ボタンの配列
var buttonArray: [UIButton] = []
//スタックビュー横を格納する為の配列
var stkArray: [UIStackView] = []
//貯金額合計
static var totalSavings = 0
//貯金開始?日目
var whatDay = 0
// DB参照ボタン
var menuBtn: UIBarButtonItem!
//DBリセットボタン
var resetBtn: UIBarButtonItem!
 
//オレンジ色のビュー
let topView: UIView = UIView()
//白色のビュー
let topViewInner: UIView = UIView()
//合計日数のラベル
let dayLabel: UILabel = UILabel()
//貯金額のラベル
let moneyLabel: UILabel = UILabel()
//Commonクラスのインスタンス生成
let common = Common()
 
override func viewDidLoad() {
super.viewDidLoad()
    
//ビューの生成
viewCreate()
//ラベルの生成
labelCreate()
//ボタンの生成
btnCreate()
//スタックビューの生成
stackViewCreate()
 
//ナビゲーションコントローラーまわり
//タイトル
self.title = "Bank365"
//背景色
self.navigationController?.navigationBar.barTintColor = UIColor(hex: self.common.main , alpha: 1)
//セーフエリアとの境目の線を消す
self.navigationController?.navigationBar.shadowImage = UIImage()
//フォントの設定
self.navigationController?.navigationBar.titleTextAttributes
    = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont(name: "HiraMaruProN-W4", size: 20)!]
    
//背景色の設定
self.view.backgroundColor = UIColor(hex: common.background , alpha: 1)
    
//メニューボタン
menuBtn = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .done, target: self, action: #selector(self.menuBtnTapped))
menuBtn.tintColor = UIColor(hex: self.common.white , alpha: 1)
self.navigationItem.rightBarButtonItem = menuBtn
 
//realmの呼び出し
let realm = try! Realm()
//保存されたデータが定数Btn365に入る
let Btn365 = realm.objects(Btn365.self)
//定数Btn365をfor文で回し、ボタンの色を変更しつつ、貯金額を足しつつ、日数も足してる。
for i in 0..<Btn365.count {
    //アクティブなボタンの色を緑に設定
    buttonArray[Btn365[i].tagNum - 1].backgroundColor = UIColor(hex: self.common.sub , alpha: 1)
    //ボタンの色が緑の場合、テキストの色は白にしたいので白に設定
    self.buttonArray[Btn365[i].tagNum - 1].setTitleColor(UIColor(hex: self.common.white , alpha: 1), for: UIControl.State())
    //貯金額を足してる
    MainViewController.totalSavings += Btn365[i].tagNum
    //日数はアクティブなボタンの数と同じ
    whatDay += 1
}
//貯金額ラベルを更新
    moneyLabel.text = "貯金額 \(MainViewController.totalSavings)円"
//合計日数ラベルを更新
dayLabel.text = "\(whatDay)日目"
}
 
//メニュー画面へ遷移させるメソッド+DB参照
@objc func menuBtnTapped() {
    let realm = try! Realm()
    let Btn365 = realm.objects(Btn365.self)
    print(Btn365)
    
    let MenuViewController = MenuViewController.init()
    let NV = UINavigationController.init(rootViewController: MenuViewController)
    present(NV, animated: true, completion: nil)
}
    
func deleteAlertBack() {
    //realmの呼び出し
    let realm = try! Realm()
    //保存されたデータが定数Btn365に入る
    let Btn365 = realm.objects(Btn365.self)
    //定数Btn365をfor文で回し、ボタンの色を変更しつつ、貯金額を足しつつ、日数も足してる。
    for i in 0..<Btn365.count {
        //アクティブなボタンの色を緑に設定
        buttonArray[Btn365[i].tagNum - 1].backgroundColor = UIColor(hex: self.common.white , alpha: 1)
        //ボタンの色が緑の場合、テキストの色は白にしたいので白に設定
        self.buttonArray[Btn365[i].tagNum - 1].setTitleColor(UIColor(hex: self.common.text , alpha: 1), for: UIControl.State())
    }
    
    //貯金額を0に
    MainViewController.totalSavings = 0
    //日数を0に
    whatDay = 0
    //貯金額ラベルを更新
    moneyLabel.text = "貯金額 \(MainViewController.totalSavings)円"
    //合計日数ラベルを更新
    dayLabel.text = "\(whatDay)日目"
}
//DBリセット
@objc func resetBtnTapped() {
    let realm = try! Realm()
    try! realm.write {
      realm.deleteAll()
    }
}
    
//ビュー生成
func viewCreate() {
    //オレンジ色のビューの表示
    view.addSubview(topView)
    //白色のビューの表示
    topView.addSubview(topViewInner)
    //スクロールビューの表示
    view.addSubview(scrollView)
    //ボタン表示用ビューの表示
    scrollView.addSubview(inView)
 
    //AutosizingをAutoLayoutに変換しないようにしている(おまじない)
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    //スクロールビューの上側の位置を設定
    scrollView.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: 20.0).isActive = true
    //スクロールビューの下側の位置を設定
    scrollView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -20.0).isActive = true
    //Y軸
    scrollView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true
    //横幅
    scrollView.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, multiplier: 0.95).isActive = true
 
 
    //ボタン表示用ビューの設定
    //AutosizingをAutoLayoutに変換しないようにしている(おまじない)
    inView.translatesAutoresizingMaskIntoConstraints = false
    //横幅
    inView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 1.0).isActive = true
    //この4つの制約をつけて初めてスクロールするっぽい
    inView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
    inView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0.0).isActive = true
    inView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0.0).isActive = true
    inView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0.0).isActive = true
 
 
    //オレンジのビューのレイアウト
    //AutosizingをAutoLayoutに変換しないようにしている(おまじない)
    topView.translatesAutoresizingMaskIntoConstraints = false
    //X軸
    topView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true
    //Y軸
    topView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
    //横幅
    topView.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, multiplier: 1).isActive = true
    //縦幅
    topView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.1).isActive = true
    //背景色
    topView.backgroundColor = UIColor(hex: self.common.main , alpha: 1)
 
    //白色のビューのレイアウト
    //(おまじない)
    topViewInner.translatesAutoresizingMaskIntoConstraints = false
    //X軸
    topViewInner.centerXAnchor.constraint(equalTo: topView.centerXAnchor).isActive = true
    //Y軸
    topViewInner.centerYAnchor.constraint(equalTo: topView.centerYAnchor).isActive = true
    //横幅
    topViewInner.widthAnchor.constraint(equalTo: topView.widthAnchor, multiplier: 0.95).isActive = true
    //縦幅
    topViewInner.heightAnchor.constraint(equalTo: topView.heightAnchor, multiplier: 0.8).isActive = true//変更
    //背景色
    topViewInner.backgroundColor = UIColor(hex: self.common.white, alpha: 1)
    //角丸
    topViewInner.layer.cornerRadius = 10
}
 
//ラベル生成
func labelCreate() {
    //合計日数のラベルを表示
    topViewInner.addSubview(dayLabel)
    //貯金額のラベルを表示
    topViewInner.addSubview(moneyLabel)
 
    //合計日数のラベルのレイアウト
    //(おまじない)
    dayLabel.translatesAutoresizingMaskIntoConstraints = false
    //X軸
    dayLabel.leftAnchor.constraint(equalTo: topViewInner.leftAnchor, constant: 20.0).isActive = true
    //Y軸
    dayLabel.centerYAnchor.constraint(equalTo: topViewInner.centerYAnchor).isActive = true
    //フォントの設定
    dayLabel.font = UIFont(name: "HiraMaruProN-W4", size: 20)
    //テキストカラー
    dayLabel.textColor = UIColor(hex: common.text , alpha: 1)
    //ラベルのテキスト
    dayLabel.text = "\(whatDay)日目"
 
    //貯金額のラベルのレイアウト
    //(おまじない)
    moneyLabel.translatesAutoresizingMaskIntoConstraints = false
    //X軸
    moneyLabel.rightAnchor.constraint(equalTo: topViewInner.rightAnchor, constant: -20.0).isActive = true
    //Y軸
    moneyLabel.centerYAnchor.constraint(equalTo: topViewInner.centerYAnchor).isActive = true
    //フォントの設定
    moneyLabel.font = UIFont(name: "HiraMaruProN-W4", size: 20)
    //テキストカラー
    moneyLabel.textColor = UIColor(hex: common.text , alpha: 1)
    //ラベルのテキスト
    moneyLabel.text = "貯金額 \(MainViewController.totalSavings)円"
}
    
//スタックビュー生成
func stackViewCreate() {
    //スタックビュー縦の設定
    //スタックビューの方向を縦に
    stackV.axis = .vertical
    //中のオブジェクトをどこに揃えて配置するか
    stackV.alignment = .fill
    //どう配置するか
    stackV.distribution = .fill
    //オブジェクト同士のスペース
    stackV.spacing = 10
    //おまじない
    stackV.translatesAutoresizingMaskIntoConstraints = false
    //stackVの背景色
    stackV.backgroundColor = UIColor(hex: self.common.background , alpha: 1)//変更
    //stackVを表示
    inView.addSubview(stackV)
    //X軸
    stackV.centerXAnchor.constraint(equalTo: inView.centerXAnchor).isActive = true
    //トップ位置
    stackV.topAnchor.constraint(equalTo: stackV.topAnchor, constant: 0.0).isActive = true
    //横幅
    stackV.widthAnchor.constraint(equalTo: inView.widthAnchor, multiplier: 1.0).isActive = true
    //縦幅
    stackV.heightAnchor.constraint(equalTo: inView.heightAnchor, multiplier: 1.0).isActive = true
 
    //スタックビュー横を自動生成
    for i in 0 ..< 73 {
        //StackHの生成
        let stackH:UIStackView = UIStackView()
        //スタックビューの方向を横に
        stackH.axis = .horizontal
        //オブジェクト同士のスペース
        stackH.spacing = 10
        //中のオブジェクトをどこに揃えて配置するか
        stackH.alignment = .fill
        //どう配置するか
        stackH.distribution = .fillEqually
        //スタックビュー配列に追加
        stkArray.append(stackH)
        //stackVの中にstackHを格納
        stackV.addArrangedSubview(stkArray[i])
    }
 
    //ボタンの配置
    //for文のカウンター変数
    var count = 0
    //for文で365個のボタンをどのstackHに入れるか決めている
    for i in 1 ..< 366 {
        //カウントの数に応じてどのstackHに入るか決定する。
        stkArray[count].addArrangedSubview(buttonArray[i - 1] as UIView)
        //ボタンは5列なので5の倍数でcountを一つ増やす。
        if i % 5 == 0 {
            count += 1
        }
    }
}
 
//ボタン生成#imageLiteral(resourceName: "スクリーンショット 2021-07-09 12.31.23.png")
func btnCreate() {
    //ボタンの生成と設定
    //ボタンにtagを付ける為の変数
    var tagNumber = 1
    //for文でボタンを生成
    for _ in 0...364 {
        //ボタン生成
        let button: UIButton = UIButton(type: .custom)
        //タイトルの色
        button.setTitleColor(UIColor(hex: common.text , alpha: 1), for: UIControl.State())
        //タイトルは数字なのでtagナンバーを指定
        button.setTitle(String(tagNumber), for: UIControl.State())
        //tag設定
        button.tag = tagNumber
        //背景色
        button.backgroundColor = UIColor(hex: common.white , alpha: 1)
        //iphpneとipadで処理を分岐
        if UIDevice.current.userInterfaceIdiom == .pad {
            //フォントの設定
            button.titleLabel!.font = UIFont(name: "HiraMaruProN-W4", size: 40)
        } else {
            //フォントの設定
            button.titleLabel!.font = UIFont(name: "HiraMaruProN-W4", size: 20)
        }
        
        //角丸
        button.layer.cornerRadius = 10
        //ボーダーの横幅
        button.layer.borderWidth = 3
        //ボターの色
        button.layer.borderColor = UIColor(hex: common.main , alpha: 1).cgColor
        //AutosizingをAutoLayoutに変換しないようにしている(おまじない)
        button.translatesAutoresizingMaskIntoConstraints = false
        //ボタンの横幅が可変なのでボタンの高さは横幅と同じ長さを指定する事で正方形にしてる。
        button.heightAnchor.constraint(equalTo: button.widthAnchor, multiplier: 1.0).isActive = true
        //変数tagNumberに1追加
        tagNumber += 1
        //ボタン長押し
        let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressed(_:))
        )
        //長押しを認識する時間の設定
        longPressGesture.minimumPressDuration = 0.01
        //ボタンに長押し機能を付ける
        button.addGestureRecognizer(longPressGesture)
        //ボタンの配列に追加
        buttonArray.append(button)
    }
}
 
// MARK: - ここからアニメーション関係
//タイマー初期化
var timer: Timer!
//2秒長押ししたかどうか判定する変数
var longTapOK = false
//ボタンのタグを関数の外に出す為の変数
var btnTag = 0
 
//長押し機能
@objc func longPressed(_ sender: UILongPressGestureRecognizer) {
 
    if sender.state == .began {
        //realmの呼び出し
        let realm = try! Realm()
        //if文で条件分岐
        if realm.objects(Btn365.self).filter("tagNum == \(sender.view!.tag)").first == nil {
            print("のー済み")
            print("ボタン押した")
            btnTag = sender.view!.tag
            UIView.animate(withDuration: 0.5) {
                //ボタンを押してる最中のボタンの色を変更
                self.buttonArray[sender.view!.tag - 1].backgroundColor = UIColor(hex: self.common.sub , alpha: 1)
            }
            startTimer()
        }
        
    } else if sender.state == .ended {
        print("ボタン離した")
        if longTapOK == false {
            //realmの呼び出し
            let realm = try! Realm()
 
            //if文で条件分岐
            if realm.objects(Btn365.self).filter("tagNum == \(sender.view!.tag)").first != nil {
                print("アラートを出す")
                //アラートのタイトルやメッセージの設定
                let alertController = DOAlertController(title: "解除しますか?", message: "", preferredStyle: .alert)
                //キャンセルアクションの設定
                let cancelAction = DOAlertAction(title: "Cancel", style: .cancel, handler: nil)
                //OKアクションの設定
                let okAction = DOAlertAction(title: "OK", style: .default) { action in
                    print("OKボタンが押された")
 
                    Btn365.delete(tag: sender.view!.tag)
                    self.whatDay -= 1
                    self.dayLabel.text = "\(self.whatDay)日目"
                    MainViewController.totalSavings -= sender.view!.tag
                    self.moneyLabel.text = "貯金額 \(MainViewController.totalSavings)円"
                        
                    //アラートでOKを選択した時をボタンの色を白に戻す
                    self.buttonArray[sender.view!.tag - 1].backgroundColor = UIColor(hex: self.common.white, alpha: 1)
                    //アラートでOKを選択した時をテキストの色を白から茶色に戻す
                    self.buttonArray[sender.view!.tag - 1].setTitleColor(UIColor(hex: self.common.text , alpha: 1), for: UIControl.State())
                }
                
                    // アラートのレイアウト
                    //背景色
                    alertController.alertViewBgColor = UIColor.white
                    //角丸
                    alertController.alertView.layer.cornerRadius = 15
                    // タイトルのフォント、
                    alertController.titleFont = UIFont(name: "HiraMaruProN-W4", size: 20)
                    //タイトルの文字色
                    alertController.titleTextColor = UIColor(hex: common.text, alpha: 1)
 
                    // [.Default]を[.Cancel][.Destructive]に変更すればそれぞれのアクションごとにボタンのカスタマイズが可能
                    //キャンセルボタンのフォント
                    alertController.buttonFont[.cancel] = UIFont(name: "HiraMaruProN-W4", size: 20)
                    //Cancelの背景色
                    alertController.buttonBgColor[.cancel] = UIColor(hex: common.main, alpha: 1)
                    //Cancelを押した時の色※特に変更したくないので背景色と同じ色
                    alertController.buttonBgColorHighlighted[.cancel] = UIColor(hex: common.main, alpha: 1)
                    //OKボタンの背景色
                    alertController.buttonBgColor[.default] =  UIColor(hex: common.sub, alpha: 1)
                    //OKボタンを押した時の色
                    alertController.buttonBgColorHighlighted[.default] =  UIColor(hex: common.sub, alpha: 1)
                
                //キャンセルとOKのアクションを追加
                alertController.addAction(cancelAction)
                alertController.addAction(okAction)
                //アラートを表示
                present(alertController, animated: true, completion: nil)
            } else {
                //色を白に戻す
                UIView.animate(withDuration: 0.1) {
                    //ボタンを長押しして途中で離した場合、ボタンの色を白に戻す。
                    self.buttonArray[sender.view!.tag - 1].backgroundColor = UIColor(hex: self.common.white , alpha: 1) //変更
                }
            }
            //タイマーストップ
            timer.invalidate()
        } else {
            print("ロングタップ成功")
            longTapOK = false
        }
    }
}
 
//タイマーの設定
func startTimer() {
    timer = Timer.scheduledTimer(
        timeInterval: 0.5, //タイマーの間隔を設定
        target: self,
        selector: #selector(self.timerCounter), //メソッドの設定
        userInfo: nil,
        repeats: false) //リピートするかどうか
}
    
//タイマーで実行される処理
@objc func timerCounter() {
    //ロングタップ成功
    longTapOK = true
    //ぽよよんアニメーションの実行
    animateView(topViewInner)
    //バイブさせる
    AudioServicesPlaySystemSound(1102)
    //ラベルの更新
    updateLabel()
    //RealmDBへ登録
    let a = Btn365.create()
    a.tagNum = btnTag
    a.save()
//アクティブになった時にボタンのテキストの色を白に
self.buttonArray[btnTag - 1].setTitleColor(UIColor(hex: self.common.white , alpha: 1), for: UIControl.State())
}
    
//ラベルの値を更新する(長押し認識してから2秒後に実行される)
func updateLabel() {
    //貯金額を更新
    MainViewController.totalSavings += btnTag
    //合計日数を更新
    whatDay += 1
    //貯金額ラベルを更新
    moneyLabel.text = "貯金額 \(MainViewController.totalSavings)円"
    //合計日数ラベルを更新
    dayLabel.text = "\(whatDay)日目"
}
    
//ぽよよんアニメーション
func animateView(_ viewToAnimate:UIView) {
    UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseIn, animations: {
        viewToAnimate.transform = CGAffineTransform(scaleX: 1.58, y: 1.58)
    }) { (_) in
        UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 10, options: .curveEaseOut, animations: {
            viewToAnimate.transform = .identity
 
        }, completion: nil)
    }
}
}

MenuViewController.swift

import UIKit
import RealmSwift
import StoreKit

class MenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    

//Commonクラスのインスタンス生成
let common = Common()
//Cancelボタン
var cancelBtn: UIBarButtonItem!
//テーブルビューの変数
var tableView: UITableView?
//セル用の配列
let items = ["貯金忘れ防止アラーム", "使い方", "シェア", "評価", "データリセット"]
let icons = ["menuIcon-1", "menuIcon-2", "menuIcon-3", "menuIcon-4", "menuIcon-5"]
//Realm通知
var token : NotificationToken?

override func viewDidLoad() {
    super.viewDidLoad()
    
//Realmのデータが更新されたら検知する
let realm = try! Realm()
token = realm.observe({ (notification:Realm.Notification, realm) in
  print("realm is updated")
    //アラートの表示
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        let alertController = self.common.originalAlert(title: "削除しました", message: "")
        self.present(alertController, animated: true, completion: nil)
    }
})

//ナビゲーションコントローラーまわり
//タイトル
self.title = "メニュー"
//背景色
self.navigationController?.navigationBar.barTintColor = UIColor(hex: self.common.main , alpha: 1)
//セーフエリアとの境目の線を消す
self.navigationController?.navigationBar.shadowImage = UIImage()
//フォントの設定
self.navigationController?.navigationBar.titleTextAttributes
    = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont(name: "HiraMaruProN-W4", size: 20)!]
//Cancelボタンを追加
cancelBtn = UIBarButtonItem(title: "Cancel", style: .done, target: self, action: #selector(self.cancelBtnTapped))
cancelBtn.tintColor = UIColor(hex: self.common.white , alpha: 1)
self.navigationItem.leftBarButtonItem = cancelBtn
    
//テーブルビューの設定
self.tableView = {
    let tableView = UITableView(frame: self.view.bounds, style: .plain)
    tableView.autoresizingMask = [
      .flexibleWidth,
      .flexibleHeight
    ]

    tableView.delegate = self
    tableView.dataSource = self

    self.view.addSubview(tableView)

    tableView.register(MenuTableViewCell.self, forCellReuseIdentifier: "Cell")
    
    return tableView

  }()
}

//Cancelボタンのメソッド
@objc func cancelBtnTapped() {
    //前の画面に戻る
    dismiss(animated: true, completion: nil)
}

    
//テーブルビューまわり
//テーブルビューのセクションの数を設定
func numberOfSections(in tableView: UITableView) -> Int {
  return 1
}
//テーブルビューのセルの数を設定
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  //配列itemsの数になるように設定
  return self.items.count
}

//セルの設定
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //カスタムをセルを使えるように設定
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! MenuTableViewCell
    //ラベルのテキストとアイコン画像のファイル名を設定
    cell.setCell(item: self.items[indexPath.row], iconName: icons[indexPath.row])
    //セルの右側に<を付ける
    cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
    //セル選択時に背景色を変更しない
    cell.selectionStyle = UITableViewCell.SelectionStyle.none
    return cell
}
    
//セルを選択した時の設定
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.row {

case 0:
print("アラーム設定画面へ")
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
//[.alert, .badge, .sound]と指定されているので、「アラート、バッジ、サウンド」の3つに対しての許可をリクエストした
if granted {
    //「許可」が押された場合
    //アラーム設定画面へ遷移(メインスレッド)
    DispatchQueue.main.async {
        let NotificationViewController = NotificationViewController.init()
        let NV = UINavigationController.init(rootViewController: NotificationViewController)
        self.present(NV, animated: true, completion: nil)
    }
    //UserDefaultsにtrueを保持させる
    UserDefaults.standard.set(true, forKey: "permit")

} else {
    //「許可しない」が押された場合
    if UserDefaults.standard.bool(forKey: "permit") == true {
    //通知を許可しない状態で2回目以降「貯金忘れ防止アラーム」をタップした時にアラートを出す
    DispatchQueue.main.async {
        let alertController = DOAlertController(title: "通知設定が許可されていません", message: "", preferredStyle: .alert)
        let cancelAction = DOAlertAction(title: "Cancel", style: .cancel, handler: nil)
        let okAction = DOAlertAction(title: "設定画面へ", style: .default) { action in
            //OKが押された時、365bankの設定画面へ遷移
            guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
              return
            }
            if UIApplication.shared.canOpenURL(settingsUrl)  {
              if #available(iOS 10.0, *) {
                UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
                })
              }
              else  {
                UIApplication.shared.openURL(settingsUrl)
              }
            }
        }
        alertController.alertViewBgColor = UIColor.white
        alertController.alertView.layer.cornerRadius = 15
        alertController.titleFont = UIFont(name: "HiraMaruProN-W4", size: 16)
        alertController.titleTextColor = UIColor(hex: self.common.text, alpha: 1)
        alertController.buttonFont[.cancel] = UIFont(name: "HiraMaruProN-W4", size: 20)
        alertController.buttonBgColor[.cancel] = UIColor(hex: self.common.main, alpha: 1)
        alertController.buttonBgColorHighlighted[.cancel] = UIColor(hex: self.common.main, alpha: 1)
        alertController.buttonBgColor[.default] =  UIColor(hex: self.common.sub, alpha: 1)
        alertController.buttonBgColorHighlighted[.default] =  UIColor(hex: self.common.sub, alpha: 1)
        alertController.addAction(cancelAction)
        alertController.addAction(okAction)
        self.present(alertController, animated: true, completion: nil)
    }
} else {
    //通知を許可しないを押して一回目の処理
    UserDefaults.standard.set(true, forKey: "permit")
}
}
}

case 1:
    print("ウォークスルー画面へ")
    DispatchQueue.main.async {
        let FirstViewController = FirstViewController.init()
        self.present(FirstViewController, animated: true, completion: nil)
    }
case 2:
    print("シェア")
    share()
case 3:
    print("評価")
    //レビューダイアログを表示
    if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
        SKStoreReviewController.requestReview(in: scene)
    }
case 4:
    print("データリセット")
    deleteAlert()
default:
    break
    }
}

func share() {
    //URLは自分のアプリのappstoreURLを設定する
    let shareUrl = ShareItem(URL(string: "https://apps.apple.com/jp/app/%E3%82%82%E3%81%97%E3%82%82%E8%B2%AF%E9%87%91%E7%AE%B1/id1553026256")!)
    let activityVC = UIActivityViewController(activityItems: [shareUrl], applicationActivities: nil)
    //ipadの場合の処理
    if UIDevice.current.userInterfaceIdiom == .pad {
        let screenSize = UIScreen.main.bounds
        activityVC.popoverPresentationController?.sourceView = self.view
        activityVC.popoverPresentationController?.sourceRect = CGRect(x:screenSize.size.width/2, y: screenSize.size.height-200, width: 0, height: 0)
    }
    present(activityVC, animated: true, completion: nil)
}

func deleteAlert() {
    //タイトルとメッセージの設定
    let alertController = DOAlertController(title: "本当に削除しますか?", message: "削除すると元には戻せません!", preferredStyle: .alert)
    //キャンセルボタンの設定
    let cancelAction = DOAlertAction(title: "Cancel", style: .cancel, handler: nil)
    //OKボタンの設定
    let okAction = DOAlertAction(title: "OK", style: .default) { action in
        //メインビューコントローラー取得
        let nc = self.presentingViewController as! UINavigationController
        let vcNum = nc.viewControllers.count
        let firstVC = nc.viewControllers[vcNum - 1] as! MainViewController
        //先に色とか変更しちゃう
        firstVC.deleteAlertBack()
        //realmDBを全て削除
        let realm = try! Realm()
        try! realm.write {
          realm.deleteAll()
        }
    }

    // アラートビューの背景色
    alertController.alertViewBgColor = UIColor.white
    alertController.alertView.layer.cornerRadius = 15
    // タイトルのフォント、文字色
    alertController.titleFont = UIFont(name: "HiraMaruProN-W4", size: 17)
    alertController.titleTextColor = UIColor(hex: "8a6b52", alpha: 1)
    alertController.messageFont = UIFont(name: "HiraMaruProN-W4", size: 17)
    alertController.messageTextColor = UIColor.red
    //キャンセルボタンの設定
    alertController.buttonFont[.cancel] = UIFont(name: "HiraMaruProN-W4", size: 17)
    alertController.buttonBgColor[.cancel] = UIColor(hex: common.main, alpha: 1)
    alertController.buttonBgColorHighlighted[.cancel] = UIColor(hex: common.main, alpha: 1)
    //OKボタンの設定
    alertController.buttonFont[.default] = UIFont(name: "HiraMaruProN-W4", size: 17)
    alertController.buttonBgColor[.default] = UIColor.red
    alertController.buttonBgColorHighlighted[.default] = UIColor.red

    // アラートコントローラにアクションを追加
    alertController.addAction(cancelAction)
    alertController.addAction(okAction)

    // 表示
    present(alertController, animated: true, completion: nil)
}

}

class ShareItem<T>: NSObject, UIActivityItemSource {

    private let item: T

    init(_ item: T) {
        self.item = item
    }

    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return item
    }

    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
        return activityViewControllerPlaceholderItem(activityViewController)
    }
}

MenuTableViewCell.swift

import UIKit

class MenuTableViewCell: UITableViewCell {

//Commonクラスのインスタンス生成
let common = Common()

//左側のアイコン
let menuIcon: UIImageView = {
    let ImageView = UIImageView()
    ImageView.translatesAutoresizingMaskIntoConstraints = false
    return ImageView
}()
//右側のラベル
let itemName: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

//レイアウト
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: .subtitle, reuseIdentifier: reuseIdentifier )
    //アイコンとラベルを表示
    addSubview(itemName)
    addSubview(menuIcon)
    //アイコンの設定
    menuIcon.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 20).isActive = true
    menuIcon.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
    menuIcon.topAnchor.constraint(equalTo: self.topAnchor, constant: 20).isActive = true
    menuIcon.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.09).isActive = true
    menuIcon.heightAnchor.constraint(equalTo: menuIcon.widthAnchor, multiplier: 1).isActive = true
    //ラベルの設定
    itemName.textColor =  UIColor(hex: self.common.text, alpha: 1)
    itemName.font = UIFont(name: "HiraMaruProN-W4", size: 17)
    itemName.leftAnchor.constraint(equalTo: menuIcon.rightAnchor, constant: 20).isActive = true
    itemName.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
}
    
//セルにテキストと画像の名前を設定
func setCell(item: String, iconName: String) {
    itemName.text = item
    menuIcon.image = UIImage(named: iconName)
}
    
required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
    
override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code
}

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    // Configure the view for the selected state
}
}

NotificationViewController.swift

import UIKit

class NotificationViewController: UIViewController {
//Commonクラスのインスタンス生成
let common = Common()
//Cancelボタン
var cancelBtn: UIBarButtonItem!
//トップラベルを入れるビュー
let topView: UIView = UIView()
//トップのラベル
let topLabel: UILabel = UILabel()
//通知設定のラベル
let notificationLabel: UILabel = UILabel()
//Switch
let notificationSwitch: UISwitch = UISwitch()
//datePicker
let datePicker: UIDatePicker = UIDatePicker()

override func viewDidLoad() {
super.viewDidLoad()
//レイアウト
layout()
//datePickerに時刻をセット
if let value = UserDefaults.standard.object(forKey: "Date") as? Date {
    datePicker.date = value
}
// これから予定されている通知を取得
UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
    // これから予定されている通知がある=通知の設定は行われている
    if requests.count > 0 {
        // UIの変更を伴うので、メインスレッドで処理を行わせる
        DispatchQueue.main.async {
            // スイッチをオンに変更
            self.notificationSwitch.isOn = true
        }
    }
}
}

//Cancelボタンのメソッド
@objc func cancelBtnTapped() {
    //前の画面に戻る
    dismiss(animated: true, completion: nil)
}


func layout() {
    //バックグランドカラー設定
    self.view.backgroundColor = UIColor(hex: common.background , alpha: 1)

    //オブジェクト追加
    self.view.addSubview(topView)
    topView.addSubview(topLabel)
    self.view.addSubview(notificationLabel)
    self.view.addSubview(notificationSwitch)
    self.view.addSubview(datePicker)

    //トップビューのレイアウト
    topView.translatesAutoresizingMaskIntoConstraints = false
    topView.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, multiplier: 1).isActive = true
    topView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.08).isActive = true
    topView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
    topView.backgroundColor = UIColor(hex: common.white , alpha: 1)

    //トップラベルのレイアウト
    topLabel.translatesAutoresizingMaskIntoConstraints = false
    topLabel.leftAnchor.constraint(equalTo: topView.leftAnchor, constant: 20.0).isActive = true
    topLabel.centerYAnchor.constraint(equalTo: topView.centerYAnchor).isActive = true
    topLabel.font = UIFont(name: "HiraMaruProN-W4", size: 16)
    topLabel.textColor = UIColor(hex: common.text , alpha: 1)
    topLabel.text = "貯金忘れ防止に通知設定ができます"

    //アラートラベルのレイアウト
    notificationLabel.translatesAutoresizingMaskIntoConstraints = false
    notificationLabel.leftAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leftAnchor, constant: 20.0).isActive = true
    notificationLabel.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: 30.0).isActive = true
    notificationLabel.font = UIFont(name: "HiraMaruProN-W4", size: 16)
    notificationLabel.textColor = UIColor(hex: common.text , alpha: 1)
    notificationLabel.text = "通知設定"

    //Switchのレイアウト
    notificationSwitch.translatesAutoresizingMaskIntoConstraints = false
    notificationSwitch.rightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.rightAnchor, constant: -20.0).isActive = true
    notificationSwitch.centerYAnchor.constraint(equalTo: notificationLabel.centerYAnchor).isActive = true
    //オンにした時の色
    notificationSwitch.onTintColor = UIColor(hex: common.sub, alpha: 1)
    //UISwitch値が変更された時に呼び出すメソッドの設定
    notificationSwitch.addTarget(self, action: #selector(changeSwitch), for: UIControl.Event.valueChanged)//追加
    //一旦オフ・操作不可にする
    notificationSwitch.isOn = false//追加

    //datePickerのレイアウト
    datePicker.translatesAutoresizingMaskIntoConstraints = false
    datePicker.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, multiplier: 1).isActive = true
    datePicker.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.4).isActive = true
    datePicker.topAnchor.constraint(equalTo: notificationLabel.bottomAnchor, constant: 30).isActive = true
    //スタイル
    datePicker.preferredDatePickerStyle = .wheels
    //表示させるモードの設定。今回は時間だけを表示
    datePicker.datePickerMode = UIDatePicker.Mode.time
    //テキストカラ-
    datePicker.setValue(UIColor(hex: common.text, alpha: 1), forKey: "textColor")
    //背景色
    datePicker.backgroundColor = UIColor.white
    datePicker.addTarget(self, action: #selector(changeDatePicker), for: UIControl.Event.valueChanged)
    //ナビゲーションコントローラーまわり
    //タイトル
    self.title = "アラーム設定"
    //背景色
    self.navigationController?.navigationBar.barTintColor = UIColor(hex: self.common.main , alpha: 1)
    //セーフエリアとの境目の線を消す
    self.navigationController?.navigationBar.shadowImage = UIImage()
    //フォントの設定
    self.navigationController?.navigationBar.titleTextAttributes
        = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont(name: "HiraMaruProN-W4", size: 20)!]
    //Cancelボタンを追加
    cancelBtn = UIBarButtonItem(title: "Cancel", style: .done, target: self, action: #selector(self.cancelBtnTapped))
    cancelBtn.tintColor = UIColor(hex: self.common.white , alpha: 1)
    self.navigationItem.leftBarButtonItem = cancelBtn
}

//追加
@objc func changeSwitch(_ sender: Any) {

    if (sender as AnyObject).isOn {

        // 通知を管理するクラスのシングルトンを取得
        let center = UNUserNotificationCenter.current()

        // datePickerで指定された時間を取得
        let date = self.datePicker.date

        //datePickerに時刻を保存
        let valueToSave = self.datePicker.date
        UserDefaults.standard.set(valueToSave, forKey: "Date")

        // 予定されている全ての通知の設定を削除してから通知の設定を行う
        center.removeAllPendingNotificationRequests()

        // 通知の内容を設定 ======
        let content = UNMutableNotificationContent()

        // 通知のタイトル
        content.title = "今日は貯金しましたか?"

        // 通知のサブタイトル(iOS10から使用可能)
        content.subtitle = "現在の貯金額は\(MainViewController.totalSavings)円です!"

        // 通知の本文
        //content.body = ""

        // 通知音の設定
        content.sound = UNNotificationSound.default

        // カレンダーのインスタンスを生成
        let calendar = Calendar.current

        // 日付や時間をを数値で取得できるDateComponentsを作成
        // 今回はdatePickerで設定した時間を基に時間、分のみを取得
        let dateComponents = calendar.dateComponents([.hour, .minute], from: date)

        // どの時間で通知をするかを設定するか、繰り返し通知するかの設定
        // dateComponentsで設定した時間で通知。今回は繰り返し通知を行うので、repeatsはtrue
        let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents,repeats: true)

        // 通知の識別子を設定
        let identifier = "Notification"

        // 通知の内容と時間を基にリクエストを作成
        let request = UNNotificationRequest(identifier:identifier,
                                            content:content,
                                            trigger:trigger)
        // 通知を設定する
        center.add(request, withCompletionHandler: nil)

        //アラート
        let alertController = self.common.originalAlert(title: "アラームをセットしました", message: "")
        self.present(alertController, animated: true, completion: nil)

    } else {
        // 予定されている通知を解除する
        UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
        //アラート
        let alertController = self.common.originalAlert(title: "アラームを解除しました", message: "")
        self.present(alertController, animated: true, completion: nil)
    }
}
@objc func changeDatePicker(_ sender: Any) {
    //DatePickerの値が変更されて、SwitchがオンだったらSwitchをオフにして
    //アラーム設定を解除する
    if self.notificationSwitch.isOn == true {
        self.notificationSwitch.isOn = false
        UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
    }
}
}

RealmDB.swift

import Foundation
import RealmSwift

class Btn365: Object {
//おまじない
static let realm = try! Realm()
//必要なデータの変数を作る。
@objc dynamic var tagNum: Int = 0

//データの登録
static func create() -> Btn365 {
        let addDate = Btn365()
        return addDate
}
//データの保存
func save() {
    try! Btn365.realm.write {
        Btn365.realm.add(self)
    }
}
//データの消去
static func delete(tag: Int) {
    if let del = Btn365.realm.objects(Btn365.self).filter("tagNum == \(tag)").first {
        try! Btn365.realm.write {
            Btn365.realm.delete(del)
        }
    }
}
    
}

SceneDelegate.swift

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
    //初回起動時判定。2回目以降はfalseに設定してウォークスルーをスルー
    let ud = UserDefaults.standard
    let firstLunchKey = "firstLunch"
    if ud.bool(forKey: firstLunchKey) {
        ud.set(false, forKey: firstLunchKey)
        ud.synchronize()
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = FirstViewController()
        self.window = window
        window.makeKeyAndVisible()
    } else {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UINavigationController(rootViewController: MainViewController())
        self.window = window
        window.makeKeyAndVisible()
    }
}
}

func sceneDidDisconnect(_ scene: UIScene) {
    // Called as the scene is being released by the system.
    // This occurs shortly after the scene enters the background, or when its session is discarded.
    // Release any resources associated with this scene that can be re-created the next time the scene connects.
    // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
}

func sceneDidBecomeActive(_ scene: UIScene) {
    // Called when the scene has moved from an inactive state to an active state.
    // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}

func sceneWillResignActive(_ scene: UIScene) {
    // Called when the scene will move from an active state to an inactive state.
    // This may occur due to temporary interruptions (ex. an incoming phone call).
}

func sceneWillEnterForeground(_ scene: UIScene) {
    // Called as the scene transitions from the background to the foreground.
    // Use this method to undo the changes made on entering the background.
}

func sceneDidEnterBackground(_ scene: UIScene) {
    // Called as the scene transitions from the foreground to the background.
    // Use this method to save data, release shared resources, and store enough scene-specific state information
    // to restore the scene back to its current state.
}

}

AppDelegate.swift

import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
var window: UIWindow?
 
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    //スプラッシュの為に少し起動を遅らせる
    sleep(1)
    //ナビゲーションバーの色がデフォルトだと半透明になってしまうので、半透明にさせないよう設定
    UINavigationBar.appearance().isTranslucent = false
    //初回起動時判定。初期値をtrueに設定。アプリの更新では無視されます。
    let ud = UserDefaults.standard
    let firstLunchKey = "firstLunch"
    let firstLunch = [firstLunchKey: true]
    ud.register(defaults: firstLunch)
    
    return true
}

// MARK: UISceneSession Lifecycle

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

Color.swift

import UIKit

extension UIColor {
    convenience init(hex: String, alpha: CGFloat = 1.0) {
        let v = Int("000000" + hex, radix: 16) ?? 0
        let r = CGFloat(v / Int(powf(256, 2)) % 256) / 255
        let g = CGFloat(v / Int(powf(256, 1)) % 256) / 255
        let b = CGFloat(v / Int(powf(256, 0)) % 256) / 255
        self.init(red: r, green: g, blue: b, alpha: min(max(alpha, 0), 1))
    }
}

Common.swift

import Foundation
import UIKit

class Common: NSObject {
//カラー指定
let background = "faf2ed"
let main = "eeaa7b"
let sub = "66b9bf"
let text = "8a6b52"
let Overlay = "000000"
let white = "ffffff"

func originalAlert(title: String, message: String) -> DOAlertController {
    // アラートコントローラ: タイトル、メッセージ、アラートスタイル(Alert/ActionSheet)を設定
    let alertController = DOAlertController(title: title, message: message, preferredStyle: .alert)
    
    // アクション: ボタンの文字、ボタンスタイル(Default/Cancel/Destructive)、ボタンを押した時の処理を設定
    let cancelAction = DOAlertAction(title: "OK", style: .cancel, handler: nil)
    
    // アラートビューの背景色
    alertController.alertViewBgColor = UIColor.white
    alertController.alertView.layer.cornerRadius = 15
    // タイトルのフォント、文字色
    alertController.titleFont = UIFont(name: "HiraMaruProN-W4", size: 14)
    alertController.titleTextColor = UIColor(hex: text, alpha: 1)
    
    alertController.messageTextColor = UIColor(hex: text, alpha: 1)
    alertController.messageFont = UIFont(name: "HiraMaruProN-W4", size: 14)
    // ボタンのフォント、文字色、背景色(通常時/ハイライト時)
    // [.Default]を[.Cancel][.Destructive]に変更すればそれぞれのアクションごとにボタンのカスタマイズが可能
    alertController.buttonFont[.cancel] = UIFont(name: "HiraMaruProN-W4", size: 17)
    //            alertController.buttonTextColor[.cancel] = UIColor(hex: "66b9bf", alpha: 1)
    alertController.buttonBgColor[.cancel] = UIColor(hex: main, alpha: 1)
    alertController.buttonBgColorHighlighted[.cancel] = UIColor(hex: main, alpha: 1)
    
    // アラートコントローラにアクションを追加
    alertController.addAction(cancelAction)

    return alertController
}
}

DOAlertController.swift

import Foundation
import UIKit

let DOAlertActionEnabledDidChangeNotification = "DOAlertActionEnabledDidChangeNotification"

public enum DOAlertActionStyle : Int {
    case `default`
    case cancel
    case destructive
}

public enum DOAlertControllerStyle : Int {
    case actionSheet
    case alert
}

// MARK: DOAlertAction Class

open class DOAlertAction : NSObject, NSCopying {
    open var title: String
    open var style: DOAlertActionStyle
    var handler: ((DOAlertAction?) -> Void)!
    open var enabled: Bool {
        didSet {
            if (oldValue != enabled) {
                NotificationCenter.default.post(name: Notification.Name(rawValue: DOAlertActionEnabledDidChangeNotification), object: nil)
            }
        }
    }
    
    required public init(title: String, style: DOAlertActionStyle, handler: ((DOAlertAction?) -> Void)!) {
        self.title = title
        self.style = style
        self.handler = handler
        self.enabled = true
    }
    
    open func copy(with zone: NSZone?) -> Any {
        let copy = type(of: self).init(title: title, style: style, handler: handler)
        copy.enabled = self.enabled
        return copy
    }
}

// MARK: DOAlertAnimation Class

open class DOAlertAnimation : NSObject, UIViewControllerAnimatedTransitioning {
    
    let isPresenting: Bool
    
    init(isPresenting: Bool) {
        self.isPresenting = isPresenting
    }
    
    open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        if (isPresenting) {
            return 0.45
        } else {
            return 0.25
        }
    }
    
    open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if (isPresenting) {
            self.presentAnimateTransition(transitionContext)
        } else {
            self.dismissAnimateTransition(transitionContext)
        }
    }
    
    func presentAnimateTransition(_ transitionContext: UIViewControllerContextTransitioning) {
        
        let alertController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as! DOAlertController
        let containerView = transitionContext.containerView
        
        alertController.overlayView.alpha = 0.0
        if (alertController.isAlert()) {
            alertController.alertView.alpha = 0.0
            alertController.alertView.center = alertController.view.center
            alertController.alertView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
        } else {
            alertController.alertView.transform = CGAffineTransform(translationX: 0, y: alertController.alertView.frame.height)
        }
        containerView.addSubview(alertController.view)
        
        UIView.animate(withDuration: 0.25,
                                   animations: {
                                    alertController.overlayView.alpha = 1.0
                                    if (alertController.isAlert()) {
                                        alertController.alertView.alpha = 1.0
                                        alertController.alertView.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
                                    } else {
                                        let bounce = alertController.alertView.frame.height / 480 * 10.0 + 10.0
                                        alertController.alertView.transform = CGAffineTransform(translationX: 0, y: -bounce)
                                    }
        },
                                   completion: { finished in
                                    UIView.animate(withDuration: 0.2,
                                                               animations: {
                                                                alertController.alertView.transform = CGAffineTransform.identity
                                    },
                                                               completion: { finished in
                                                                if (finished) {
                                                                    transitionContext.completeTransition(true)
                                                                }
                                    })
        })
    }
    
    func dismissAnimateTransition(_ transitionContext: UIViewControllerContextTransitioning) {
        
        let alertController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as! DOAlertController
        
        UIView.animate(withDuration: self.transitionDuration(using: transitionContext),
                                   animations: {
                                    alertController.overlayView.alpha = 0.0
                                    if (alertController.isAlert()) {
                                        alertController.alertView.alpha = 0.0
                                        alertController.alertView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
                                    } else {
                                        alertController.containerView.transform = CGAffineTransform(translationX: 0, y: alertController.alertView.frame.height)
                                    }
        },
                                   completion: { finished in
                                    transitionContext.completeTransition(true)
        })
    }
}

// MARK: DOAlertController Class
@objc(DOAlertController)
open class DOAlertController : UIViewController, UITextFieldDelegate, UIViewControllerTransitioningDelegate {
    
    // Message
    open var message: String?
    
    // AlertController Style
    fileprivate(set) var preferredStyle: DOAlertControllerStyle?
    
    // OverlayView
    fileprivate var overlayView = UIView()
    open var overlayColor = UIColor(red:0, green:0, blue:0, alpha:0.5)
    
    // ContainerView
    fileprivate var containerView = UIView()
    fileprivate var containerViewBottomSpaceConstraint: NSLayoutConstraint?
    
    // AlertView
    open var alertView = UIView()
    open var alertViewBgColor = UIColor(red:239/255, green:240/255, blue:242/255, alpha:1.0)
    fileprivate var alertViewWidth: CGFloat = 270.0
    fileprivate var alertViewHeightConstraint: NSLayoutConstraint?
    fileprivate var alertViewPadding: CGFloat = 15.0
    fileprivate var innerContentWidth: CGFloat = 240.0
    fileprivate let actionSheetBounceHeight: CGFloat = 20.0
    
    // TextAreaScrollView
    fileprivate var textAreaScrollView = UIScrollView()
    fileprivate var textAreaHeight: CGFloat = 0.0
    
    // TextAreaView
    fileprivate var textAreaView = UIView()
    
    // TextContainer
    fileprivate var textContainer = UIView()
    fileprivate var textContainerHeightConstraint: NSLayoutConstraint?
    
    // TitleLabel
    open var titleLabel = UILabel()
    open var titleFont = UIFont(name: "HelveticaNeue-Bold", size: 18)
    open var titleTextColor = UIColor(red:77/255, green:77/255, blue:77/255, alpha:1.0)
    
    // MessageView
    open var messageView = UILabel()
    open var messageFont = UIFont(name: "HelveticaNeue", size: 15)
    open var messageTextColor = UIColor(red:77/255, green:77/255, blue:77/255, alpha:1.0)
    
    // TextFieldContainerView
    open var textFieldContainerView = UIView()
    open var textFieldBorderColor = UIColor(red: 203.0/255, green: 203.0/255, blue: 203.0/255, alpha: 1.0)
    
    // TextFields
    fileprivate(set) var textFields: [AnyObject]?
    fileprivate let textFieldHeight: CGFloat = 30.0
    open var textFieldBgColor = UIColor.white
    fileprivate let textFieldCornerRadius: CGFloat = 4.0
    
    // ButtonAreaScrollView
    fileprivate var buttonAreaScrollView = UIScrollView()
    fileprivate var buttonAreaScrollViewHeightConstraint: NSLayoutConstraint?
    fileprivate var buttonAreaHeight: CGFloat = 0.0
    
    // ButtonAreaView
    fileprivate var buttonAreaView = UIView()
    
    // ButtonContainer
    fileprivate var buttonContainer = UIView()
    fileprivate var buttonContainerHeightConstraint: NSLayoutConstraint?
    fileprivate let buttonHeight: CGFloat = 44.0
    fileprivate var buttonMargin: CGFloat = 10.0
    
    // Actions
    open fileprivate(set) var actions: [AnyObject] = []
    
    // Buttons
    open var buttons = [UIButton]()
    open var buttonFont: [DOAlertActionStyle : UIFont] = [
        .default : UIFont(name: "HelveticaNeue-Bold", size: 16) ?? UIFont.systemFont(ofSize: 16),
        .cancel  : UIFont(name: "HelveticaNeue-Bold", size: 16) ?? UIFont.systemFont(ofSize: 16),
        .destructive  : UIFont(name: "HelveticaNeue-Bold", size: 16) ?? UIFont.systemFont(ofSize: 16)
    ]
    open var buttonTextColor: [DOAlertActionStyle : UIColor] = [
        .default : UIColor.white,
        .cancel  : UIColor.white,
        .destructive  : UIColor.white
    ]
    open var buttonBgColor: [DOAlertActionStyle : UIColor] = [
        .default : UIColor(red:52/255, green:152/255, blue:219/255, alpha:1),
        .cancel  : UIColor(red:127/255, green:140/255, blue:141/255, alpha:1),
        .destructive  : UIColor(red:231/255, green:76/255, blue:60/255, alpha:1)
    ]
    open var buttonBgColorHighlighted: [DOAlertActionStyle : UIColor] = [
        .default : UIColor(red:74/255, green:163/255, blue:223/255, alpha:1),
        .cancel  : UIColor(red:140/255, green:152/255, blue:153/255, alpha:1),
        .destructive  : UIColor(red:234/255, green:97/255, blue:83/255, alpha:1)
    ]
    fileprivate var buttonCornerRadius: CGFloat = 4.0
    
    fileprivate var layoutFlg = false
    fileprivate var keyboardHeight: CGFloat = 0.0
    fileprivate var cancelButtonTag = 0
    
    // Initializer
    public convenience init(title: String?, message: String?, preferredStyle: DOAlertControllerStyle) {
        self.init(nibName: nil, bundle: nil)
        
        self.title = title
        self.message = message
        self.preferredStyle = preferredStyle
        
        self.providesPresentationContextTransitionStyle = true
        self.definesPresentationContext = true
        self.modalPresentationStyle = UIModalPresentationStyle.custom
        
        // NotificationCenter
        NotificationCenter.default.addObserver(self, selector: #selector(DOAlertController.handleAlertActionEnabledDidChangeNotification(_:)), name: NSNotification.Name(rawValue: DOAlertActionEnabledDidChangeNotification), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(DOAlertController.handleKeyboardWillShowNotification(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(DOAlertController.handleKeyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
        
        // Delegate
        self.transitioningDelegate = self
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName:nibNameOrNil, bundle:nibBundleOrNil)
    }
    
    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func currentOrientation() -> UIInterfaceOrientation {
        return UIScreen.main.bounds.size.width < UIScreen.main.bounds.size.height ? .portrait : .landscapeLeft
    }
    
    override open func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }
    
    override open func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        if (!isAlert() && cancelButtonTag != 0) {
            let tapGesture = UITapGestureRecognizer(target: self, action: #selector(DOAlertController.handleContainerViewTapGesture(_:)))
            containerView.addGestureRecognizer(tapGesture)
        }
    }
    
    open override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        layoutView(self.presentingViewController)
    }
    
    
    open func layoutView(_ presenting: UIViewController?) {
        if (layoutFlg) { return }
        layoutFlg = true
        
        // Screen Size
        var screenSize = presenting != nil ? presenting!.view.bounds.size : UIScreen.main.bounds.size
        if ((UIDevice.current.systemVersion as NSString).floatValue < 8.0) {
            if (currentOrientation().isLandscape) {
                screenSize = CGSize(width: screenSize.height, height: screenSize.width)
            }
        }
        
        // variable for ActionSheet
        if (!isAlert()) {
            alertViewWidth =  screenSize.width
            alertViewPadding = 8.0
            innerContentWidth = (screenSize.height > screenSize.width) ? screenSize.width - alertViewPadding * 2 : screenSize.height - alertViewPadding * 2
            buttonMargin = 8.0
            buttonCornerRadius = 6.0
        }
        
        // self.view
        self.view.frame.size = screenSize
        
        // OverlayView
        self.view.addSubview(overlayView)
        
        // ContainerView
        self.view.addSubview(containerView)
        
        // AlertView
        containerView.addSubview(alertView)
        
        // TextAreaScrollView
        alertView.addSubview(textAreaScrollView)
        
        // TextAreaView
        textAreaScrollView.addSubview(textAreaView)
        
        // TextContainer
        textAreaView.addSubview(textContainer)
        
        // ButtonAreaScrollView
        alertView.addSubview(buttonAreaScrollView)
        
        // ButtonAreaView
        buttonAreaScrollView.addSubview(buttonAreaView)
        
        // ButtonContainer
        buttonAreaView.addSubview(buttonContainer)
        
        //------------------------------
        // Layout Constraint
        //------------------------------
        overlayView.translatesAutoresizingMaskIntoConstraints = false
        containerView.translatesAutoresizingMaskIntoConstraints = false
        alertView.translatesAutoresizingMaskIntoConstraints = false
        textAreaScrollView.translatesAutoresizingMaskIntoConstraints = false
        textAreaView.translatesAutoresizingMaskIntoConstraints = false
        textContainer.translatesAutoresizingMaskIntoConstraints = false
        buttonAreaScrollView.translatesAutoresizingMaskIntoConstraints = false
        buttonAreaView.translatesAutoresizingMaskIntoConstraints = false
        buttonContainer.translatesAutoresizingMaskIntoConstraints = false
        
        // self.view
        let overlayViewTopSpaceConstraint = NSLayoutConstraint(item: overlayView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1.0, constant: 0.0)
        let overlayViewRightSpaceConstraint = NSLayoutConstraint(item: overlayView, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1.0, constant: 0.0)
        let overlayViewLeftSpaceConstraint = NSLayoutConstraint(item: overlayView, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1.0, constant: 0.0)
        let overlayViewBottomSpaceConstraint = NSLayoutConstraint(item: overlayView, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0.0)
        let containerViewTopSpaceConstraint = NSLayoutConstraint(item: containerView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1.0, constant: 0.0)
        let containerViewRightSpaceConstraint = NSLayoutConstraint(item: containerView, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1.0, constant: 0.0)
        let containerViewLeftSpaceConstraint = NSLayoutConstraint(item: containerView, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1.0, constant: 0.0)
        containerViewBottomSpaceConstraint = NSLayoutConstraint(item: containerView, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0.0)
        self.view.addConstraints([overlayViewTopSpaceConstraint, overlayViewRightSpaceConstraint, overlayViewLeftSpaceConstraint, overlayViewBottomSpaceConstraint, containerViewTopSpaceConstraint, containerViewRightSpaceConstraint, containerViewLeftSpaceConstraint, containerViewBottomSpaceConstraint!])
        
        if (isAlert()) {
            // ContainerView
            let alertViewCenterXConstraint = NSLayoutConstraint(item: alertView, attribute: .centerX, relatedBy: .equal, toItem: containerView, attribute: .centerX, multiplier: 1.0, constant: 0.0)
            let alertViewCenterYConstraint = NSLayoutConstraint(item: alertView, attribute: .centerY, relatedBy: .equal, toItem: containerView, attribute: .centerY, multiplier: 1.0, constant: 0.0)
            containerView.addConstraints([alertViewCenterXConstraint, alertViewCenterYConstraint])
            
            // AlertView
            let alertViewWidthConstraint = NSLayoutConstraint(item: alertView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1.0, constant: alertViewWidth)
            alertViewHeightConstraint = NSLayoutConstraint(item: alertView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: 1000.0)
            alertView.addConstraints([alertViewWidthConstraint, alertViewHeightConstraint!])
            
        } else {
            // ContainerView
            let alertViewCenterXConstraint = NSLayoutConstraint(item: alertView, attribute: .centerX, relatedBy: .equal, toItem: containerView, attribute: .centerX, multiplier: 1.0, constant: 0.0)
            let alertViewBottomSpaceConstraint = NSLayoutConstraint(item: alertView, attribute: .bottom, relatedBy: .equal, toItem: containerView, attribute: .bottom, multiplier: 1.0, constant: actionSheetBounceHeight)
            let alertViewWidthConstraint = NSLayoutConstraint(item: alertView, attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: 1.0, constant: 0.0)
            containerView.addConstraints([alertViewCenterXConstraint, alertViewBottomSpaceConstraint, alertViewWidthConstraint])
            
            // AlertView
            alertViewHeightConstraint = NSLayoutConstraint(item: alertView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: 1000.0)
            alertView.addConstraint(alertViewHeightConstraint!)
        }
        
        // AlertView
        let textAreaScrollViewTopSpaceConstraint = NSLayoutConstraint(item: textAreaScrollView, attribute: .top, relatedBy: .equal, toItem: alertView, attribute: .top, multiplier: 1.0, constant: 0.0)
        let textAreaScrollViewRightSpaceConstraint = NSLayoutConstraint(item: textAreaScrollView, attribute: .right, relatedBy: .equal, toItem: alertView, attribute: .right, multiplier: 1.0, constant: 0.0)
        let textAreaScrollViewLeftSpaceConstraint = NSLayoutConstraint(item: textAreaScrollView, attribute: .left, relatedBy: .equal, toItem: alertView, attribute: .left, multiplier: 1.0, constant: 0.0)
        let textAreaScrollViewBottomSpaceConstraint = NSLayoutConstraint(item: textAreaScrollView, attribute: .bottom, relatedBy: .equal, toItem: buttonAreaScrollView, attribute: .top, multiplier: 1.0, constant: 0.0)
        let buttonAreaScrollViewRightSpaceConstraint = NSLayoutConstraint(item: buttonAreaScrollView, attribute: .right, relatedBy: .equal, toItem: alertView, attribute: .right, multiplier: 1.0, constant: 0.0)
        let buttonAreaScrollViewLeftSpaceConstraint = NSLayoutConstraint(item: buttonAreaScrollView, attribute: .left, relatedBy: .equal, toItem: alertView, attribute: .left, multiplier: 1.0, constant: 0.0)
        let buttonAreaScrollViewBottomSpaceConstraint = NSLayoutConstraint(item: buttonAreaScrollView, attribute: .bottom, relatedBy: .equal, toItem: alertView, attribute: .bottom, multiplier: 1.0, constant: isAlert() ? 0.0 : -actionSheetBounceHeight)
        alertView.addConstraints([textAreaScrollViewTopSpaceConstraint, textAreaScrollViewRightSpaceConstraint, textAreaScrollViewLeftSpaceConstraint, textAreaScrollViewBottomSpaceConstraint, buttonAreaScrollViewRightSpaceConstraint, buttonAreaScrollViewLeftSpaceConstraint, buttonAreaScrollViewBottomSpaceConstraint])
        
        // TextAreaScrollView
        let textAreaViewTopSpaceConstraint = NSLayoutConstraint(item: textAreaView, attribute: .top, relatedBy: .equal, toItem: textAreaScrollView, attribute: .top, multiplier: 1.0, constant: 0.0)
        let textAreaViewRightSpaceConstraint = NSLayoutConstraint(item: textAreaView, attribute: .right, relatedBy: .equal, toItem: textAreaScrollView, attribute: .right, multiplier: 1.0, constant: 0.0)
        let textAreaViewLeftSpaceConstraint = NSLayoutConstraint(item: textAreaView, attribute: .left, relatedBy: .equal, toItem: textAreaScrollView, attribute: .left, multiplier: 1.0, constant: 0.0)
        let textAreaViewBottomSpaceConstraint = NSLayoutConstraint(item: textAreaView, attribute: .bottom, relatedBy: .equal, toItem: textAreaScrollView, attribute: .bottom, multiplier: 1.0, constant: 0.0)
        let textAreaViewWidthConstraint = NSLayoutConstraint(item: textAreaView, attribute: .width, relatedBy: .equal, toItem: textAreaScrollView, attribute: .width, multiplier: 1.0, constant: 0.0)
        textAreaScrollView.addConstraints([textAreaViewTopSpaceConstraint, textAreaViewRightSpaceConstraint, textAreaViewLeftSpaceConstraint, textAreaViewBottomSpaceConstraint, textAreaViewWidthConstraint])
        
        // TextArea
        let textAreaViewHeightConstraint = NSLayoutConstraint(item: textAreaView, attribute: .height, relatedBy: .equal, toItem: textContainer, attribute: .height, multiplier: 1.0, constant: 0.0)
        let textContainerTopSpaceConstraint = NSLayoutConstraint(item: textContainer, attribute: .top, relatedBy: .equal, toItem: textAreaView, attribute: .top, multiplier: 1.0, constant: 0.0)
        let textContainerCenterXConstraint = NSLayoutConstraint(item: textContainer, attribute: .centerX, relatedBy: .equal, toItem: textAreaView, attribute: .centerX, multiplier: 1.0, constant: 0.0)
        textAreaView.addConstraints([textAreaViewHeightConstraint, textContainerTopSpaceConstraint, textContainerCenterXConstraint])
        
        // TextContainer
        let textContainerWidthConstraint = NSLayoutConstraint(item: textContainer, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1.0, constant: innerContentWidth)
        textContainerHeightConstraint = NSLayoutConstraint(item: textContainer, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: 0.0)
        textContainer.addConstraints([textContainerWidthConstraint, textContainerHeightConstraint!])
        
        // ButtonAreaScrollView
        buttonAreaScrollViewHeightConstraint = NSLayoutConstraint(item: buttonAreaScrollView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: 0.0)
        let buttonAreaViewTopSpaceConstraint = NSLayoutConstraint(item: buttonAreaView, attribute: .top, relatedBy: .equal, toItem: buttonAreaScrollView, attribute: .top, multiplier: 1.0, constant: 0.0)
        let buttonAreaViewRightSpaceConstraint = NSLayoutConstraint(item: buttonAreaView, attribute: .right, relatedBy: .equal, toItem: buttonAreaScrollView, attribute: .right, multiplier: 1.0, constant: 0.0)
        let buttonAreaViewLeftSpaceConstraint = NSLayoutConstraint(item: buttonAreaView, attribute: .left, relatedBy: .equal, toItem: buttonAreaScrollView, attribute: .left, multiplier: 1.0, constant: 0.0)
        let buttonAreaViewBottomSpaceConstraint = NSLayoutConstraint(item: buttonAreaView, attribute: .bottom, relatedBy: .equal, toItem: buttonAreaScrollView, attribute: .bottom, multiplier: 1.0, constant: 0.0)
        let buttonAreaViewWidthConstraint = NSLayoutConstraint(item: buttonAreaView, attribute: .width, relatedBy: .equal, toItem: buttonAreaScrollView, attribute: .width, multiplier: 1.0, constant: 0.0)
        buttonAreaScrollView.addConstraints([buttonAreaScrollViewHeightConstraint!, buttonAreaViewTopSpaceConstraint, buttonAreaViewRightSpaceConstraint, buttonAreaViewLeftSpaceConstraint, buttonAreaViewBottomSpaceConstraint, buttonAreaViewWidthConstraint])
        
        // ButtonArea
        let buttonAreaViewHeightConstraint = NSLayoutConstraint(item: buttonAreaView, attribute: .height, relatedBy: .equal, toItem: buttonContainer, attribute: .height, multiplier: 1.0, constant: 0.0)
        let buttonContainerTopSpaceConstraint = NSLayoutConstraint(item: buttonContainer, attribute: .top, relatedBy: .equal, toItem: buttonAreaView, attribute: .top, multiplier: 1.0, constant: 0.0)
        let buttonContainerCenterXConstraint = NSLayoutConstraint(item: buttonContainer, attribute: .centerX, relatedBy: .equal, toItem: buttonAreaView, attribute: .centerX, multiplier: 1.0, constant: 0.0)
        buttonAreaView.addConstraints([buttonAreaViewHeightConstraint, buttonContainerTopSpaceConstraint, buttonContainerCenterXConstraint])
        
        // ButtonContainer
        let buttonContainerWidthConstraint = NSLayoutConstraint(item: buttonContainer, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1.0, constant: innerContentWidth)
        buttonContainerHeightConstraint = NSLayoutConstraint(item: buttonContainer, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: 0.0)
        buttonContainer.addConstraints([buttonContainerWidthConstraint, buttonContainerHeightConstraint!])
        
        //------------------------------
        // Layout & Color Settings
        //------------------------------
        overlayView.backgroundColor = overlayColor
        alertView.backgroundColor = alertViewBgColor
        
        //------------------------------
        // TextArea Layout
        //------------------------------
        let hasTitle: Bool = title != nil && title != ""
        let hasMessage: Bool = message != nil && message != ""
        let hasTextField: Bool = textFields != nil && textFields!.count > 0
        
        var textAreaPositionY: CGFloat = alertViewPadding
        if (!isAlert()) {textAreaPositionY += alertViewPadding}
        
        // TitleLabel
        if (hasTitle) {
            titleLabel.frame.size = CGSize(width: innerContentWidth, height: 0.0)
            titleLabel.numberOfLines = 0
            titleLabel.textAlignment = .center
            titleLabel.font = titleFont
            titleLabel.textColor = titleTextColor
            titleLabel.text = title
            titleLabel.sizeToFit()
            titleLabel.frame = CGRect(x: 0, y: textAreaPositionY, width: innerContentWidth, height: titleLabel.frame.height)
            textContainer.addSubview(titleLabel)
            textAreaPositionY += titleLabel.frame.height + 5.0
        }
        
        // MessageView
        if (hasMessage) {
            messageView.frame.size = CGSize(width: innerContentWidth, height: 0.0)
            messageView.numberOfLines = 0
            messageView.textAlignment = .center
            messageView.font = messageFont
            messageView.textColor = messageTextColor
            messageView.text = message
            messageView.sizeToFit()
            messageView.frame = CGRect(x: 0, y: textAreaPositionY, width: innerContentWidth, height: messageView.frame.height)
            textContainer.addSubview(messageView)
            textAreaPositionY += messageView.frame.height + 5.0
        }
        
        // TextFieldContainerView
        if (hasTextField) {
            if (hasTitle || hasMessage) { textAreaPositionY += 5.0 }
            
            textFieldContainerView.backgroundColor = textFieldBorderColor
            textFieldContainerView.layer.masksToBounds = true
            textFieldContainerView.layer.cornerRadius = textFieldCornerRadius
            textFieldContainerView.layer.borderWidth = 0.5
            textFieldContainerView.layer.borderColor = textFieldBorderColor.cgColor
            textContainer.addSubview(textFieldContainerView)
            
            var textFieldContainerHeight: CGFloat = 0.0
            
            // TextFields
            for (_, obj) in (textFields!).enumerated() {
                let textField = obj as! UITextField
                textField.frame = CGRect(x: 0.0, y: textFieldContainerHeight, width: innerContentWidth, height: textField.frame.height)
                textFieldContainerHeight += textField.frame.height + 0.5
            }
            
            textFieldContainerHeight -= 0.5
            textFieldContainerView.frame = CGRect(x: 0.0, y: textAreaPositionY, width: innerContentWidth, height: textFieldContainerHeight)
            textAreaPositionY += textFieldContainerHeight + 5.0
        }
        
        if (!hasTitle && !hasMessage && !hasTextField) {
            textAreaPositionY = 0.0
        }
        
        // TextAreaScrollView
        textAreaHeight = textAreaPositionY
        textAreaScrollView.contentSize = CGSize(width: alertViewWidth, height: textAreaHeight)
        textContainerHeightConstraint?.constant = textAreaHeight
        
        //------------------------------
        // ButtonArea Layout
        //------------------------------
        var buttonAreaPositionY: CGFloat = buttonMargin
        
        // Buttons
        if (isAlert() && buttons.count == 2) {
            let buttonWidth = (innerContentWidth - buttonMargin) / 2
            var buttonPositionX: CGFloat = 0.0
            for button in buttons {
                let action = actions[button.tag - 1] as! DOAlertAction
                button.titleLabel?.font = buttonFont[action.style]
                button.setTitleColor(buttonTextColor[action.style], for: UIControl.State())
                button.setBackgroundImage(createImageFromUIColor(buttonBgColor[action.style]!), for: UIControl.State())
                button.setBackgroundImage(createImageFromUIColor(buttonBgColorHighlighted[action.style]!), for: .highlighted)
                button.setBackgroundImage(createImageFromUIColor(buttonBgColorHighlighted[action.style]!), for: .selected)
                button.frame = CGRect(x: buttonPositionX, y: buttonAreaPositionY, width: buttonWidth, height: buttonHeight)
                buttonPositionX += buttonMargin + buttonWidth
            }
            buttonAreaPositionY += buttonHeight
        } else {
            for button in buttons {
                let action = actions[button.tag - 1] as! DOAlertAction
                if (action.style != DOAlertActionStyle.cancel) {
                    button.titleLabel?.font = buttonFont[action.style]
                    button.setTitleColor(buttonTextColor[action.style], for: UIControl.State())
                    button.setBackgroundImage(createImageFromUIColor(buttonBgColor[action.style]!), for: UIControl.State())
                    button.setBackgroundImage(createImageFromUIColor(buttonBgColorHighlighted[action.style]!), for: .highlighted)
                    button.setBackgroundImage(createImageFromUIColor(buttonBgColorHighlighted[action.style]!), for: .selected)
                    button.frame = CGRect(x: 0, y: buttonAreaPositionY, width: innerContentWidth, height: buttonHeight)
                    buttonAreaPositionY += buttonHeight + buttonMargin
                } else {
                    cancelButtonTag = button.tag
                }
            }
            
            // Cancel Button
            if (cancelButtonTag != 0) {
                if (!isAlert() && buttons.count > 1) {
                    buttonAreaPositionY += buttonMargin
                }
                let button = buttonAreaScrollView.viewWithTag(cancelButtonTag) as! UIButton
                let action = actions[cancelButtonTag - 1] as! DOAlertAction
                button.titleLabel?.font = buttonFont[action.style]
                button.setTitleColor(buttonTextColor[action.style], for: UIControl.State())
                button.setBackgroundImage(createImageFromUIColor(buttonBgColor[action.style]!), for: UIControl.State())
                button.setBackgroundImage(createImageFromUIColor(buttonBgColorHighlighted[action.style]!), for: .highlighted)
                button.setBackgroundImage(createImageFromUIColor(buttonBgColorHighlighted[action.style]!), for: .selected)
                button.frame = CGRect(x: 0, y: buttonAreaPositionY, width: innerContentWidth, height: buttonHeight)
                buttonAreaPositionY += buttonHeight + buttonMargin
            }
            buttonAreaPositionY -= buttonMargin
        }
        buttonAreaPositionY += alertViewPadding
        
        if (buttons.count == 0) {
            buttonAreaPositionY = 0.0
        }
        
        // ButtonAreaScrollView Height
        buttonAreaHeight = buttonAreaPositionY
        buttonAreaScrollView.contentSize = CGSize(width: alertViewWidth, height: buttonAreaHeight)
        buttonContainerHeightConstraint?.constant = buttonAreaHeight
        
        //------------------------------
        // AlertView Layout
        //------------------------------
        // AlertView Height
        reloadAlertViewHeight()
        alertView.frame.size = CGSize(width: alertViewWidth, height: alertViewHeightConstraint?.constant ?? 150)
    }
    
    // Reload AlertView Height
    func reloadAlertViewHeight() {
        
        var screenSize = self.presentingViewController != nil ? self.presentingViewController!.view.bounds.size : UIScreen.main.bounds.size
        if ((UIDevice.current.systemVersion as NSString).floatValue < 8.0) {
            if (currentOrientation().isLandscape) {
                screenSize = CGSize(width: screenSize.height, height: screenSize.width)
            }
        }
        let maxHeight = screenSize.height - keyboardHeight
        
        // for avoiding constraint error
        buttonAreaScrollViewHeightConstraint?.constant = 0.0
        
        // AlertView Height Constraint
        var alertViewHeight = textAreaHeight + buttonAreaHeight
        if (alertViewHeight > maxHeight) {
            alertViewHeight = maxHeight
        }
        if (!isAlert()) {
            alertViewHeight += actionSheetBounceHeight
        }
        alertViewHeightConstraint?.constant = alertViewHeight
        
        // ButtonAreaScrollView Height Constraint
        var buttonAreaScrollViewHeight = buttonAreaHeight
        if (buttonAreaScrollViewHeight > maxHeight) {
            buttonAreaScrollViewHeight = maxHeight
        }
        buttonAreaScrollViewHeightConstraint?.constant = buttonAreaScrollViewHeight
    }
    
    // Button Tapped Action
    @objc func buttonTapped(_ sender: UIButton) {
        sender.isSelected = true
        let action = actions[sender.tag - 1] as! DOAlertAction
        if (action.handler != nil) {
            action.handler(action)
        }
        self.dismiss(animated: true, completion: nil)
    }
    
    // Handle ContainerView tap gesture
    @objc func handleContainerViewTapGesture(_ sender: AnyObject) {
        // cancel action
        let action = actions[cancelButtonTag - 1] as! DOAlertAction
        if (action.handler != nil) {
            action.handler(action)
        }
        self.dismiss(animated: true, completion: nil)
    }
    
    // UIColor -> UIImage
    func createImageFromUIColor(_ color: UIColor) -> UIImage {
        let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
        UIGraphicsBeginImageContext(rect.size)
        let contextRef: CGContext = UIGraphicsGetCurrentContext()!
        contextRef.setFillColor(color.cgColor)
        contextRef.fill(rect)
        let img: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return img
    }
    
    // MARK : Handle NSNotification Method
    
    @objc func handleAlertActionEnabledDidChangeNotification(_ notification: Notification) {
        for i in 0..<buttons.count {
            buttons[i].isEnabled = actions[i].isEnabled
        }
    }
    
    @objc func handleKeyboardWillShowNotification(_ notification: Notification) {
        if let userInfo = notification.userInfo as? [String: NSValue],
            let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey]?.cgRectValue.size {
            var _keyboardSize = keyboardSize
            if ((UIDevice.current.systemVersion as NSString).floatValue < 8.0) {
                if (currentOrientation().isLandscape) {
                    _keyboardSize = CGSize(width: _keyboardSize.height, height: _keyboardSize.width)
                }
            }
            keyboardHeight = _keyboardSize.height
            reloadAlertViewHeight()
            containerViewBottomSpaceConstraint?.constant = -keyboardHeight
            UIView.animate(withDuration: 0.25, animations: {
                self.view.layoutIfNeeded()
            })
        }
    }
    
    @objc func handleKeyboardWillHideNotification(_ notification: Notification) {
        keyboardHeight = 0.0
        reloadAlertViewHeight()
        containerViewBottomSpaceConstraint?.constant = keyboardHeight
        UIView.animate(withDuration: 0.25, animations: {
            self.view.layoutIfNeeded()
        })
    }
    
    // MARK: Public Methods
    
    // Attaches an action object to the alert or action sheet.
    open func addAction(_ action: DOAlertAction) {
        // Error
        if (action.style == DOAlertActionStyle.cancel) {
            for ac in actions as! [DOAlertAction] {
                if (ac.style == DOAlertActionStyle.cancel) {
                    let error: NSError? = nil
                    NSException.raise(NSExceptionName(rawValue: "NSInternalInconsistencyException"), format:"DOAlertController can only have one action with a style of DOAlertActionStyleCancel", arguments:getVaList([error ?? "nil"]))
                    return
                }
            }
        }
        // Add Action
        actions.append(action)
        
        // Add Button
        let button = UIButton()
        button.layer.masksToBounds = true
        button.setTitle(action.title, for: UIControl.State())
        button.isEnabled = action.enabled
        button.layer.cornerRadius = buttonCornerRadius
        button.addTarget(self, action: #selector(DOAlertController.buttonTapped(_:)), for: .touchUpInside)
        button.tag = buttons.count + 1
        buttons.append(button)
        buttonContainer.addSubview(button)
    }
    
    // Adds a text field to an alert.
    open func addTextFieldWithConfigurationHandler(_ configurationHandler: ((UITextField?) -> Void)!) {
        
        // You can add a text field only if the preferredStyle property is set to DOAlertControllerStyle.Alert.
        if (!isAlert()) {
            let error: NSError? = nil
            NSException.raise(NSExceptionName(rawValue: "NSInternalInconsistencyException"), format: "Text fields can only be added to an alert controller of style DOAlertControllerStyleAlert", arguments:getVaList([error ?? "nil"]))
            return
        }
        if (textFields == nil) {
            textFields = []
        }
        
        let textField = UITextField()
        textField.frame.size = CGSize(width: innerContentWidth, height: textFieldHeight)
        textField.borderStyle = UITextField.BorderStyle.none
        textField.backgroundColor = textFieldBgColor
        textField.delegate = self
        
        if ((configurationHandler) != nil) {
            configurationHandler(textField)
        }
        
        textFields!.append(textField)
        textFieldContainerView.addSubview(textField)
    }
    
    open func isAlert() -> Bool { return preferredStyle == .alert }
    
    // MARK: UITextFieldDelegate Methods
    
    open func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if (textField.canResignFirstResponder) {
            textField.resignFirstResponder()
            self.dismiss(animated: true, completion: nil)
        }
        return true
    }
    
    // MARK: UIViewControllerTransitioningDelegate Methods
    open
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        layoutView(presenting)
        return DOAlertAnimation(isPresenting: true)
    }
    
    open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return DOAlertAnimation(isPresenting: false)
    }
}

コメントを残す

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

アプリ