この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
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 でカスタムのモーダル表示を実装する