[iOS] Xibを使って自前のダイアログを作る


きっかけ

Androidとおなじにしてよ。っていわれたのが最初のきっかけです。
IDやパスワードを入力したり、カスタマイズがしやすいようにXibでAlertViewが簡単に作れるといいなと思いました。

今までこんな書き方をよく使ってましたが
self.presentViewController(vc, animated: true, completion: nil)

Viewを上に重ねるだけでなく、Alertっぽくしたい!!っていうのが一番の希望。

でも

でも、実際Alertなわけではなく、ViewControllerをAlertっぽくみせているだけです

プロジェクトを作る

シングルビューアプリケーションでプロジェクトを作ります

プロジェクト名を入力します

Alertに使うViewControllerを作ります

メニューからFile-New-FileでCocoa Touch Classを選択します。

ViewControllerの名前をつけます

*Also create XIB fileにチェックをつけます

出来上がったMyAlertViewController.swiftを修正します

*画面遷移のアニメーションをカスタマイズするのでUIViewControllerTransitioningDelegateを追加します

外観のカスタマイズ

MyAlertViewController.Xibにボタンをつけたり、タイトルをつけてMyAlertViewController.swiftとOutletで結んでください。

MyAlertViewController.swiftを編集

3つのinit

1.xibを初期化するinit。背景を透明にしたり、アニメーションをカスタマイズする設定をします

MyAlertViewController.swift
override init(nibName nibNameOrNil:String?, bundle ninBundleOrNil:Bundle? ){
        super.init(nibName:nibNameOrNil,bundle:ninBundleOrNil)
        self.transitioningDelegate = self
        self.providesPresentationContextTransitionStyle = true
        self.definesPresentationContext = true
        self.modalPresentationStyle = .custom

    }

2.実際に使用する初期化処理です

MyAlertViewController.swift
    convenience init(title:String,desc:String){
        self.init(nibName:"MyAlertViewController",bundle:nil)
        titleText = title
        messageText = desc
    }

3.普通のinitは使わないのでエラーにします

MyAlertViewController.swift
    required init?(coder aDecider: NSCoder){
        fatalError("init error")
    }

UIViewControllerTransitioningDelegate

デフォルトのアニメーションだと画面下から上に移動するアニメーションなので書き換えます

MyAlertViewController.swift
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return AlertAnimation(show:true)
    }
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return AlertAnimation(show:false)
    }

アニメーションの実態を書きます

class MyAlertViewController: UIViewController,UIViewControllerTransitioningDelegate {

の最後の括弧「}」が閉じてから下に付け足すか、もしくは別ファイルにしてください

MyAlertViewController.swift
class AlertAnimation : NSObject, UIViewControllerAnimatedTransitioning {

    let show: Bool

    init(show: Bool) {
        self.show = show
    }
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        if (show) {
            return 0.45
        } else {
            return 0.25
        }
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if (show) {
            self.presentAnimateTransition(transitionContext: transitionContext)
        } else {
            self.dismissAnimateTransition(transitionContext: transitionContext)
        }
    }

    func presentAnimateTransition(transitionContext: UIViewControllerContextTransitioning) {

        let alertVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as! MyAlertViewController
        alertVC.view.frame = UIScreen.main.bounds
        let containerView = transitionContext.containerView
        alertVC.alertView.alpha = 0.0
        alertVC.alertView.center = alertVC.view.center
        alertVC.alertView.transform = CGAffineTransform.init(scaleX: 0.5, y: 0.5)
        containerView.addSubview(alertVC.view)

        UIView.animate(withDuration: 0.25,
                       animations: {
                                    alertVC.view.alpha = 1.0
                                    alertVC.alertView.alpha = 1.0
                                    alertVC.alertView.transform = CGAffineTransform.init(scaleX: 1.05, y:1.05)
        },
                                   completion: { finished in
                                    UIView.animate(withDuration:0.2,
                                                    animations: {
                                                    alertVC.alertView.transform = CGAffineTransform.identity
                                    },
                                                    completion: { finished in
                                                            if (finished) {
                                                                    transitionContext.completeTransition(true)
                                                            }
                                    })
        })
    }
    func dismissAnimateTransition(transitionContext: UIViewControllerContextTransitioning) {

        let alertVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as! MyAlertViewController

        UIView.animate(withDuration:self.transitionDuration(using: transitionContext),
                                   animations: {
                                    alertVC.view.alpha = 0.0
                                    alertVC.alertView.alpha = 0.0
                                    alertVC.alertView.transform = CGAffineTransform.init(scaleX: 0.9, y: 0.9)
        },
                                   completion: { finished in
                                    transitionContext.completeTransition(true)
        })
    }
}

ソース

GitHubにおきました