遷移元のビューが透過で見えるモーダルビュー(UIPresentationControllerを使わないで、既存の画面遷移をいじる感じで実装してみました)
1 はじめに
iOS 8以降で利用可能な、UIPresentationControllerを使用すると、ViewControllerのモーダルのような画面を自由に表示することが可能です。
[iOS 8] UIPresentationController でカスタムのモーダル表示を実装する
今回は、あえて、UIPresentationControllerを使用せず、通常の画面遷移の実装を修正していくことで、このようなバックを透過させたモーダルビューを幾つか試してみたいと思います。
題材としては、次のようなUICollectionViewから選択した画像を、モーダルビューで拡大表示するというような、簡単なサンプルを用意しました。
2 半透明
バックを半透明にして、遷移元のビューが見えるような表現と言うことで、とりあえず、遷移先のビューのbackgroundColorのalpha値を変更します。
override func viewDidLoad() { // ・・・省略・・・ view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5) }
しかし、これだけでは、遷移した瞬間はいいのですが、その後、背後になったビューが消えてしまい、真っ黒になってしまいます。
この問題は、遷移先のビューの Presentationを over Current Contentにすることで、解決できます。
もし、これをコードで書くなら、遷移前に指定する必要があるので、遷移元のprepare(for:sender:)で設定する必要がります。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let dist = segue.destination as? PresentationViewController { dist.modalPresentationStyle = .overCurrentContext } }
動作している様子は、次のとおりです。
3 ぼかし
iOS 8以降で使用可能な、UIVisualEffectViewを使用すると、ぼかし効果を簡単に実装できます。
[iOS 8] ぼかし効果をたった 4 行で!
下記では、遷移先のビューにUIVisualEffectViewを挿入しています。
override func viewDidLoad() { // ・・・省略・・・ // 元のバックのビューは、とりあえず透明にして見えなくする view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0) // UIVisualEffectViewを生成する let visualEffectView = UIVisualEffectView(frame: view.frame) // エフェクトの種類を設定 visualEffectView.effect = UIBlurEffect(style: .regular) // UIVisualEffectViewを他のビューの下に挿入する view.insertSubview(visualEffectView, at: 0) }
UIVisualEffectViewに指定できるエフェクトの種類は、下記の3種類です。
light | dark | extralight |
---|---|---|
動作している様子は、次のとおりです。
4 遷移のアニメーション
ここまでの遷移は、デフォルトで準備されているTrasitionから選択するしかありません。
Cover Vertical | Flip Horizontal | Cross Dissolve |
---|---|---|
上記以外の遷移を表現するためには、下記の実装が必要です。
- 遷移を表現するクラスを作成
- 1.を指定するdelegateクラスを作成
- 2.をmodalPresentationStyleに設定する
1.は、UIViewControllerAnimatedTransitioningプロトコルを実装したクラスです。 また、2.は、UIViewControllerTransitioningDelegateプロトコルを実装したクラスです。
サンプルでは、1. 及び、2.を1つのクラス(OrgTransition)で実装しています。
class OrgTransition: NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning{ fileprivate var isPresent = false // MARK: - UIViewControllerTransitioningDelegate public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { // 遷移時にTrasitionを担当する(UIViewControllerAnimatedTransitioningプロトコルを実装した)クラスを返す isPresent = true return self } public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { // 復帰時にTrasitionを担当する(UIViewControllerAnimatedTransitioningプロトコルを実装した)クラスを返す isPresent = false return self } // MARK: - UIViewControllerAnimatedTransitioning func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.7 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { if isPresent { animatePresentTransition(transitionContext: transitionContext) } else { animateDissmissalTransition(transitionContext: transitionContext) } } // 遷移時のTrastion処理 func animatePresentTransition(transitionContext: UIViewControllerContextTransitioning) { UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { // 遷移のアニメーションなど }, completion: { finished in transitionContext.completeTransition(true) }) } // 復帰時のTrastion処理 func animateDissmissalTransition(transitionContext: UIViewControllerContextTransitioning) { UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { // 遷移のアニメーションなど }, completion: { finished in transitionContext.completeTransition(true) }) } }
transitioningDelegateに設定するのは、modalPresentationStyleと同様に、遷移前に設定する必要が有ります。
let orgTrasition = OrgTransition() // UIViewControllerTransitioningDelegateを実装したクラス override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let dist = segue.destination as? PresentationViewController { // ・・・省略・・・ dist.transitioningDelegate = orgTrasition } }
なお、UIVisualEffectViewの挿入などは、遷移前に行う必要があるため、こちら側(UIViewControllerAnimatedTransitioning)で実装する必要があります。詳しくは、サンプルをご参照下さい。
5 最後に
今回は、前の画面を残す遷移について、既存の画面遷移をいじる感じで、色々試してみました。 これらを把握すれば、かなり自由に画面遷移が表現できるのでは無いでしょうか。
サンプルは下記に置いています。気になるところが有りましたら、ぜひ教えてやってください。
[GitHub] https://github.com/furuya02/PresentationViewSample
6 参考リンク
[iOS 8] ぼかし効果をたった 4 行で!
[iOS 8] UIPresentationController でカスタムのモーダル表示を実装する