UITableViewをExtensionを使って実装する方法

こんにちは、プログラマーの@Yuuです。
iPhoneアプリ開発で切っても切り離せない「UITableView」。
今までにUITableViewの使い方の記事やカスタムセルの実装方法の記事を書きました。
UITableViewは非常に便利なのですが、いかんせん1つのViewController内に実装しようとするとコードが長くなり可読性が悪くなります。
そういう時に使えるのが「Extension」!
Extensionを使うとUITableViewを実装する際に、可読性を上げつつ処理の切り分けも行って上で実装する事が出来るようになります。
文字で書くと大層な事をやるように感じられますが、やる事は簡単ですのでご安心を。
今回の解説を行う環境は以下の通りです。
- OS:macOS Sierra 10.12.1
- Xcode:8.1
- Swift:3.0
UITableViewを普通に実装するとこんなに長くなる
以前の記事「UITableViewの使い方」で書いたコードを今回はExtensionを使って実装してみます。
その時に使ったコード↓
(Swift3の書き方に変更しています)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
import UIKit class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { let fruits = ["リンゴ", "みかん", "ぶどう"] override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } /// セルの個数を指定するデリゲートメソッド(必須) func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return fruits.count } /// セルに値を設定するデータソースメソッド(必須) func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // セルを取得する let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) // セルに表示する値を設定する cell.textLabel!.text = fruits[indexPath.row] return cell } /// セルが選択された時に呼ばれるデリゲートメソッド func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("セル番号:\(indexPath.row) セルの内容:\(fruits[indexPath.row])") } } |
これだけでも中々コード量ありますよね。
コメントアウトもありますし、このコードでは未使用のdidReceiveMemoryWarning
メソッドやviewDidLoad
メソッドを書いたままという事もコード量増加の原因になっていますが。
しかし、普段の開発の際にはこれ以外にもコードを大量に書いていきます。
そのためUITableViewのデータソースメソッドやデリゲートメソッドが別に書けるだけでも可読性が上がりそうじゃないですか?
そこで使用するのが「Extension」になります。
Extensionを使ってTableViewを実装する
Extensionを使って実装する前にExtensionについての説明を簡単に。
Extensionとは
Extensionを使う事で既存のクラスにメソッドを追加したり、プロパティを追加する事が出来ます。
「Extension」という英語は「拡張・拡大・延長」という意味がありますが、文字通りクラスを拡張する事が出来ます。
個人的に以下の文章が非常に分かりやすいなと思ったので引用させて頂きます。
拡張(エクステンション)とは、既存のクラスや構造体、Enumからデータ型まであらゆるものに対して、名前はそのままにプロパティやメソッドを追加する機能である。
プロパティやメソッドを追加する機能といえば「継承」をイメージするが、「継承」はクラスのプロパティやメソッドを引き継いで新たなクラスを別名で作るのに対し、「拡張」は名前はそのままにプロパティやメソッドを追加することができるのが特徴である。
【Swift】拡張(エクステンション)の使い方。クラスからデータ型まであらゆる機能を拡張する。 | はじはじアプリ体験記より引用
実際にExtensionを使って実装
具体的にどのように実装するかというと「UITableViewを実装したいViewControllerクラスを拡張し、そこにUITableViewのデリゲートメソッドやデータソースメソッドのみを書く」という事をします。
以下が実際のコードになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import UIKit class ViewController: UIViewController { let fruits = ["リンゴ", "みかん", "ぶどう"] override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } /// TableViewのデリゲートメソッド・データソースメソッド extension ViewController: UITableViewDelegate, UITableViewDataSource { /// セルの個数を指定するデリゲートメソッド(必須) func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return fruits.count } /// セルに値を設定するデータソースメソッド(必須) func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // セルを取得する let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) // セルに表示する値を設定する cell.textLabel!.text = fruits[indexPath.row] return cell } /// セルが選択された時に呼ばれるデリゲートメソッド func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("セル番号:\(indexPath.row) セルの内容:\(fruits[indexPath.row])") } } |
20行目からがExtensionを使った実装です。
やっている事は単純ですよね。
UITableViewのデリゲートメソッドやデータソースメソッドを別に分けて書いただけのようなものなので。
ただ「たったこれだけで可読性上がるの?」と疑問に思われるかもしれません。
この書き方の利点は今回のようにUITableViewだけではなく、UITextFieldやUITextViewなど複数のデリゲートを一緒に実装する場合に特に威力を発揮します。
実際に以下のような実装が可能になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import UIKit class ViewController: UIViewController { let fruits = ["リンゴ", "みかん", "ぶどう"] override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } /// TableViewのデリゲートメソッド・データソースメソッド extension ViewController: UITableViewDelegate, UITableViewDataSource { /// セルの個数を指定するデリゲートメソッド(必須) func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return fruits.count } /// セルに値を設定するデータソースメソッド(必須) func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // セルを取得する let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) // セルに表示する値を設定する cell.textLabel!.text = fruits[indexPath.row] return cell } /// セルが選択された時に呼ばれるデリゲートメソッド func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("セル番号:\(indexPath.row) セルの内容:\(fruits[indexPath.row])") } } extension ViewController: UITextFieldDelegate { //UITextFieldのデリゲートメソッド } extension ViewController: UITextViewDelegate { //UITextViewのデリゲートメソッド } |
44行目からが追加したコードになります。
このように書く事で「UITextField」の処理はUITextFieldDelegate
をextensionで実装したところに。「UITextView」の処理はUITextViewDelegate
をExtensionで実装したところに。
というように処理の切り分けが出来るようになります。
処理の切り分けが出来れば自然と可読性も上がりますし、Extensionでの実装もそこまで難しくないので、積極的に使っていきましょう!
Extensionを使う場合の注意点(Swift3以降)
Swift3で変更が加わった事でprivateな値・メソッドを使う際の注意点です。
privateな変数やメソッドをExtensionで拡張したクラス内でも使いたい場合、Swift2まではprivate
で定義したものも使用出来たのですが、Swift3以降ではfileprivate
と定義する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class ViewController: UIViewController { let fruits = ["リンゴ", "みかん", "ぶどう"] private let num1 = 3 fileprivate let num2 = 4 ・ ・ 省略 ・ ・ } extension ViewController: UITableViewDelegate, UITableViewDataSource { /// セルの個数を指定するデリゲートメソッド(必須) func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { print(num1) //エラーになる print(num2) //エラーにならない return fruits.count } ・ ・ 省略 ・ ・ } |
あとがき
Swiftの勉強を始めたばかりの頃は「Extensionなんて使う機会ないだろう」ぐらいに思っていましたが、今回のような使い方を知ってよく使うようになりました。
このようなデリゲートをExtensionを使って実装する方法は、UITableViewだけに限らず色んな場面で使える手法のなので積極的に使っていきましょう!
ではでは
今回はこの辺で!ヽ(•̀ω•́ )ゝ✧
