iOS
Swift

背景透過なViewControllerをクロスディゾルブでモーダル表示

More than 1 year has passed since last update.

iOS 7とiOS 8でやり方が違うので実は結構面倒だったりする。

  • iOS 7は遷移元のViewControllerに対してごにょごにょ
  • iOS 8は遷移先のViewControllerに対してごにょごにょ

iOS 7の場合

モーダルを出す時

modalPresentationStyle = .CurrentContext
modalTransitionStyle = .CrossDissolve
navigationController?.modalPresentationStyle = .CurrentContext
navigationController?.modalTransitionStyle = .CrossDissolve

presentViewController(controller, animated: animated, completion: {
  [weak self] () -> Void in

  // 遷移が終わったら元の状態に戻す。モーダルから戻ってきた後に別のモーダルを出そうとしてクロスディゾルブのままだったりする
  self?.modalPresentationStyle = .FullScreen
  self?.modalTransitionStyle = .CoverVertical
  self?.navigationController?.modalPresentationStyle = .FullScreen
  self?.navigationController?.modalTransitionStyle = .CoverVertical
})

モーダルを閉じる時

modalPresentationStyle = .CurrentContext
modalTransitionStyle = .CrossDissolve
navigationController?.modalPresentationStyle = .CurrentContext
navigationController?.modalTransitionStyle = .CrossDissolve

dismissViewControllerAnimated(true, completion: nil)

iOS 8の場合

モーダルを出す時

controller.modalPresentationStyle = .OverCurrentContext
controller.modalTransitionStyle = .CrossDissolve
presentViewController(controller, animated: animated, completion: nil)

モーダルを閉じる時

presentingViewController?.modalPresentationStyle = .OverCurrentContext
presentingViewController?.modalTransitionStyle = .CrossDissolve

// completionの中で、self?.presentingViewController とやってもnilなので
let p = presentingViewController

dismissViewControllerAnimated(true, completion: {
  [weak self] () -> Void in

  p?.modalPresentationStyle = .FullScreen
  p?.modalTransitionStyle = .CoverVertical
})

まとめると

モーダルを出す時

if iOS8以上 {
  controller.modalPresentationStyle = .OverCurrentContext
  controller.modalTransitionStyle = .CrossDissolve
} else {
  modalPresentationStyle = .CurrentContext
  modalTransitionStyle = .CrossDissolve
  navigationController?.modalPresentationStyle = .CurrentContext
  navigationController?.modalTransitionStyle = .CrossDissolve
}

presentViewController(controller, animated: animated, completion: {
  [weak self] () -> Void in

  if iOS8以上 { return }

  self?.modalPresentationStyle = .FullScreen
  self?.modalTransitionStyle = .CoverVertical
  self?.navigationController?.modalPresentationStyle = .FullScreen
  self?.navigationController?.modalTransitionStyle = .CoverVertical
})

モーダルを閉じる時

if iOS8以上 {
  presentingViewController?.modalPresentationStyle = .OverCurrentContext
  presentingViewController?.modalTransitionStyle = .CrossDissolve
} else {
  modalPresentationStyle = .CurrentContext
  modalTransitionStyle = .CrossDissolve
  navigationController?.modalPresentationStyle = .CurrentContext
  navigationController?.modalTransitionStyle = .CrossDissolve
}

let p = presentingViewController

dismissViewControllerAnimated(true, completion: {
  [weak self] () -> Void in

  if iOS8以上 {
    p?.modalPresentationStyle = .FullScreen
    p?.modalTransitionStyle = .CoverVertical
  }
})

やってられるか!!

というわけで、以下のようなextensionを用意する。

UIViewController+Extensions.swift
extension UIViewController {
  enum Transition {
    case Default, CrossDissolve
  }

  func tak_presentViewController(controller: UIViewController, animated: Bool, transition: Transition) {
    tak_presentViewController(controller, animated: animated, transition: transition, completion: nil)
  }

  func tak_presentViewController(controller: UIViewController, animated: Bool, transition: Transition, completion: (Void -> Void)?) {
    if iOS8以上 {
      controller.tak_setupTransition(transition)
    } else {
      tak_setupTransitionForIos7(transition)
    }

    presentViewController(controller, animated: animated, completion: {
      [weak self] () -> Void in

      if iOS8以上 { return }

      self?.tak_setupTransitionForIos7(.Default)
      completion?()
    })
  }

  func tak_dismissViewControllerAnimated(animated: Bool, transition: Transition) {
    tak_dismissViewControllerAnimated(animated, transition: transition, completion: nil)
  }

  func tak_dismissViewControllerAnimated(animated: Bool, transition: Transition, completion: (Void -> Void)?) {
    if iOS8以上 {
      presentingViewController?.tak_setupTransition(transition)
    } else {
      tak_setupTransitionForIos7(transition)
    }

    // completionの中で、self?.presentingViewController とやってもnilなので
    let p = presentingViewController

    dismissViewControllerAnimated(true, completion: {
      [weak self] () -> Void in

      if iOS8以上 {
        p?.tak_setupTransition(.Default)
      }

      completion?()
    })
  }

  // iOS7用
  private func tak_setupTransitionForIos7(transition: Transition) {
    switch transition {
    case .Default:
      modalPresentationStyle = .FullScreen
      modalTransitionStyle = .CoverVertical
    case .CrossDissolve:
      modalPresentationStyle = .CurrentContext
      modalTransitionStyle = .CrossDissolve
    }
    navigationController?.tak_setupTransitionForIos7(transition)
  }

  private func tak_setupTransition(transition: Transition) {
    switch transition {
    case .Default:
      modalPresentationStyle = .FullScreen
      modalTransitionStyle = .CoverVertical
    case .CrossDissolve:
      modalPresentationStyle = .OverCurrentContext
      modalTransitionStyle = .CrossDissolve
    }
  }
}

モーダルを出す時

tak_presentViewController(c, animated: true, transition: .CrossDissolve)

モーダルを閉じる時

tak_dismissViewControllerAnimated(animated: true, transition: .CrossDissolve)