ボクココ

個人開発に関するテックブログ

UITableViewをカスタマイズして表示する流れ

これは今後何度も使いそうになるのでメモ。

今回やることは、UITableViewの各セルの内容を独自のUIにしつつ、そのデータをネットワーク経由で取得するというもの。 Swift, Xcode 6.1。

UITableView を設置

いつも通りStoryBoardからUITableViewとUITableViewCellをそれぞれ配置。

UITableView の Outlets である dataSourcedelegate のそれぞれの向き先を対象のViewControllerへ。 さらに UITableView の Outlets も ViewController に登録する。

f:id:cevid_cpp:20141114020027p:plain

カスタム UITableViewCell の作成

File -> New で Cocoa Class を指定。 名前を「CustomViewCell」にする。継承元に UITableViewCell を指定。すると、swift ファイルと xib ファイルの2つができる。

xib ファイルには既に UITableViewCellが設置されているのでその中にカスタマイズしたセルの内容を配置していく。配置したそれぞれのUI Objectは、 Outlet で CustomViewCell に登録しておく。

f:id:cevid_cpp:20141114020427p:plain

StoryBoard 内でCustomViewCellを指定

StoryBoard の UITableViewCell の CustomClass に CustomViewCell を指定する。また、identifier を myCell とする。

ViewController でコーディング

いよいよコーディングに入る。以下が基本形だ。

import UIKit

class BoardViewController : UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    @IBOutlet weak var mTableView: UITableView!
    
    var posts: [AnyObject] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // custom view
        var nib  = UINib(nibName: "CustomTableViewCell", bundle:nil)
        mTableView.registerNib(nib, forCellReuseIdentifier:"myCell")
        mTableView.estimatedRowHeight = 100.0
        mTableView.rowHeight = UITableViewAutomaticDimension
    }
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return posts.count
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCellWithIdentifier("myCell") as? CustomTableViewCell
        var post = posts[indexPath.row] as Post
        cell?.message.text = post.content

        return cell!
    }
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath,animated: true)
    }
}

CustomViewCell に表示する内容はエンティティとして作成する。上記例だと Post。この配列をViewController内でフィールドとして保持し、その配列と対象メソッドの indexPathを元に表示すべきエンティティを取得し、Cellに反映するといった流れだ。CustomTableViewCell への反映は上記コードの内, cell?.message.text = post.content にあたる。

UITableViewAutomaticDimension を指定すると、高さがコンテンツに応じて動的に変化するようになる。

ネットワーク経由での取得

さてこれで実行しても中身が無いので、空のテーブルが表示されるだけだ。ネットワークを通じてJSONデータを取得しよう。

今回はHTTP通信のライブラリとして Alamofire を利用する。インストール方法等は別途参照

以下のような感じになる。

import Alamofire


class BoardViewController : UIViewController, UITableViewDataSource, UITableViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        // ....
        fetchData()
    }

    func fetchData() {
        Alamofire.request(.GET, "http://sample.com/posts.json")
            .responseJSON { (_, _, JSON, _) in
                var posts = JSON?["posts"]
                if let postArray = posts as? Array<Dictionary<String, AnyObject>> {
                    for post in postArray {
                        let p = Post(
                            content: post["content"] as String
                            //...
                        )
                        self.posts.append(p)
                    }
                }
                // update
                self.mTableView.reloadData()
        }
    }
}

取得してフィールドのpostsにデータが入り終わった後にself.mTableView.reloadData()するのがポイント。これで非同期にテーブルが更新できる。