miyazaki-dev

【Swift】自作でポップアップを作成してみた。

Swift

ポップアップだったり、アラートを表示したい場合はswiftが用意してくれてるやつとかライブラリとかあると思うんですけど

実際に自分で作って見たので使って見たい人いたらコピペで使ってみてね。

2パターン用意しました。個人的にはパターン2がいいかなと思います。

swiftで自作ポップアップ

パターン1:swfit4でポップアップウィンドウを作成

まずは1つ目の実装

viewControllerとポップアップViewControllerで構成されています。

まずはviewcontrollerでボタンとUIViewを作成。

これがViewController.swift

class ViewController: UIViewController {

  var popupViewController = PopupViewController()
  var popupBtn = UIButton()
    
  override func viewDidLoad() {
    super.viewDidLoad()
    
    addChildViewController(popupViewController)    
    popupViewController.didMove(toParentViewController: self)
        
    // 押されたときにポップアップを表示するボタンを作成。
    self.popupBtn.backgroundColor = UIColor.red
    self.popupBtn.frame = CGRect(x: 0, y: 100, width: 100, height: 50)
    self.popupBtn.setTitle("popup", for: .normal)
    self.popupBtn.addTarget(self, action: #selector(self.popup(_:)), for: .touchUpInside)
    self.view.addSubview(self.popupBtn)
  }

  // ボタンが押されたときにaddSubviewする。
  @objc func popup(_ sender: Any) {
    view.addSubview(popupViewController.view)
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
}

popupViewController.swiftを作成

class PopupViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
        
    let screenWidth:CGFloat = self.view.frame.width
    let screenHeight:CGFloat = self.view.frame.height
        
    let popupWidth = (screenWidth * 3)/4
    let popupHeight = (screenWidth * 4)/5

    // uiviewの作成 ポップアップ
    let testUIView = UIView()
    testUIView.frame = CGRect(
      x:screenWidth/8,
      y:screenHeight/5,
      width:popupWidth,
      height:popupHeight
    )
    
    testUIView.backgroundColor = UIColor.white
    testUIView.layer.cornerRadius = 10

    self.view.addSubview(testUIView)
        
    // 画面のどこかがタップされたらポップアップを消す処理
    let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(
      target: self,
      action: #selector(self.tapped(_:))
    )
        
    // デリゲートをセット
    tapGesture.delegate = self as? UIGestureRecognizerDelegate
        
    self.view.addGestureRecognizer(tapGesture)
        
    // ポップアップ以外のところを半透明のグレーに。
    self.view.backgroundColor = UIColor(
      red: 150/255,
      green: 150/255,
      blue: 150/255,
      alpha: 0.6
    )
  }

  // どこかタップされたときポップアップを消し去る関数
  @objc func tapped(_ sender: UITapGestureRecognizer){
    self.view.removeFromSuperview()
  }
    
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }
}

この中のUIviewとかにlabelやtextFieldを追加してけば、いろいろ作れるはずです!

パターン2:swift4でポップアップを作成

swiftで自作ポップアップ2

こちらは既存のモーダルをカスタムするパターンです。 まずはViewControllerから。

こちらは単純にボタンを用意。 そしたらModalViewControllerのインスタンスを生成させてpresentで表示させるというもの。

そこでそのpresentの処理をcustomとしているので、そのcustomeにしているUIPresentationControllerを呼び出す。

class ViewController: UIViewController {

  let sampleBtn = UIButton()

    
  override func viewDidLoad() {
    super.viewDidLoad()
    self.sampleBtn.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
    self.sampleBtn.backgroundColor = UIColor.red
    self.sampleBtn.setTitle("button", for: .normal)
    self.sampleBtn.addTarget(
      self,
      action: #selector(self.btnTapped(sender:)),
      for: .touchUpInside
    )

    self.view.addSubview(self.sampleBtn)
  }
    
  // タップされたときのアクション
  @objc func btnTapped(sender: UIButton){
    let modalViewController = ModalViewController()
    modalViewController.modalPresentationStyle = .custom
    modalViewController.transitioningDelegate = self
    present(modalViewController, animated: true, completion: nil)
  }
    
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
}


// modalViewController.modalPresentationStyle = .custom
// でカスタムとしたので、こちらで設定するように呼び出す。
extension ViewController: UIViewControllerTransitioningDelegate {
  func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
    return CustomPresentationController(presentedViewController: presented, presenting: presenting)
  }
}

UIPresentationControllerの設定をする。

// モーダル用のカスタム  UIPresentationController
class CustomPresentationController: UIPresentationController {
  // 表示されるモーダル
  var modalView = UIView()
  let margin = (x: CGFloat(30), y: CGFloat(220.0))
    
  // 表示トランジション開始前に呼ばれる
  override func presentationTransitionWillBegin() {
    guard let containerView = containerView else {
      return
    }
    
    self.modalView.frame = containerView.bounds

    let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(
      target: self,
      action: #selector(self.modalViewTouched(_:))
    )

    self.modalView.addGestureRecognizer(tapGesture)
    self.modalView.backgroundColor = .black
    self.modalView.alpha = 0.0
        
    containerView.addSubview(self.modalView)
        
    // トランジションを実行
    presentedViewController.transitionCoordinator?.animate(
        alongsideTransition: { (context) in
            self.modalView.alpha = 0.7
        },
        completion: nil
    )
  }
    
  // 非表示トランジション開始前
  override func dismissalTransitionWillBegin() {
    presentedViewController.transitionCoordinator?.animate(
      alongsideTransition: { (context) in
        self.modalView.alpha = 0.0
      },
      completion: nil
    )
  }
    
  // 非表示トランジション開始後
  override func dismissalTransitionDidEnd(_ completed: Bool) {
    if completed {
      self.modalView.removeFromSuperview()
    }
  }
    
  // モーダルのサイズを返す。
  override func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize) -> CGSize {
    // parentSizeに呼び出し元のviewControllerのサイズが入ってる。
    return CGSize(width: parentSize.width - margin.x, height: parentSize.height - margin.y)
  }

  // モーダルの場所設定
  override var frameOfPresentedViewInContainerView: CGRect {
    var presentedViewFrame = CGRect()
    let containerBounds = containerView!.bounds
    let childContentSize = size(
      forChildContentContainer: presentedViewController,
      withParentContainerSize: containerBounds.size
    )
    presentedViewFrame.size = childContentSize
    presentedViewFrame.origin.x = margin.x / 2.0
    presentedViewFrame.origin.y = margin.y / 2.0

    return presentedViewFrame
  }

  // レイアウト開始前に呼ばれる
  override func containerViewWillLayoutSubviews() {
    self.modalView.frame = containerView!.bounds
    presentedView?.frame = frameOfPresentedViewInContainerView
    presentedView?.layer.cornerRadius = 10
    presentedView?.clipsToBounds = true
  }
    
  // モーダルを消す処理。
  @objc func modalViewTouched(_ sender: UITapGestureRecognizer) {
    presentedViewController.dismiss(animated: true, completion: nil)
  }
}

以上!

じゃーの