[swift]挫折しながら覚えるiOS開発その3 カスタムセルの実装


[まとめ] 現在開催中のKindleセール情報はこちら

前回はカスタムセルのレイアウト作成を行ったので、今回はいよいよ実装に入っていきます。

記事毎の情報を持つLinkクラス(Model)と、Linkインスタンスから情報を組み立てて表示するカスタムセルクラスを作成します。

API通信部分は次回にして、今回は決め打ちのサンプルデータでテーブルセルを確認できるところまで進めます。

今回は主にQiitaのこちらの記事を参考にさせていただきました。

SwiftでCustomCellを作って画像付きリスト表示 – Qiita

1. Linkクラスの作成

「File」=>「New」=>「File」で新規ファイルを追加。

ios3-1

「Swift File」を選択。

ios3-2

「Link.swift」として追加。

ios3-3

前回作成したレイアウトに必要な情報をインスタンス変数に設定できるようにインスタンス変数とinitメソッドを追加します。

# Link.swift

import Foundation

class Link : NSObject {
    var title:String
    var username:String
    var userIconUrl:String?
    var stockCount:Int

    init(title: String, username: String, userIconUrl: String?, stockCount: Int){
        self.title = title
        self.username = username
        self.userIconUrl = userIconUrl
        self.stockCount = stockCount
    }
}

これでLinkモデルができました。

2. LinkViewCellクラスの作成

Linkクラスと同様に「File」=>「New」=>「File」で新規ファイルを追加。

UITableViewCellを継承したLinkViewCellクラスを作成します。

import UIKit

class LinkViewCell: UITableViewCell {
}

続いて、StoryboardのTableViewCellがLinkViewCellを参照するように「Custom Class」を変更します。

(このCustom Classの変更に気づかずかなりハマりました。。。)

ios3-4-1

Classが「LinkViewCell」に変更されたことを確認。

ios3-5

これでStoryboardのカスタムセルにLinkViewCellを紐付けることができたので、次はStoryboardの各要素を紐付けるようにします。

Storyboardとクラスの2画面表示に切り替えます。

そのままだとViewControllerが表示されてしまったので、以下のようにヘッダーメニューをクリックしてLinkViewCellクラスに切り替えます。

ios3-6

クラスが切り替わったら、title要素を選択して、ctrlを押しながら、LinkViewCellへドラッグ&ドロップ。

同様にして、各プロパティを設定していきます。

ios3-7

最終的には以下の4つのプロパティを追加します。

そして、カスタムセルを設定するためのsetCellメソッドを追加しておきます。

userIconUrlの部分はnilが入る可能性があるので、nilチェックを行っています。

それ以外は単にLinkモデルのデータを設定しているだけになります。

# LinkViewCell.swift

import UIKit

class LinkViewCell: UITableViewCell {

    @IBOutlet weak var title: UILabel!
    @IBOutlet weak var userIcon: UIImageView!
    @IBOutlet weak var username: UILabel!
    @IBOutlet weak var stockCount: UILabel!

    func setCell(link: Link) {
        self.title.text = link.title
        self.username.text = link.username
        self.stockCount.text = String(link.stockCount)

        if let userIconUrl = link.userIconUrl {
            let data = NSData(contentsOfURL: NSURL(string: userIconUrl)!)
            self.userIcon.image = UIImage(data: data!)
        }
    }
}

3. ViewControllerの編集

LinkViewCellの設定はできましたが現状だとViewController側は前々回の設定のままであるため、シミュレータを起動しても結果が変わりません。

前々回に作成した以下のViewControllerを編集してLinkViewCellの内容を表示できるように変更します。

# 前々回のViewController.swift

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        tableView.delegate = self
        tableView.dataSource = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    let texts = ["hello", "swift", "world"]

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return texts.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "Cell")

        cell.textLabel?.text = texts[indexPath.row]
        return cell
    }

}

ダミーデータを登録するsetupLinksメソッドを追加して、viewDidLoadの中で呼び出し初期設定を行います。

また、tableViewメソッド内の内容もLinkViewCellを呼び出すように変更します。

最終的なコードは以下のようになります。

# 今回のViewController.swift

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.setupLinks()  // Link情報を読み込んで変数に設定

        tableView.delegate = self
        tableView.dataSource = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    var links = [Link]() // Linkの情報をまとめる配列

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return links.count // link数に変更
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        // IdentifierがLinkViewCellのセルを使いまわす
        let cell: LinkViewCell = tableView.dequeueReusableCellWithIdentifier("LinkViewCell")! as! LinkViewCell
        // cellの内容を設定
        cell.setCell(links[indexPath.row])
        return cell
    }

    // linksの設定
    func setupLinks() {
        // 適当なデータを設定します
        var l1 = Link(title: "hello world", username: "hilotter", userIconUrl: "https://pbs.twimg.com/profile_images/1313260738/______2_normal.png", stockCount: 99)
        var l2 = Link(title: "hello swift", username: "hilotter", userIconUrl: nil, stockCount: 50)
        links.append(l1)
        links.append(l2)
    }
}

dequeueReusableCellWithIdentifierでLinkViewCellを指定することで、IdentifierがLinkViewCellとなっているセルを使いまわすことができますが、Identifierを設定してしていない状態でシミュレータを起動すると以下のようなエラーが出てシミュレータが落ちます。

fatal error: unexpectedly found nil while unwrapping an Optional value
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier LinkViewCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

最初Identifierの設定が抜けていたため上記エラーでハマっていました。

IdentifierはStoryboardのLinkViewCellを選択した状態で「Attributes inspector」から設定できます。

ios3-8-1

Identifierを設定した状態でシミュレータを起動するとカスタムセル版のTableViewが表示されます。

ios3-9

これでカスタムセルの表示までができました。

次回はいよいよAPI通信の部分を作ります。

参考

[まとめ] 現在開催中のKindleセール情報はこちら