68304


UITableViewのdidSelectRowAtを設定したいと思っています。

以下の3つのファイルを主に使っているのですが、CustomCell.swiftで実装したいのですが、実装方法がわかりません。  
ソースコードは全て[GitHub](https://github.com/zunda-pixel/AnkoMovie)に公開しています。

  * SearchViewController.swift  
<https://github.com/zunda-
pixel/AnkoMovie/blob/master/AnkoMovie/SearchViewController.swift>

  * CustomCell.swift  
<https://github.com/zunda-
pixel/AnkoMovie/blob/master/AnkoMovie/CustomCell.swift>

  * DataSource.swift  
<https://github.com/zunda-
pixel/AnkoMovie/blob/master/AnkoMovie/DataSource.swift>

SearchViewController.swift

[code]

    import UIKit
    
    class SearchViewController: UIViewController {
    
        private var tableView = UITableView()
        private var searchBar = UISearchBar()
        private var dataSource = DataSource()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            tableView.frame = view.bounds
            //Cellのデータを決定
            tableView.dataSource = dataSource as UITableViewDataSource
            //Cellの登録
            tableView.register(CustomCell.self, forCellReuseIdentifier: "cell")
    
            view.addSubview(tableView)
        }
    }
    
[/code]


CC BY-SA 4.0
2020-07-04T16:51:46.043
68277
2020-07-05T22:22:26.800
2020-07-05T07:04:35.257
40856
40856
question
swift
UITableViewのdidSelectRowAtの設定をしたい


オブジェクト指向のプログラミングには、独特のデザインパターンがいくつかあります。デザインパターンを直訳すると、設計様式とでもなるでしょうか。デザインパターンの有名なもののひとつに
**MVCデザインパターン** があります。（[ウィキペディア「Model View
Controller」](https://ja.wikipedia.org/wiki/Model_View_Controller)）  
macOS、iOS等のインターフェイスのフレームワーク（iOSでは、UIKit）は、このMVCデザインパターンに準拠して構成されています。なので、`Controller`が名前に含まれるクラスが多数あります。特に`UITableView`は、MVCデザインパターンの考え方を、強く反映したクラスとなっています。  
ひとつ、質問者さんにこちらから質問してみましょう。100行あるTableViewがあるとします。この`UITableView`インスタンスは、いくつの`UITableViewCell`インスタンスを持っているでしょうか？「100個」ではありません。画面上に10行表示されていたとすると、スクロールを考慮してもせいぜい12個の`UITableViewCell`のインスタンスを持っているだけです。データの数と、データを表示するセルの数は一致しません。Model（データ）とView（表示）を分離するという、MVCデザインパターンの基本的な考え方を、反映している結果です。  
`UITableView`のDelegateは2種類あります。`UITableViewDataSource`と`UITableViewDelegate`です。ふたつに分かれているのは、まさしくMVCデザインパターンに準拠しているからです。`UITableViewDataSource`は、Model（データ）を管理しているオブジェクト（クラス）へ委譲し、`UITableViewDelegate`は、Viewを制御しているオブジェクト（クラス）へ委譲します。

[Protocol
UITableViewDataSource](https://developer.apple.com/documentation/uikit/uitableviewdatasource)  
[Protocol
UITableViewDelegate](https://developer.apple.com/documentation/uikit/uitableviewdelegate)

メソッド`tableView(_:didSelectRowAt:)`が、`UITableViewDelegate`のメソッドであることを確認してください。  
委譲先が「Viewを制御しているオブジェクト」ならば、ここでは、`SearchViewController`にするのが妥当ですが、もちろんMVCデザインパターンの準拠が絶対ということではありません。テーブルのセルをタップして、選択状態にしたら、どういう挙動をするのかを考えて、委譲先を決定するのがいいでしょう。  
移譲先`UITableViewDataSource`を、`tableView.dataSource = dataSource as
UITableViewDataSource`と指定したように、移譲先`UITableViewDelegate`も、`tableView.delegate =
self`と指定します。

* * *

以下は、質問から離れて、ご提示のコードを見て、「それはちょっとまずいでしょ？」と感じたところをお話しします。  
まず、以下のサンプルコードを実際に動かしてみてください。XcodeのPlaygroundで動くように書いてあります。

[code]

    import PlaygroundSupport
    import UIKit
    
    class DataSource: NSObject {
        
        let reuseIdentifier = "Cell"
        
        var fruitsArray: [String]
        
        /* シングルトンの定型的な書き方。*/
        static let shared = DataSource()
        
        private override init() {
            fruitsArray = ["Apple", "Banana", "Candy", "Donut"]
        }
        /* ここまで */
    }
    
    extension DataSource: UITableViewDataSource {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return fruitsArray.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)
            cell.textLabel?.text = fruitsArray[indexPath.row]
            return cell
        }
    }
    
    class ViewController: UIViewController, UITableViewDelegate {
        
        var tableView: UITableView
        
        override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
            tableView = UITableView(frame: CGRect.zero, style: .plain)
            
            super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    
            self.view.addSubview(tableView)
            /* Storyboardでなく、コードを使ってViewをAuto Layoutでレイアウトする時の書き方*/
            tableView.translatesAutoresizingMaskIntoConstraints = false
            tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
            tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
            tableView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
            tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
            /* ここまで */
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            tableView.register(UITableViewCell.self, forCellReuseIdentifier: DataSource.shared.reuseIdentifier)
            tableView.dataSource = DataSource.shared
            tableView.delegate = self
        }
        
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            print("Selected")
        }
    }
    
    PlaygroundPage.current.liveView = ViewController()
    
[/code]

`UITableView`のDelegate`UITableViewDataSource`の委譲先のインスタンスには、ふたつの条件があります。ひとつは、オブジェクトTable
Viewが存在している間、移譲先のオブジェクトも存在していなければならないという点。もうひとつは、ただひとつだけ存在するインスタンスでなければならないという点です。これらの条件を満たすには、オブジェクト指向のもうひとつのデザインパターンである、[シングルトン（Singleton）](https://ja.wikipedia.org/wiki/Singleton_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)を採用するのがいいでしょう。

View（もちろんTable Viewも含みます）の画面上のレイアウトには、Auto
Layoutを使ってください。使用しないと、iPhone/iPadを横向きにするだけで、レイアウトが破綻してしまうことに、早く気づいてください。


CC BY-SA 4.0
2020-07-05T12:19:56.297
68304
2020-07-05T22:22:26.800
2020-07-05T22:22:26.800
18540
18540
68277
answer