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


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

前回はFabric(Twitter API)を使用して、ResponseをUITableViewとして表示する部分の実装について紹介しました。今回は表示したCellに左スワイプと右スワイプを可能にして、カスタムなアクションを加えていく実装方法について紹介します。

Fabricを使用してTweetsをTableViewで表示するときはTWTRTweetTableViewCellという部品を使用していると思います。

今回はそのTWTRTweetTableViewCellを継承したカスタムなCellクラスを作って、そこにスワイプによるアクションを定義していきます。

未読記事を管理するためのViewで使用するCellのクラスをStockedTweetTableViewCell.swiftで定義します。
このクラスでのスワイプ時アクションについては以下のように動作します。

  • 左スワイプ: readTweetという未読記事に対して読んだフラグを立てるための処理を行う
  • 右スワイプ: favoriteTweetというTwitter上でのお気に入り処理を行う

StockedTweetTableViewCell.swift

import Foundation
import UIKit
import TwitterKit

@objc protocol StockedTableViewCellDelegate {
    optional func favoriteTweet(cell: StockedTweetTableViewCell)
    optional func readTweet(cell: StockedTweetTableViewCell)
}

class StockedTweetTableViewCell : TWTRTweetTableViewCell {
    
    weak var delegate: StockedTableViewCellDelegate?
    var haveButtonsDisplayed = false
    
    func favoriteTweet() {
        delegate?.favoriteTweet?(self)
    }

    func readTweet() {
        delegate?.readTweet?(self)
    }

    override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.selectionStyle = .None
        // バックグラウンドに使用されるViewを生成する
        self.createView()
        // 右スワイプ時のアクションの紐付け
        self.contentView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: "onRightSwipe"))
        // 左スワイプ時のアクションの紐付け
        let swipeRecognizer = UISwipeGestureRecognizer(target: self, action: "onLeftSwipe")
        swipeRecognizer.direction = .Left
        self.contentView.addGestureRecognizer(swipeRecognizer)
    }
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func createView() {
        self.contentView.backgroundColor = UIColor.whiteColor()
        self.backgroundView = UIView(frame: self.bounds)
    }
    
    func onLeftSwipe() {
        // カスタムカラーをバックグラウンドに指定
        self.backgroundView?.backgroundColor = Constants.Theme.concept()
        UIView.animateWithDuration(0.1, animations: {
            let size   = self.contentView.frame.size
            let origin = self.contentView.frame.origin
            self.contentView.frame = CGRect(x: origin.x - 100, y:origin.y, width:size.width, height:size.height)
            }) { completed in
                // アニメーション完了時に100ずれていたら、readTweetを実行する
                if self.contentView.frame.origin.x == -100 {
                    self.delegate?.readTweet?(self)
                }
            }
    }
    
    // 外からcellを左にずらすためのユーティリティ
    func moveToLeft() {
        let size   = self.contentView.frame.size
        let origin = self.contentView.frame.origin
        self.contentView.frame = CGRect(x: origin.x - 100, y:origin.y, width:size.width, height:size.height)
    }
    
    func onRightSwipe() {
        // カスタムカラーをバックグラウンドに指定
        self.backgroundView?.backgroundColor = Constants.Theme.twitter()
        UIView.animateWithDuration(0.1, animations: {
            let size   = self.contentView.frame.size
            let origin = self.contentView.frame.origin
            self.contentView.frame = CGRect(x: origin.x + 100, y:origin.y, width:size.width, height:size.height)
            }) { completed in
                // アニメーション完了時に100ずれていたら、favoriteTweetを実行する
                if self.contentView.frame.origin.x == 100 {
                    self.delegate?.favoriteTweet?(self)
                }
            }
    }
    
    // 外からcellを右にずらすためのユーティリティ
    func moveToRight() {
        let size   = self.contentView.frame.size
        let origin = self.contentView.frame.origin
        self.contentView.frame = CGRect(x: origin.x + 100, y:origin.y, width:size.width, height:size.height)
    }
}

ポイントはいくつかあります。

  • @objc protocol StockedTableViewCellDelegateを使用して、Swipe時のカスタムなアクションを実装するためにはここで定義しているfavoriteTweetとreadTweetを実装しなくてはいけないですよ、と明示するためのDelegateを宣言します。また、weak var delegate: StockedTableViewCellDelegate?とdelegate変数を定義して、それぞれのfavoriteTweetとreadTweet内でdelgateのfuncに自分自身を渡します
  • Initialize時にUISwipeGestureRecognizerを生成して、それぞれ左スワイプ時にonLeftSwipe、右スワイプ時にonRightSwipeが呼ばれるように紐付けます。
  • 各swipeアクションが実行された際には、UIView.animateWithDurationを読んでcellを100移動させます。そのアニメーション完了時にdelegateで実装される予定のfuncをcallbackとして呼び出します。

Cellを表示する側のViewControllerではtableViewを初期化するときに以下のように使用するCellのクラスを指定します。

TimelineViewController.swift

tableView.registerClass(StockedTweetTableViewCell.self, forCellReuseIdentifier: "cell")

また、スワイプ時のカスタムアクションの中身を実装するために、StockedTableViewCellDelegateをViewControllerが実装します。

TimelineViewController.swift

extension TimelineViewController: StockedTableViewCellDelegate {
    func favoriteTweet(cell: StockedTweetTableViewCell) {
        let index: Int = cell.tag
        self.submitFavorite(index, cell: cell)
    }

    func readTweet(cell: StockedTweetTableViewCell) {
        let index: Int = cell.tag
        self.submitRead(index)
    }
}

これで以下の画像のようにswipe時にアクションが実行されると思います。

左スワイプ時
スクリーンショット 2015-02-10 9.55.23

右スワイプ時
スクリーンショット 2015-02-10 9.55.33

以上、今回はswipe時のカスタムなアクションの実装についてでした。次回は既読処理(readTweet)を行った際にフラグをアプリ内のデータとして管理するためのCoreDataについて紹介しようかと思います。

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