こんばんわ、daihaseです。
ツール系のアプリ開発をしていて、必ずといって言いほど使われるポップアップビュー。何かボタンを押下した時に画面に「にゅ」っと出てきたりする小窓みたいなやつのことですね。
それの実装方法を紹介。いくつかあるのですがここではStoryboardとUIViewControllerを組み合わせた方法を。
StoryboardとUIViewControllerで作った画面を呼び出すためのUIWindowをExtensionしたクラスを作成します。
import UIKit fileprivate var windowStackArray: [UIWindow] = [] fileprivate var animationDuration: TimeInterval = 0.4 extension UIWindow { public static var windowStack: [UIWindow] { return windowStackArray } open static func createNewWindow(_ rootViewController: UIViewController) -> UIWindow { let window = UIWindow(frame: UIScreen.main.bounds) window.alpha = 0.0; window.windowLevel = UIWindowLevelNormal; window.rootViewController = rootViewController return window } open func open(_ animation: ((_ window: UIWindow) -> Void)? = nil) { guard let root = rootViewController else { return } self.alpha = 0.0 root.view.frame.origin = CGPoint(x: 0.0, y: 0.0) addSubview(root.view) guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } if windowStackArray.count == 0 { windowStackArray.append(appDelegate.window!) } let nowWindow = appDelegate.window nowWindow?.rootViewController?.viewWillDisappear(true) windowStackArray.append(self) appDelegate.window = self self.makeKeyAndVisible() if let animation = animation { animation(self) } else { UIView.transition(with: self, duration: animationDuration, options: [.transitionCrossDissolve, .curveLinear], animations: { () -> Void in self.alpha = 1.0 self.transform = CGAffineTransform.identity }, completion: { (finished) in nowWindow?.rootViewController?.viewDidDisappear(true) }) } } open func close(_ animation: ((_ window: UIWindow) -> Void)? = nil, completion: ((Bool) -> Swift.Void)? = nil) { guard let idx = windowStackArray.index(of: self) else { return } if windowStackArray.count <= 1 { return } let beforeWindow = windowStackArray[idx - 1] guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } windowStackArray.remove(at: idx) if beforeWindow == windowStackArray.last { appDelegate.window = beforeWindow beforeWindow.rootViewController?.viewWillAppear(true) } if let animation = animation { animation(beforeWindow) } else { UIView.transition(with: self, duration: animationDuration, options: [.transitionCrossDissolve, .curveLinear], animations: { () -> Void in self.alpha = 0.0 self.transform = CGAffineTransform.identity }, completion: { (finished) in self.rootViewController?.view.removeFromSuperview() self.removeFromSuperview() if beforeWindow == windowStackArray.last { beforeWindow.makeKeyAndVisible() beforeWindow.rootViewController?.viewDidAppear(true) } completion?(finished) }) } } }
最後に、作ったポップアップを実際呼び出すための処理をViewContoller側に書きます。
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } @IBAction func tapPopupButton(_ sender: Any) { let storyboard = UIStoryboard(name: "PopupViewController", bundle: nil) let popupViewController = storyboard.instantiateViewController(withIdentifier: "PopupViewController") as! PopupViewController popupViewController.delegate = self // ポップアップ表示 UIWindow.createNewWindow(popupViewController).open() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } } extension ViewController: PopupViewControllerDelegate { // ポップアップ閉じる func closeDialog() { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let window = appDelegate.window else { return } window.close() } }
ポップアップビューの閉じる処理はこの呼び出し側のViewControllerで行うのでdelegateにselfをセットするのを忘れずに。
次にポップアップビュー側の処理を。
import UIKit protocol PopupViewControllerDelegate { func closeDialog() } class PopupViewController: UIViewController { var delegate: PopupViewControllerDelegate? override func viewDidLoad() { super.viewDidLoad() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } @IBAction func tapCloseButton(_ sender: Any) { self.delegate?.closeDialog() } }
こちらはシンプルですね。protocolでこのポップアップビューを閉じるメソッドを定義し、実際それを実行するのはデリゲート先ということになります。つまり上のViewContollerのcloseDialog()ですね。
ちなみにこちらのUIWindow+Extended.swiftを少し弄れば、ポップアップビューが表示される際のアニメーションも自由自在です。
ソースはGitHub上にも置いておきましたのでご自由にいじっちゃってください。
なお、自作のOSSではxibを使ったカスタムUIViewにてポップアップを再現してますので、良かったらこちらを使ってもらえると嬉しかったりします。
それでは良い開発ライフを〜