環境: Xcode Version 8.2.1 + Apple Swift version 3.0.2
サンプルの動作イメージ。
UITableView にカスタムセル表示を実装した UITableViewCell 継承クラスを接続。カスタムセル表示の実装は Auto Layout constraint 制約を使用してパーツを配置し、セルの高さを自動で計算。
[Pop]ボタンで項目を削除。[Push]ボタンで項目を追加。
ひっぱってリストを更新する機能は UIRefreshControl を使用。
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)
}
}
-
ref.
- [ヅ] Swift コードだけで実現する Auto Layout constraint 制約サンプル (ラベルを中心に表示するだけ) (2016-11-10)
- [ヅ] Swift コードだけで実現する Auto Layout constraint 制約サンプル (複数の項目を配置) (2016-11-17)
- UITableViewCell (UITableViewCell - MemoWiki)
- Auto Layout (AutoLayout - MemoWiki)
- UIRefreshControl 下にひっぱって更新 (UIRefreshControl - MemoWiki)
tags: swift iphone ios
Posted by NI-Lab. (@nilab)