56215


`TableViewController` に `PopupViewController` のポップアップを表示したいのですがうまく行かないです。
何故なのでしょうか。どうしたら良いですか。

[![画像の説明をここに入力](https://i.stack.imgur.com/3OHNk.png)](https://i.stack.imgur.com/3OHNk.png)  
** この右側のポップアップを表示したい**

[![画像の説明をここに入力](https://i.stack.imgur.com/KhSlL.png)](https://i.stack.imgur.com/KhSlL.png)

↓↓こうなります。  
[![画像の説明をここに入力](https://i.stack.imgur.com/Zx3Dn.png)](https://i.stack.imgur.com/Zx3Dn.png)

**TableViewのクラス**

[code]

    import UIKit
    
    class TableViewController: UITableViewController {
    
        var array : Array = [1, 2, 3, 4, 5]
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
    
        }
    
    
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return array.count
        }
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "customTableViewCell", for: indexPath) as! costomViewCell
    
            cell.skillName.text = "〇〇〇〇〇〇\(array[indexPath.row])"
            cell.goalCountLabel.text = "20 : 00"
    
            return cell
        }
    
        override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    
        }
    
        override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
            if editingStyle == .delete {
                array.remove(at: indexPath.row)
                tableView.deleteRows(at: [indexPath], with: .fade)
            }
        }
    
    
        let popupViewController = PopupViewController()
        @IBAction func addSkillButton(_ sender: UIBarButtonItem) {
            view.addSubview(popupViewController.view)
        }
    
    
        @IBAction func secret(_ sender: UIBarButtonItem) {
        }
    
    
    }
    
    
    
[/code]

**Popupクラス**

[code]

    import UIKit
    
    class PopupViewController: UIViewController {
    
        @IBOutlet weak var newSkillView: UIView!
        @IBOutlet weak var newSkillLabel: UILabel!
        @IBOutlet weak var newSkillText: UITextField!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            self.view.backgroundColor = UIColor(
                red: 150/255,
                green: 150/255,
                blue: 150/255,
                alpha: 0.6
            )
         // newSkillView.layer.cornerRadius = 25.0 この処理を行うとThread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value というエラーが出る。。。何故？？？
    
        }
    
        @IBAction func newSkillButton(_ sender: UIButton) {
        }
    
        @IBAction func tapGestureRecogButton(_ sender: UITapGestureRecognizer) {
            self.view.removeFromSuperview()
    
        }
    
    }
    
    
[/code]


CC BY-SA 4.0
2019-06-28T14:27:42.517
56198
2019-06-29T06:56:47.887
2019-06-29T01:32:53.777
34541
34541
question
swift xcode
（UITableView上に）ポップアップを表示したい！


**こうなります** の画像を見ると、table viewの部分がグレーになっていますよね? これは`PopupViewController`が
**表示はされているけど、中身がない** 状態になっているからです。

この事象でいちばんの元凶はこの行です。

[code]

        let popupViewController = PopupViewController()
    
[/code]

view controllerの引数なしのイニシャライザを呼んでいますが、これではiOSは、storyboardの中身を読みに行ってくれません。

従って、

  * IBOutletは接続してくれない

`newSkillView`はnilのままなんで、実行時にUnexpectedly found nilになる

  * IBActionは接続してくれない

  * そもそもstoryboardエディタ上で作成したデザインは一切反映してくれない

`UIView`も`UILabel`も`UITextField`も、一切画面に現れないことになります。

* * *

今回の事象の直接の原因にはなっていませんが、あなたのコードにはもう一つ致命的にまずい点があります。

[code]

            view.addSubview(popupViewController.view)
    
[/code]

view controllerの`view`だけを別のview
controllerの支配下にある`view`に`addSubview(_:)`で追加しています。

iOSでは、

  * 全ての`UIView`は一つのview controllerの支配下にある

  * `UIViewController`用のメソッドには「現在表示を担当している」か「現在アクティブなview controllerチェーンの一部である」かで無いと動かないものがある

と言うことで、現在のあなたのコードでは`PopupViewController`は表示されています(先ほど書いたように中身は無いですが)が、今後`PopupViewController`にいろんなコードを足していって、上記のようなチェックに引っかかるメソッドを使うと、実行時エラーになってしまいます。

* * *

と言うわけで、iOSで「ポップアップを表示」する場合、一般的(storyboardを使う場合)には、

  * 普通に表示されるポップアップをstoryboardでデザインする

背景色の一部はポップアップっぽく半透明にしますが、これはstoryboard上で設定できます

  * storyboard上でポップアップのView Controller属性 **Presentation** を **Over Current Context** にしてやる

[![Over Current
Context](https://i.stack.imgur.com/jreGl.png)](https://i.stack.imgur.com/jreGl.png)

  * 必ずstoryboardが使われるようにインスタンス化する

  * 後は普通に`present`などで表示してやる

と言う手順になります。

コードの方の修正は、例えばこんな感じです。

**TableViewのクラス**

[code]

        //インスタンスプロパティとしての`popupViewController`は不要
        @IBAction func addSkillButton(_ sender: UIBarButtonItem) {
            //元(`TableViewController`)と同じstoryboardは`self.storyboard`で取得できる
            let storyboard = self.storyboard!
            //storyboardからインスタンス化すると、iOSがいろいろと面倒を見てくれる
            let popupViewController = storyboard.instantiateViewController(withIdentifier: "PopupViewController") as! PopupViewController
    
            //普通のview controllerとして、presentで表示する
            self.present(popupViewController, animated: true, completion: nil)
        }
    
[/code]

セグエを使ってもstoryboardは読み込まれるんですが、説明が書きにくいので今回は`instantiateViewController(withIdentifier:)`を使っています。(storyboard上でポップアップの
**Storyboard ID** をコードに合わせて`PopupViewController`にしてやる必要があります。)

**Popupクラス**

[code]

        override func viewDidLoad() {
            super.viewDidLoad()
    
            //...
            //Storyboardを通じてインスタンス化するとIBOutletを接続してくれるので実行時エラーにならない
            newSkillView.layer.cornerRadius = 25.0
    
        }
    
        //...
    
        @IBAction func tapGestureRecogButton(_ sender: UITapGestureRecognizer) {
            //普通のview controllerとして、presentで表示するので、dismissで元に戻れる
            self.dismiss(animated: true, completion: nil)
        }
    
[/code]

* * *

と、ここまででポップアップ表示はできるはずなんですが、ポップアップでOKを押すとtable
viewの方に結果が反映されないといけませんよね。[以前の別質問に対する回答](https://ja.stackoverflow.com/a/55758/13972)の応用(と言うよりコアな部分は殆どそのまんま)なんで、説明軽めにコードだけ示しておきます。

**Popupクラス**  
デリゲート用のプロトコル、デリゲートプロパティ、デリゲートの呼び出しを追加。

[code]

    //delegate用のプロトコルを宣言する
    protocol PopupViewControllerDelegate: class {
        //`label`は、今は`Int`にしておく
        func popupViewController(_ popupVC: PopupViewController, didEnterLabel label: Int)
    }
    class PopupViewController: UIViewController {
        //`delegate`変数をweakで宣言する
        weak var delegate: PopupViewControllerDelegate?
    
        //...
    
        @IBAction func newSkillButton(_ sender: UIButton) {
            //`label`は、今は`Int`にしてある
            let label = Int(newSkillText.text ?? "") ?? 0
            //その`label`の値を使ってdelegateメソッドを呼び出す
            delegate?.popupViewController(self, didEnterLabel: label)
        }
    
        //...
    }
    
[/code]

**TableViewのクラス**  
`PopupViewController`のデリゲートは自分であることを宣言してやる

[code]

    class TableViewController: UITableViewController {
    
        //...
    
        //↓`###`のある行を追加
        //インスタンスプロパティとしての`popupViewController`は不要
        @IBAction func addSkillButton(_ sender: UIBarButtonItem) {
            //元(`TableViewController`)と同じstoryboardは`self.storyboard`で取得できる
            let storyboard = self.storyboard!
            //storyboardからインスタンス化すると、iOSがいろいろと面倒を見てくれる
            let popupViewController = storyboard.instantiateViewController(withIdentifier: "PopupViewController") as! PopupViewController
            popupViewController.delegate = self //### `PopupViewController`のdelegateは自分(`self`)が引き受ける
            //普通のview controllerとして、presentで表示する
            self.present(popupViewController, animated: true, completion: nil)
        }
    
        //...
    }
    
    //`PopupViewControllerDelegate`としてのお仕事を定義する
    extension TableViewController: PopupViewControllerDelegate {
    
        func popupViewController(_ popupVC: PopupViewController, didEnterLabel label: Int) {
            let indexPath = IndexPath(row: array.count, section: 0)
            array.append(label)
            //↑↓辻褄を合わせる
            tableView.insertRows(at: [indexPath], with: .automatic)
            popupVC.dismiss(animated: true, completion: nil)
        }
    
    }
    
[/code]


CC BY-SA 4.0
2019-06-29T06:48:27.207
56215
2019-06-29T06:56:47.887
2019-06-29T06:56:47.887
13972
13972
56198
answer