JavaScriptプログラマがSwift iOSアプリを2週間で作って公開してみた〜その7 Tweet with UITableViewCell〜


JavaScriptプログラマー(JSer)がSwiftデビューして、ただ作りたいアプリを作ってみたシリーズ第7回目です。

前回はFabricを使ってTwitter APIを使用する実装についての紹介でした。

今回の記事はAPIを叩いて得られた結果をUITableViewCellとして表示する部分の解説です。

前回の記事でTwitterのSearch APIを叩いて得られたresponseのJSONからFabricが提供するTweetクラスTWTRTweetに変換する部分を紹介しました。

以下がそのコード例です。

TwitterAPI.swift

var list: [TWTRTweet] = []
if let statuses = top["statuses"] as? NSArray {
    list = TWTRTweet.tweetsWithJSONArray(statuses) as [TWTRTweet]
}
// callbackとして渡されるtweetsを実行する
tweets(list)

では、tweetsコールバックを渡している元のViewController側でこのTWTRTweetの配列をテーブルに描画していきます。

まずは自分のツイートを表示するためのタブとお気に入りを表示するためのタブで共通のViewControllerとしてBaseTweetViewControllerを作成します。

BaseTweetViewController.swift

import Foundation
import UIKit
import TwitterKit

class BaseTweetViewController: UIViewController {
    
    var tableView: UITableView!
    var tweets: [TWTRTweet] = []
    var prototypeCell: TWTRTweetTableViewCell?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // set title
        self.navigationItem.title = "title"
        
        // UITableViewを作成して、delegateとdataSourceを自分自身としてセットする
        tableView = UITableView(frame: self.view.bounds)
        tableView.delegate = self
        tableView.dataSource = self
        
        // tableで使用するcellのクラスを指定する
        prototypeCell = TWTRTweetTableViewCell(style: .Default, reuseIdentifier: "cell")
        
        tableView.registerClass(TWTRTweetTableViewCell.self, forCellReuseIdentifier: "cell")
        self.view.addSubview(tableView)
        
        // initial load
        self.load()
    }

    // for override
    func load(){
    }
}


// UITableViewDataSourceを実装する
extension BaseTweetViewController : UITableViewDataSource {
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tweets.count
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        // cellをTWTRTweetTableViewCellにキャストする
        let cell = tableView.dequeueReusableCellWithIdentifier("cell") as TWTRTweetTableViewCell
        if tweets.count > indexPath.row {
            let tweet = tweets[indexPath.row]
            cell.tag = indexPath.row
            cell.configureWithTweet(tweet)
        }
        return cell
    }
}

// UITableViewDelegateを実装する
extension BaseTweetViewController : UITableViewDelegate {
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        let tweet = tweets[indexPath.row]
        if tweets.count > indexPath.row {
            prototypeCell?.configureWithTweet(tweet)
        }
        // fabric 1.1以降は以下のクラスfuncでheightが取得できる
        return TWTRTweetTableViewCell.heightForTweet(tweet, width: self.view.bounds.width)
        // fabric 1.1より前のバージョンでは以下を使う
//        if let height = prototypeCell?.calculatedHeightForWidth(self.view.bounds.width) {
//            return height
//        } else {
//            return tableView.estimatedRowHeight
//        }
    }
}

SwiftのUITableは決められたプロトコルを提供しており、ViewControllerがそのプロトコルを実装することで表示してくれます(UITableViewDataSource, UITableViewDelegate)。

tableで表示するcellはviewcontroller初期化時にtableView.registerClassを用いてTWTRTweetTableViewCellを指定します。

Tableのレンダリングに関してはざっくり言うと、APIのレスポンスで得られたTWTRTweetの配列をtweetsという変数にセットして、Fabricが提供するUITableViewCellを継承したTWTRTweetTableViewCellに各tweetsのデータを渡しています(configureWithTweet)。

今回のアプリでは2つのタブでそれぞれsearchとlistFavoiteのAPIを使用するため、baseのクラスであるBaseTweetViewControllerでloadというfuncを用意しておき、それを継承するTimelineViewControllerとFavoriteViewControllerでloadをoverrideします。

TimelineViewController.swit

import Foundation
import TwitterKit
import UIKit

class TimelineViewController: BaseTweetViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // set navigation title
        self.navigationItem.title = "未読記事"
    }
    
    override func load(){
        var params = ["q": "from:" + Twitter.sharedInstance().session().userName + "+filter:links+-filter:images", "result_type": "recent", "count": "40"]
        TwitterAPI.search(params, tweets: {
            twttrs in
            for tweet in twttrs {
                self.tweets.append(tweet)
            }
            self.tableView.reloadData()
            }, error: {
                error in
                // error handling
        })
    }
}

FavoriteViewController.swift

import Foundation
import TwitterKit
import UIKit

class FavoriteViewController: BaseTweetViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // set navigation title
        self.navigationItem.title = "お気に入り"
    }
    
    override func load() {
        var params = ["count": "40"]
        TwitterAPI.listMyFavorites(params, {
            twttrs in
            for tweet in twttrs {
                self.tweets.append(tweet)
            }
            self.tableView.reloadData()
            }, error: {
                error in
                // error handling
        })
    }

それぞれBaseTweetViewControllerを継承したクラスで、前回紹介した方法でTwitterAPIを使用し、success callbackでself.tweetsにデータを追加していきます。これだけで、アプリを実行すると以下のようにTweetがCellにちゃんと表示されます。

– 未読記事タブ(TimelineViewController)
iOS Simulator Screen Shot 2015.02.08 18.13.54

– お気に入りタブ(FavoriteViewController)
iOS Simulator Screen Shot 2015.02.08 18.13.56

Cellの細かい見た目の実装などは不要で、Fabricの提供する部品とUIKitが提供するUITableの部品を使って簡単に実現できることがわかりますね。(ちなみにTweetの表示はきちんと公式が提供するデザインのものを使わないと規約違反になりますので注意が必要です。)

以上、今回はAPIで受けったデータをTableCellとして表示する部分の解説でした。次回は今回作ったCellをSwipeして何かアクションを行う部分の実装について紹介します。

TwitStockerのダウンロードはこちらから。
https://itunes.apple.com/en/app/twitstocker/id958798898?l=ja&ls=1&mt=8