読者です 読者をやめる 読者になる 読者になる

JavaプログラマがXcodeでiPhoneアプリを作ってみる.7週目

無限リストを作成する練習をしてみました。
具体的なコードは最後に載せています。実際に動作するプロジェクトもGitHubにあげました。

動作

  1. 最初に10データ表示
  2. リストの最後が表示されると、最後尾に自動で5行追加
  3. リストの最初で引っ張られると、先頭に自動で2行追加

1.最初に10データ表示

Cellの使い回しを自動的にやってくれるらしいのでUITableViewControllerやUITableViewを使います。重くなるを防げるようになります。
モデルとしてDataクラス、モデルのリストにdataArray:[Data]を準備して、画面表示時にdataArrayに10データ作成しておきます。



2.リストの最後が表示されると、最後尾に自動で5行追加

StoryBoard

Prototype Cellをデータ用(identifier:dataCell)とフッター用(identifier:fotterCell)の2つ用意します。

numberOfSectionsInTableView

2を返すように設定します。

cellForRowAtIndexPath

section=0の時はデータ用、section=1の時はフッター用のCellを返すようにします。

willDisplayCell

再表示を含めセルが表示されるときには、willDisplayCellラベルをもつtableViewメソッドが呼ばれるようです。
section=1のときにdataArrayに5行追加してテーブル再更新をすれば完了です。



3.リストの最初で引っ張られると、先頭に自動で2行追加

引っ張る動作は、すでにフレームワークで準備されていました。出来合いの動作でいいのであれば、UIRefreshControlをTableViewに追加して、addTargetでコールバックするメソッド名を指定するだけです。便利です。
コールバックイベントが発生した時に、self.dataArray.insert(newData, atIndex: 0)で単純にデータ追加でとりあえずそれっぽい動きをするようになりました。

ただこの方法だとindexPathがUITableViewが管理している内容とずれるはずなので、Cellクラスを複数使う場合には、tableView.dequeueReusableCellWithIdentifier()の箇所で各Cellクラスにキャストする時につじつまが合わなくなるような気がしますが今週はここまで。


ソース

https://github.com/grachro/junk201408-swift-TableView-InfiniteScroll

//
//  ViewController.swift
//  junk201408-swift-TableView-InfiniteScroll
//
//  Created by grachro on 2014/08/24.
//  Copyright (c) 2014年 grachro. All rights reserved.
//

import UIKit

class ViewController: UIViewController ,UITableViewDelegate, UITableViewDataSource {
    
    @IBOutlet weak var tableView: UITableView!

    let refreshControl = UIRefreshControl()
    
    let DETA_SECTION = 0
    let FOTTER_SECTION = 1
    
    var dataArray:[Data] = [] //モデルのリスト
    let INIT_DATA_COUNT = 10 //初期表示データ
    let ADD_DATA_COUNT = 5 ///最後尾が表示された時に追加するデータ数
    let REFLESH_DATA_COUNT = 2 ///引っ張られた時に上に追加するデータ数
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        addDataAfter(INIT_DATA_COUNT)
        
        self.refreshControl.addTarget(self, action: "callbackRefreshControl", forControlEvents: UIControlEvents.ValueChanged)
        
        self.tableView.delegate = self;
        self.tableView.dataSource = self
        self.tableView.addSubview(self.refreshControl)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    //UITableViewDataSource
    func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
        return 2
    }

    //UITableViewDataSource
    func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
        
        switch section {
            case DETA_SECTION:return self.dataArray.count
            case FOTTER_SECTION:return 1
            default:return 0
        }
  
    }

    //UITableViewDataSource
    func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
        
        switch indexPath.section {
        case DETA_SECTION:
            let cell = tableView.dequeueReusableCellWithIdentifier("dataCell", forIndexPath: indexPath) as DataCell
            
            let data = self.dataArray[indexPath.row]
            cell.setCaption("Dataセル セクション:\(data.index)  \(data.caption)")
            
            return cell
        case FOTTER_SECTION:
            let cell = tableView.dequeueReusableCellWithIdentifier("fotterCell", forIndexPath: indexPath) as UITableViewCell
            return cell
        default:return UITableViewCell()
        }

    }
    
    //UITableViewDelegate
    func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
        return 40
    }
    
    //UITableViewDelegate
    func tableView(tableView: UITableView!, willDisplayCell cell: UITableViewCell!, forRowAtIndexPath indexPath: NSIndexPath!) {

        //2つ目のセクションが表示されたら、モデルにデータ追加
        if indexPath.section == FOTTER_SECTION {
            addDataAfter(ADD_DATA_COUNT)
            self.tableView.reloadData()
        }
    }

    //モデル操作
    private func addDataAfter(count:Int) {
        for i in 0..<count {
            let last:Data? = self.dataArray.last
            let baseIndex = last == nil ? -1 : last!.index
            
            let newIndex = baseIndex + 1
            let newData = Data(index: newIndex,caption: "\(newIndex)行目")
            self.dataArray.append(newData)
        }
    }
    
    //UIRefreshControl
    func callbackRefreshControl(){
        addDataBefore(REFLESH_DATA_COUNT)
        self.tableView.reloadData()
        self.refreshControl.endRefreshing()
    }
    
    //モデル操作
    private func addDataBefore(count:Int) {
        for i in 0..<count {
            let first:Data = self.dataArray.first!
            let baseIndex = first.index
            
            let newIndex = baseIndex - 1
            let newData = Data(index: newIndex,caption: "\(newIndex)行目")
            self.dataArray.insert(newData, atIndex: 0)
        }
    }

}