[Swift/iOS] フリックで更新するTableView

SNSのアプリなどでおなじみのタイムラインを下にフリックしてテーブルを更新させるためのコードです。

例として1から6までの数字が並んだテーブルがフリックする度に数字が2倍されていくというものを作ります。
この2倍する処理がネットワーク経由でデータを更新する処理だと思ってください。


UI部品の配置

  • デフォルトのViewの中にTableViewを追加
  • TableViewの中にTableViewCellを追加
  • TableViewCellの中にLabelを追加

TableViewCellの設定

  • UITableViewCellを継承した新しいクラス(NumberCell)を追加
  • TableViewCellのIdentity Inspector→Custom Class→ClassにNumberCellを指定
  • Attribute Inspector→Table View Cell→Identifierに識別子(numberCell)を指定
  • Labelにカーソルを合わせてCtrlキーを押しながらNumberCellにドラッグしコネクションを設定

[swift title=”NumberCell.swift”]
import Foundation
import UIKit

class NumberCell: UITableViewCell {

@IBOutlet weak var label: UILabel!
}
[/swift]

Labelの設定

  • Labelの上下左右にスペースのConstraintを設定

ViewControllerの設定

  • UITableViewDataSourceとUITableViewDelegateを継承
  • UITableViewDataSourceとUITableViewDelegateの必須メソッドを実装
  • UIRefreshControlオブジェクトを生成しUIRefreshControl.addTarget()でスワイプ時に呼ぶメソッド(refresh)を指定
  • refreshControlオブジェクトをaddSubview()でTableViewにサブビューとして追加
  • refreshを実装。必ずUIRefreshControl.endRefreshing()を呼ぶ

[swift title=”ViewController.swift”]
import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

@IBOutlet weak var tableView: UITableView!

var numberList:[Int] = [1, 2, 3, 4, 5, 6]
var refreshControl:UIRefreshControl!
let semaphore = DispatchSemaphore(value: 1)

override func viewDidLoad() {
super.viewDidLoad()

tableView.delegate = self
tableView.dataSource = self

refreshControl = UIRefreshControl()
refreshControl.attributedTitle = NSAttributedString(string: "再読み込み中")
refreshControl.addTarget(self, action: #selector(ViewController.refresh), for: UIControlEvents.valueChanged)
tableView.addSubview(refreshControl)
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func updateTable () {
//時間がかかる処理と想定してグローバルキューで実行
DispatchQueue.global().async {

for (i, num) in self.numberList.enumerated() {
self.numberList[i] = num * 2
}
DispatchQueue.main.async {
// UI更新はメインスレッドで実行
self.tableView.reloadData()
self.semaphore.signal()
}
}
}

//UIRefreshControl によって画面を縦にフリックしたあとに呼ばれる
@objc func refresh() {
updateTable()
// 別スレッドでの処理が終了するのを待つ
semaphore.wait()
semaphore.signal()
//この処理の前にbeginRefreshingが呼ばれているはずなので終了する
refreshControl.endRefreshing()
}

//UITableViewDataSourceプロトコルの必須メソッド
//テーブルの行数を返す
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberList.count
}

//UITableViewDataSourceプロトコルの必須メソッド
//指定行のセルデータを返す
internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "numberCell", for: indexPath) as? NumberCell else {
return UITableViewCell()
}
cell.label.text = String(numberList[indexPath.row])
return cell
}

//UITableViewDelegateプロトコルの必須メソッド
//行をタップされたときに呼ばれる
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:IndexPath) {
print(numberList[indexPath.row])
}
}

[/swift]

[Swift4/iOS] TableViewで高さの違うセルを表示する

Twitterの投稿をTableViewで表示する場合など、各セルの高さを投稿の長さに応じて変えたい場合があると思います。
例)

そんなときのコード例です。

UI部品の配置

  • デフォルトのViewの中にTableViewを追加
  • TableViewの中にTableViewCellを追加
  • TableViewCellの中にLabelを追加

TableViewCellの設定

  • UITableViewCellを継承した新しいクラス(TextCell)を追加しIdentity Inspector→Custom Classで指定する
  • カーソルをLabelにあてて、Ctrlキーを押しながらTableViewCellクラスにドロップ。コネクションを設定する
  • Attribute Inspector→Table View Cell→Identifierに識別子(textCell)を設定

[swift title=”TextCell.swift”]
import UIKit

class TextCell: UITableViewCell {

@IBOutlet weak var label: UILabel!
}
[/swift]

Labelの設定

  • Attribute Inspector→Label→Linesをゼロに設定
  • Labelの上下左右にスペースのConstraintを設定

ViewControllerの設定

  • カーソルをTableViewに合わせてCtlキーを押しながらViewControllerにドラッグ。コネクションを設定する
  • viewDidLoad内でtableView.rowHeightにUITableViewAutomaticDimensionにセット。tableView.estimatedRowHeightも適当な値をセットしておく
  • UITableViewDataSourceとUITableViewDelegateを継承
  • UITableViewDataSourceプロトコルのメソッドを実装
  • UITableViewDelegateプロトコルのメソッドを実装

[swift title=”TableViewController.swift”]
import UIKit

class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

@IBOutlet weak var tableView: UITableView!

let textList:[String] = [
"Hello World",
"Hello World\nHello World\nHello World\nHello World\nHello World\n",
"Hello World",
"Hello World\nHello World\nHello World\n",
"Hello World\nHello World\n"]

override func viewDidLoad() {
super.viewDidLoad()

tableView.delegate = self
tableView.dataSource = self

tableView.estimatedRowHeight = 100.0
tableView.rowHeight = UITableViewAutomaticDimension
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

//UITableViewDataSourceプロトコルの必須メソッド
//テーブルの行数を返す
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return textList.count
}

//UITableViewDataSourceプロトコルの必須メソッド
//指定行のセルデータを返す
internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

guard let cell = tableView.dequeueReusableCell(withIdentifier: "textCell", for: indexPath) as? TextCell else {
return UITableViewCell()
}

cell.label.text = textList[indexPath.row]
return cell
}

//UITableViewDelegateプロトコルの必須メソッド
//行をタップされたときに呼ばれる
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:IndexPath)
{
print(textList[indexPath.row])
}
}

[/swift]

[Swift4/iOS] TextField で開いたキーボードを閉じる

TextFieldをもつiOSアプリでユーザの特定のアクションでキーボードを閉じるためのコードです。

キーボードの『改行』キーを押された場合にキーボードを閉じる

  • 表示するViewControllerクラスにUITextFieldDelegateを継承させる
  • TextFieldのdelegateにselfを指定
  • textFieldShouldReturnを実装しtextField.resignFirstResponder()を呼ぶ

[swift]

class ViewController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var textField: UITextField!

/* 略 */

override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool{
textField.resignFirstResponder()
return true
}
}

[/swift]

TextField以外をタップされたらキーボードを閉じる

  • touchesBeganを実装しself.view.endEditing()を呼ぶ

[swift]

class ViewController: UIViewController {

@IBOutlet weak var textField: UITextField!

/* 略 */

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
}

[/swift]

[C++] std::stringにキャストされた場合に文字列を返すようにする

クラスのオブジェクトがstd::stringとしてキャストされた場合任意の文字列を返すようにする。
ロギングなどで複数の別々のクラスをstringとして扱って統計情報などを出力したりするのに使える。

[cpp]
#include <iostream>
#include <string>

class Coordinate {
private:
int _x;
int _y;
public:
Coordinate(int x, int y): _y(y), _x(x) {}
explicit operator std::string () const {
return "x=" + std::to_string(_x) + ", y=" + std::to_string(_y);
}
};

int main() {
Coordinate coord(300, 400);
std::cout << static_cast(coord) << std::endl;
}
[/cpp]

[C++] オブジェクトのアドレスを表示

オブジェクトのアドレスを表示するコードです。
一言で言えば stati_cast で void のポインタにキャストして stream に渡してるということです。

[cpp title=”ShowAddress.cpp”]
#include <iostream>
#include <vector>

void showAddress() {

std::vector<char> vec(100);

//vectorオブジェクトのアドレスを表示
std::cout << "vec = "
<< static_cast<void *>(&vec)
<< std::endl;

//vector内部で使われているバッファのアドレスを表示
std::cout << "data = " <<
static_cast<void *>(vec.data())
<< std::endl;

// new して確保したchar配列のアドレスを表示
char *chars = new char[100];
std::cout << "chars = " <<
static_cast<void *>(chars)
<< std::endl;
}

int main(){

showAddress();
}
[/cpp]

出力(Mac上のXcodeでビルドし実行)

$ ./ShowAddress
vec   = 0x7fff5fbff6e8
data  = 0x100400100
chars = 0x100400170

vector オブジェクトはスタック上に、vector内部で確保されるバッファとnewで確保したオブジェクトは別のメモリ領域に確保されている様子がみえます。