環境: Xcode Version 8.2.1 + Apple Swift version 3.0.2

サンプルの動作イメージ。

UITableView にカスタムセル表示を実装した UITableViewCell 継承クラスを接続。カスタムセル表示の実装は Auto Layout constraint 制約を使用してパーツを配置し、セルの高さを自動で計算。

Swift で UITableViewCell を継承してテーブル(リスト)表示 (コードだけで実現する。コピペで動く)

[Pop]ボタンで項目を削除。[Push]ボタンで項目を追加。

Swift で UITableViewCell を継承してテーブル(リスト)表示 (コードだけで実現する。コピペで動く)

ひっぱってリストを更新する機能は UIRefreshControl を使用。

Swift で UITableViewCell を継承してテーブル(リスト)表示 (コードだけで実現する。コピペで動く)

Xcode で新規に Single View Application のプロジェクトを作成して、以下の3ファイル分のコードを追加すればOK(ViewController.swiftはデフォルトで用意されているものを上書き)。

ViewController.swift では、メインのUIパーツを配置するのがメイン。


//
//  ViewController.swift
//

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
  
  private let cellReuseIdentifier = "MyTable"
  private let myTableView = UITableView()
  private let myRefreshControl = UIRefreshControl()
  private let myPopButton = UIButton()
  private let myPushButton = UIButton()
  private let myItemList = MyItemList()
  
  override func viewDidLoad() {

    print("viewDidLoad")
    
    super.viewDidLoad()

    // screen size
    let w: CGFloat = self.view.frame.width
    let h: CGFloat = self.view.frame.height
    let sbh: CGFloat = UIApplication.shared.statusBarFrame.size.height
    
    // button size
    let bw: CGFloat = w / 2
    let bh: CGFloat = 50
    
    // table size
    let tw = w * 3 / 4
    let th = h / 2
    
    // table
    myTableView.frame = CGRect(x: w / 2 - tw / 2, y: h / 2 - th / 2 + sbh, width: tw, height: th)
    myTableView.register(MyCustomCell.self, forCellReuseIdentifier: cellReuseIdentifier)
    myTableView.dataSource = self
    myTableView.delegate = self

    // auto layout
    myTableView.estimatedRowHeight = MyCustomCell.geteEtimatedRowHeight()
    myTableView.rowHeight = UITableViewAutomaticDimension
    self.view.addSubview(myTableView)
    
    // refresh control
    myRefreshControl.attributedTitle = NSAttributedString(string: "pull update")
    myRefreshControl.addTarget(self, action: #selector(self.onRefresh(sender:)), for: .valueChanged)
    myTableView.addSubview(myRefreshControl)
    
    // pop button
    myPopButton.frame = CGRect(x: w / 2 - bw / 2, y: bh + sbh, width: bw, height: bh)
    myPopButton.backgroundColor = UIColor.red
    myPopButton.layer.masksToBounds = true
    myPopButton.layer.cornerRadius = 20.0
    myPopButton.setTitle("Pop", for: .normal)
    myPopButton.setTitleColor(UIColor.white, for: .normal)
    myPopButton.setTitle("Pop", for: .highlighted)
    myPopButton.setTitleColor(UIColor.black, for: .highlighted)
    myPopButton.addTarget(self, action: #selector(self.onClickPopButton(sender:)), for: .touchUpInside)
    self.view.addSubview(myPopButton)
    
    // push button
    myPushButton.frame = CGRect(x: w / 2 - bw / 2, y: h - bh * 2, width: bw, height: bh)
    myPushButton.backgroundColor = UIColor.red
    myPushButton.layer.masksToBounds = true
    myPushButton.layer.cornerRadius = 20.0
    myPushButton.setTitle("Push", for: .normal)
    myPushButton.setTitleColor(UIColor.white, for: .normal)
    myPushButton.setTitle("Push", for: .highlighted)
    myPushButton.setTitleColor(UIColor.black, for: .highlighted)
    myPushButton.addTarget(self, action: #selector(self.onClickPushButton(sender:)), for: .touchUpInside)
    self.view.addSubview(myPushButton)
  }
  
  internal func onRefresh(sender: UIButton) {
    print("onRefresh")
    myItemList.reset()
    myTableView.reloadData()
    myRefreshControl.endRefreshing()
  }
  
  internal func onClickPopButton(sender: UIButton) {
    print("onClickPopButton")
    myItemList.pop()
    myTableView.reloadData()
  }
  
  internal func onClickPushButton(sender: UIButton) {
    print("onClickPushButton")
    myItemList.push()
    myTableView.reloadData()
  }

  // return number of cells / UITableViewDataSource
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    print("numberOfRowsInSection")
    return myItemList.count()
  }
  
  // set value to a cell / UITableViewDataSource
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    print("cellForRowAt")
    let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath as IndexPath) as! MyCustomCell
    let item = myItemList.get(indexPath.row)
    cell.setItem(item)
    return cell
  }

  // at select cell / UITableViewDelegate
  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    print("didSelectRowAt")
    let item = myItemList.get(indexPath.row)
    print("Num: \(indexPath.row)")
    print("Title: \(item.title)")
    print("Desc: \(item.desc)")
  }

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

MyCustomCell.swift ファイル。 MyCustomCell は UITableViewCell を継承したクラスで、テーブルセルのカスタム表示部分を担当する。セル内部のUI部品は Auto Layout で配置する。 Auto Layout を使うことによって、セルの高さを自動で計算してくれる。


//
//  MyCustomCell.swift
//

import Foundation
import UIKit

// カスタム化されたテーブルのセル
class MyCustomCell: UITableViewCell {

  let titleLabel = UILabel()
  let descLabel = UILabel()

  static func geteEtimatedRowHeight() -> CGFloat {
    // とりあえずの推定の高さ
    return 22 + 14 + 22 + 22 + 22;
  }
  
  func setItem(_ item: MyItem) {
    titleLabel.text = item.title
    descLabel.text = item.desc
  }
  
  required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)!
  }
  
  override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    
    // セルの背景色
    self.backgroundColor = UIColor.cyan
    
    // セル選択状態のスタイル
    self.selectionStyle = UITableViewCellSelectionStyle.none
    
    // セルの境界線のマージン(左端から境界線が引かれるように設定)
    self.separatorInset = UIEdgeInsets.zero
    
    // セルを構築する
    MyCustomCell.addContentView(frameView: self.contentView, descLabel: self.descLabel, titleLabel: self.titleLabel)
  }

  // セルをひとつ構築する
  private static func addContentView(frameView: UIView, descLabel: UILabel, titleLabel: UILabel) {
    let titleLabel = MyCustomCell.addtitleLabel(frameView: frameView, titleLabel: titleLabel)
    _ = MyCustomCell.adddescLabel(frameView: frameView, titleLabel: titleLabel, descLabel: descLabel)
  }
  
  private static func addtitleLabel(frameView: UIView, titleLabel: UILabel) -> UILabel {
    
    // ラベルを構築
    titleLabel.textAlignment = .right
    titleLabel.font = UIFont.systemFont(ofSize: 14)
    titleLabel.textColor = .purple
    titleLabel.backgroundColor = .yellow
    frameView.addSubview(titleLabel);
    
    // Autosizing を constraints に適用しない
    titleLabel.translatesAutoresizingMaskIntoConstraints = false
    
    // constraints を設定
    frameView.addConstraints([
      
      // 上端: 22ポイント空ける
      NSLayoutConstraint(
        item: titleLabel,
        attribute: .top,
        relatedBy: .equal,
        toItem: frameView,
        attribute: .top,
        multiplier: 1.0,
        constant: 22),
      
      // 左端: 22ポイント空ける
      NSLayoutConstraint(
        item: titleLabel,
        attribute: .left,
        relatedBy: .equal,
        toItem: frameView,
        attribute: .left,
        multiplier: 1.0,
        constant: 22),
      
      // 右端: 22ポイント空ける
      NSLayoutConstraint(
        item: titleLabel,
        attribute: .right,
        relatedBy: .equal,
        toItem: frameView,
        attribute: .right,
        multiplier: 1.0,
        constant: -22),
    ])
    
    return titleLabel
  }
  
  private static func adddescLabel(frameView: UIView, titleLabel: UIView, descLabel: UILabel) -> UILabel {
    
    // ラベルを構築
    descLabel.textAlignment = .left
    descLabel.font = UIFont.systemFont(ofSize: 22)
    descLabel.numberOfLines = 0
    descLabel.textColor = .red
    descLabel.backgroundColor = .blue
    frameView.addSubview(descLabel);
    
    // Autosizing を constraints に適用しない
    descLabel.translatesAutoresizingMaskIntoConstraints = false
    
    // constraints を設定
    frameView.addConstraints([
      
      // 上端: 22ポイント空ける
      NSLayoutConstraint(
        item: descLabel,
        attribute: .top,
        relatedBy: .equal,
        toItem: titleLabel,
        attribute: .bottom,
        multiplier: 1.0,
        constant: 22),
      
      // 左端: 22ポイント空ける
      NSLayoutConstraint(
        item: descLabel,
        attribute: .left,
        relatedBy: .equal,
        toItem: frameView,
        attribute: .left,
        multiplier: 1.0,
        constant: 22),
      
      // 右端: 22ポイント空ける
      NSLayoutConstraint(
        item: descLabel,
        attribute: .right,
        relatedBy: .equal,
        toItem: frameView,
        attribute: .right,
        multiplier: 1.0,
        constant: -22),
      
      // 下端: 22ポイント空ける
      NSLayoutConstraint(
        item: descLabel,
        attribute: .bottom,
        relatedBy: .equal,
        toItem: frameView,
        attribute: .bottom,
        multiplier: 1.0,
        constant: -22),
    ])
    
    return descLabel
  }
}

MyItemList.swift ファイル。 MyItemList はテーブルに表示するアイテムを管理する。


//
//  MyItemList.swift
//

import Foundation

// 表示するアイテム情報
struct MyItem {
  var title: String
  var desc: String
}

// 表示するアイテムを管理する
class MyItemList {

  private var myItemList: [MyItem] = []
  
  init() {
    reset()
  }

  // アイテムのリストをクリアしてデフォルトのアイテムを追加
  func reset() {
    // default my item list
    myItemList.removeAll()
    myItemList.append(MyItem(title: "TITLE", desc: "DESC"))
    myItemList.append(MyItem(title: "タイトル", desc: "ディスクリプション"))
    myItemList.append(MyItem(title: "たいとるたいとる", desc: "でぃすくりぷしょんでぃすくりぷしょん"))
  }

  // アイテムの数を返す
  func count() -> Int {
    return myItemList.count;
  }
  
  // 指定したインデックスのアイテムを返す
  func get(_ index: Int) -> MyItem {
    return myItemList[index]
  }

  // アイテムを1つ削除
  func pop() {
    if !myItemList.isEmpty {
      myItemList.removeFirst()
    }
  }
  
  // アイテムを1つ追加
  func push() {
    myItemList.append(createItem())
  }
  
  // アイテムを生成する
  private func createItem() -> MyItem {
    // randomly generate title and desc
    let a = arc4random_uniform(10000)
    let b = arc4random_uniform(1000000000)
    let c = arc4random_uniform(10) + 1
    let newTitle = "T-\(a)"
    let newDesc = String(repeating: "あああ\(b)", count: Int(c))
    return MyItem(title: newTitle, desc: newDesc)
  }
}

tags: swift iphone ios

Posted by NI-Lab. (@nilab)