swiftでtableViewを表示する…ときのエラー・トラブル対応方法

はじめに

どうも、どうまずです。

Swiftを初めて、最初のほうにやること、『SwiftでTableViewを表示する。』
いくつかのサイトや参考書があり、私もそれらを見ながら作成しました。

しかし、エラーやトラブルで躓いていませんか?
躓きすぎて、匙投げようとしていませんか?

今回は、『swiftでtableViewを表示する』を試みて、よく発生するエラー・トラブルの解決方法を記載しようと思います。

ちなみに、環境は

Swift4
Xcode 9

です。

MainStroryBoardの使い方のトラブル編

ユーティリティエリアが表示されない。(右の作業枠)

XcodeでViewにTableViewを貼り付けようと ユーティリティエリアで説明するように記載されているが、そもそも、ユーティリティエリアってのが表示されていない!

原因

単なる操作ミスで、ユーティリティエリアを隠すボタンをクリックしてしまったようです。

解決策

下記のボタンをクリックして下さい。ユーティリティエリアが表示されます。

f:id:project-unknown:20170925014924p:plain:w150

Table Viewが二つになった!

MainStoryboardでtable viewをViewControllerを貼り付けたら、View Controllerが二つになってしまった。

f:id:project-unknown:20171027211940p:plain:w300

説明

「Table View」じゃなくて、「table View Controller」を選択していた。

正:Table View(枠が四角)

f:id:project-unknown:20171005194137p:plain:w150

誤:Table View Controller(枠が丸、黄色)

f:id:project-unknown:20171005194115p:plain:w150

解決策

新しくできたViewを削除(選択してDeleteキー)する。 うまく選択できない場合は、左側に「Table View Controller Scene」と表示されているので、選択して削除する。 「Table View Controller」をMainStoryboardに貼り付けただけなら、削除しても問題ありません。

MainStoryBoardとソースの紐付けトラブル編

ソースにTableViewを紐づけたら、テーブル変数名を間違えた

MainStoryboardのTableViewとViewController.swiftなど紐付けしたけど、タイプミスなどでテーブルの変数名を間違えてしまった。

説明

ViewController.swiftに作成された変数名を修正するだけでは、MainStoryboardに設定の名残が残ってしまい、実行してもエラーが発生してしまいます。

MainStoryboardのTableViewとViewController.swiftの紐付けを断つことが大切です。

解決策

  1. ViewController.swiftのテーブル変数を削除する。
  2. MainStoryboardのtableViewを2本指でクリックする。
  3. 下の画面キャプチャの✖︎ボタンをクリックする。

f:id:project-unknown:20170925020210p:plain:w200

これで、「Table View」とViewController.swiftの紐付けは断たれました。

確認のためシミュレータで実行してみましょう。

エラーが発生しなければ、成功です。

@IBOutlet var mainStoryboard: [UITableView]と表示された。

MainStroryboardのTableViewとViewController.swiftの紐付けを行なった際、

本来、

@IBOutlet weak var mainTableView: UITableView!

と表示されるはずだが、なぜか

@IBOutlet var mainTableView: [UITableView]!

と表示されてしまった。

説明

MainStroryboardのTableViewとViewController.swiftの紐付けを行なったとき、ダイアログが表示されますが、 Connectionのところをデフォルトの「Outlet」ではなく、「Outlet Collection」を選択してしまったようです。

f:id:project-unknown:20171005210326p:plain

「Outlet Collection」はコレクションつまり配列扱いになります。テーブルを複数使う場合などに活用できますので、完全なミスというわけではありません。

しかし、意図していない場合は・・・単なる操作ミスです。

解決策

やり直しましょう。

「ソースにTableViewを紐づけたら、テーブル変数名を間違えた」を参考にテーブル変数の削除を実施して下さい。

ViewController.swiftの記載編

UITableViewDelegateとUITableViewDataSourceを継承したらエラーになった。

ViewController.swiftでTableViewを操作できるようにUITableViewDataSourceを継承させた。

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

そしたら、

ViewController.swift:11:7: Type 'ViewController' does not conform to protocol 'UITableViewDataSource'

というエラーが発生した。

説明

たぶんですが、デリゲートとデータソースを継承しただけで、作業を止めていませんか?

エラーの内容は
「'ViewController'がプロトコル 'UITableViewDataSource'に準拠していません」
です。

UITableViewDataSourceを継承すると、UITableViewDataSourceのルールに則ったメソッドを用意しなければなりません。

解決策

まず、どこかのサイト、参考書を参照しているのでしたら、このエラーを無視して続きの説明を読みましょう。 大抵の場合は、

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)

を記載しているはずです。

特に記載していない場合、エラーのところにFixと出ているはずです。

f:id:project-unknown:20171005213159p:plain

これをクリックすると、最低限必要なメソッドを自動的に追加してくれます。

補足ですが、
・エラーの左の赤丸の中が白丸の場合は、今回のようにエラー解決案を提示してくれます。
・エラーの左の赤丸の中が!の場合は、エラー解決案を提示してくれませんので、エラー内容から何とか解決しましょう。

TableViewにソースで記載した内容が反映されない。

ある程度なれてきたときに発生するトラブルです。
TableViewの設定や処理をViewController.swiftに記載したのに、シミュレータで実行してみると反映されないというトラブルです。

説明

なにか大切で肝心な何かを忘れています。

解決策

viewDidLoadにデリゲートとデータソースの指定を忘れていませんか?

@IBOutlet weak var mainTableView: UITableView!
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    mainTableView.delegate   = self
    mainTableView.dataSource = self
    
}

「mainTableViewのデリゲートとデータソースはこのソース自身に記載しているよ」という指定ですね。 これがないと、mainTableViewはどのデリゲート・データソースを使っていいかわからず、沈黙してしまいます。

全てのセルが同じ設定になってしまう。

例えば、

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    
    return CGFloat(100.0)
    
}

と記述すると、セルの高さが変更されますが、全部のセルが同じ高さになってしまう。

先頭のセルだけ高さを別の高さを指定したい。

説明

heightForRowAtの引数にindexPathがあります。

indexPathには、どのセクションの何行目のセルかという情報が入りますので、これを活用しましょう。

解決策

下記のように条件分岐を記載します。

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if indexPath.section == 0 && indexPath.row == 0{
      return CGFloat(50.0)
    }
    return CGFloat(100.0)    
}

indexPath.sectionでheightForRowAtを呼んだセルのセクション番号を取得します。
indexPath.rowでheightForRowAtを呼んだセルの行番号を取得します。

これらを活用すれば、任意のセルを制御できると思いますよ。

シミュレータで実行しようとしたら編

シミュレータで実行しようとしてエラーになった。

プロジェクト作成直後、とりあえずシミュレータ起動しようとしたら、エラーになっちゃったパターンです。

エラーの内容は下記のような内容。

Signing for "blogTest" requires a development team. Select a development team in the project editor.
Code signing is required for product type 'Application' in SDK 'iOS 10.3'

原因

原因はずばり、
何も設定していない実機(○◯のiPhoneなど)を選択している。 です。

Xcodeで作成しているアプリケーションを実機で実行・デバックすることは可能ですが、いろいろと設定が必要です。

その設定をやっていないため、エラーが発生しています。

解決

実機ではなく、シミュレータを選択して、再度実行して下さい。

f:id:project-unknown:20171008134525p:plain:w200

ここではiPhone7を選択していますが、動かしたい任意のシミュレータを選択して大丈夫です。

AppDelegateクラスに「Thread 1: signal SIGABRT」が発生した。

シミュレータで実行したところ、下のようにAppDelegateクラスに「Thread 1: signal SIGABRT」が発生した。

f:id:project-unknown:20171005201014p:plain

ログ(デバックエリアの左側の黒いウインドウ)に下記のエラーが出しまった。

BlogTest *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:
 '[<BlogTest.ViewController 0x7fb824907140> setValue:forUndefinedKey:]:
 this class is not key value coding-compliant for the key mainTableView.'

ちなみに、私は、「Thread 1: signal SIGABR」を「初心者殺し」と呼んでます(笑)
エラーが今まで触ってないところに表示され、何がなんだかキョトンとしてしまいますよね?
私は固まりましたよ!

説明

『そもそも、「signal SIGABR」って何よ?』という方は、 signal SIGABRに関する当ブログの記事をご覧下さい。

www.project-unknown.jp

この記事の「その1インターフェースビルダーとコードをOutlet引いたのに、そのOutletが外れてしまっているパターン」にハマっています。

テーブルの変数名を間違えたりして、ViewController.swiftのテーブル変数を一度削除したりしてませんか?
テーブルの変数名をViewController.swift上で変更したりしていませんか?

そういう場合に発生します。

解決策

  1. MainStoryboardのtableViewを2本指でクリックする。
  2. 下の画面キャプチャの✖︎ボタンをクリックする。

f:id:project-unknown:20170925020210p:plain:w200

ちゃんと消してから、再度MainStoryboardのTableViewとViewController.swiftを紐付けしましょう。

だいたいこれで、TableViewの器は作れたと思います。

次回は続きのセルの操作時のエラー・トラブルを記載しますので、少々お待ち下さい。