FloatingPanelを使うとStroybardで作ったtableViewがnilになる時


結論、フロートさせるControllerをストーリーボードから生成しよう。

ちょっと練習でTikTokのコメント欄っぽいものを作ろうとしてFloatingPanelを使ったのですが、その際にtableViewがnilになってハマりました。

ちゃんとIBOutletはキチンと紐付けられておりましたが、どうやらViewControllerでcontentViewを生成したときにClassから生成してるもんだから、Storyboardに配置したtableviewが読み込まれてなくてnilになってたようだ。

そこで生成時にfromStoryboard()関数を使ってストーリーボードからViewControllerを生成してやるようにすることで無事解決できた。

ViewController.swift
import UIKit
import FloatingPanel

class ViewController: UIViewController, FloatingPanelControllerDelegate {
    var fpc: FloatingPanelController!

    override func viewDidLoad() {
        super.viewDidLoad()

        fpc = FloatingPanelController()
        fpc.delegate = self // Optional // レイアウトなどを変更しないときは不要

        //let contentVC = CommenttViewController()
        let contentVC = CommentViewController.fromStoryboard()
        fpc.set(contentViewController: contentVC)

        // Track a scroll view(or the siblings) in the content view controller.
        fpc.track(scrollView: contentVC.tableView)

        // Add and show the views managed by the `FloatingPanelController` object to self.view.
        fpc.addPanel(toParent: self)
    }
}
CommentViewController.swift
import UIKit

class CommentViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var commentCountLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self // ここでtableViewがnilになって落ちてた
        tableView.dataSource = self
    }

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

    static func fromStoryboard(_ storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)) -> CommentViewController {
        let controller = storyboard.instantiateViewController(withIdentifier: "CommentViewController") as! CommentViewController
        return controller
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! CommentCell
        return cell
    }
}

ストーリーボードからViewControllerを生成するにはStoryboard IDを設定してやる必要がありますのでこの点だけ注意。

おまけ

なお、いつも忘れるのでおさらいですがtableViewの中に直接ProtoTypeCellを入れて作った場合は、以下のようなregisterは必要ないみたいです。ただnibでの登録とClassでの登録の違いを忘れた‥ので優しい人コメントで教えてくれるとうれしい‥。

tableView.register(UINib(nibName: "CommentCell", bundle: nil), forCellReuseIdentifier: "CommentCell") // storyboardではなく別途xibファイルを作ってCellをデザインしたとき?
tableView.register(CommentCell.self, forCellReuseIdentifier: "CommentCell") //

またストーリーボードの Connections Inspector の Outlets で、dataSourceとdelegateを紐付けていれば以下も不要ですが、ぼくはここはコードでやってます。

tableView.dataSource = self
tableView.delegate = self

参考