こんにちは!Popoです。
こんな方へのお勧めの記事です。
- ロジックでUITableViewを実装してみたい。
表題に「「Interface Builder」、「Storyboard」を使わない」とあります!
詳細な位置やサイズの調整もできますし、アプリの「もっさり感」も少なくなります。
また、今回の記事を作成して気づいたのですが、以前作成したロジックをコピーして作成する事ができるので、生産性がかなり向上します。
メリットは大きいと感じています!
それでは、早速始めてみましょう!
アプリ開発の動作環境
今回のアプリ開発環境です。
項目 | バージョン |
Xcode | Version 14.3.1 (14E300c) |
Swift | Swift version 5.8.1 |
MacOS | macOS Ventura バージョン13.4(22F66) |
実装ロジックの全体
実装しましたロジックの全体になります。
import UIKit
class OneViewController: UIViewController {
let SCREEN_WIDTH = UIScreen.main.bounds.width
let SCREEN_HEIGHT = UIScreen.main.bounds.height
var navigationBarHeight: CGFloat = 44
var statusHieght:CGFloat = 0
var cellCounter:Int = 0
//UITableView
fileprivate var oneTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Popo"
let attrs: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.white,
.font: UIFont(name: "HiraginoSans-W6",size:17)!,
.baselineOffset:1
]
// iOS15以降の場合
let appearance = UINavigationBarAppearance()
appearance.backgroundColor = .brown
appearance.titleTextAttributes = attrs
self.navigationController?.navigationBar.scrollEdgeAppearance = appearance
//UITableView
self.oneTableView = UITableView()
self.oneTableView.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT)
// DataSourceの設定をする.
self.oneTableView.dataSource = self
// Delegateを設定する.
self.oneTableView.delegate = self
self.oneTableView.separatorInset = UIEdgeInsets.zero // 区切り線を左まで伸ばす
self.oneTableView.backgroundColor = RGBAlpa(238, 245, 243, 1)
self.oneTableView.separatorStyle = .singleLine
self.oneTableView.separatorColor = .gray
self.view.addSubview(self.oneTableView)
}
}
//MARK: UITableView
extension OneViewController: UITableViewDelegate, UITableViewDataSource
{
/*
セクション数を返すデータソースメソッド.
(実装必須)
*/
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
/*
Cellの総数を返すデータソースメソッド.
(実装必須)
*/
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 10
}
/*
Cellの高さを返すデータソースメソッド.
(実装必須)
*/
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 50
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let workCellName : String! = NSString(format:"MyCell%d_%d",self.cellCounter,self.cellCounter) as String
self.cellCounter = self.cellCounter + 1
self.oneTableView.register(UITableViewCell.self, forCellReuseIdentifier: workCellName)
let cell = self.oneTableView.dequeueReusableCell(withIdentifier: workCellName, for: indexPath)
//これでセルをタップ時、色は変化しなくなる
cell.selectionStyle = UITableViewCell.SelectionStyle.none
//二重に表示されるのを防ぐ
for subview in cell.contentView.subviews{
subview.removeFromSuperview()
}
let cellView:UIView = self.makeTableView(cellHeight: cell.frame.height, cellIndex: indexPath.row)
cell.contentView.addSubview(cellView)
return cell
}
/*
Cellが選択された際に呼び出されるデリゲートメソッド.
*/
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
self.oneTableView?.deselectRow(at: indexPath, animated: true)
}
}
//MARK:UITabeView View生成
extension OneViewController
{
//Header View 生成
fileprivate func makeTableView(cellHeight:CGFloat,cellIndex:Int) -> UIView
{
//header用View
let headerView:UIView = UIView()
headerView.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: cellHeight)
headerView.backgroundColor = RGBAlpa(238, 245, 243, 1)
//detailLabel
let detailLabel = UILabel()
detailLabel.frame = CGRect(x:20, y: 10 , width: headerView.frame.width - 40, height: headerView.frame.height - 20)
detailLabel.font = UIFont(name: "HiraginoSans-W6",size:15)
detailLabel.text = "Heelo World!"
detailLabel.textColor = RGBAlpa(0,51,40, 1)
detailLabel.textAlignment = .center
detailLabel.adjustsFontSizeToFitWidth = true
detailLabel.minimumScaleFactor = 0.3
switch cellIndex {
case 0:
detailLabel.backgroundColor = .red
case 1:
detailLabel.backgroundColor = .blue
case 2:
detailLabel.backgroundColor = .yellow
case 3:
detailLabel.backgroundColor = .cyan
case 4:
detailLabel.backgroundColor = .gray
case 5:
detailLabel.backgroundColor = .red
case 6:
detailLabel.backgroundColor = .blue
case 7:
detailLabel.backgroundColor = .yellow
case 8:
detailLabel.backgroundColor = .cyan
case 9:
detailLabel.backgroundColor = .gray
default:
detailLabel.backgroundColor = .purple
}
headerView.addSubview(detailLabel)
return headerView
}
}
各メソッドの解説
それでは、ロジックの詳細を解説していきたいと思います。
UITableViewDataSourceとUITableViewDelegateプロトコルの定義
UITableViewのdelegateを定義します。
//MARK: UITableView
extension OneViewController: UITableViewDelegate, UITableViewDataSource
{
中省略
}
UITableViewの定義
UITableViewの定義ロジックです。
//UITableView
fileprivate var oneTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
〜
中省略
〜
//UITableView
self.oneTableView = UITableView()
self.oneTableView.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT)
// DataSourceの設定をする.
self.oneTableView.dataSource = self
// Delegateを設定する.
self.oneTableView.delegate = self
self.oneTableView.separatorInset = UIEdgeInsets.zero // 区切り線を左まで伸ばす
self.oneTableView.backgroundColor = RGBAlpa(238, 245, 243, 1)
self.oneTableView.separatorStyle = .singleLine
self.oneTableView.separatorColor = .gray
self.view.addSubview(self.oneTableView)
}
self.oneTableView.separatorInset = UIEdgeInsets.zero // 区切り線を左まで伸ばす
この設定は、境界線の左端を空けるか空けないかの設定です。
設定あり
設定なし
下記は、境界線の線種と線色の指定になります。
self.oneTableView.separatorStyle = .singleLine
self.oneTableView.separatorColor = .gray
UITableViewDataSourceメソッド
delegateメソッド箇所について解説します。
//MARK: UITableView
extension OneViewController: UITableViewDelegate, UITableViewDataSource
{
/*
セクション数を返すデータソースメソッド.
(実装必須)
*/
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
/*
Cellの総数を返すデータソースメソッド.
(実装必須)
*/
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 10
}
/*
Cellの高さを返すデータソースメソッド.
(実装必須)
*/
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 50
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let workCellName : String! = NSString(format:"MyCell%d_%d",self.cellCounter,self.cellCounter) as String
self.cellCounter = self.cellCounter + 1
self.oneTableView.register(UITableViewCell.self, forCellReuseIdentifier: workCellName)
let cell = self.oneTableView.dequeueReusableCell(withIdentifier: workCellName, for: indexPath)
//これでセルをタップ時、色は変化しなくなる
cell.selectionStyle = UITableViewCell.SelectionStyle.none
//二重に表示されるのを防ぐ
for subview in cell.contentView.subviews{
subview.removeFromSuperview()
}
let cellView:UIView = self.makeTableView(cellHeight: cell.frame.height, cellIndex: indexPath.row)
cell.contentView.addSubview(cellView)
return cell
}
/*
Cellが選択された際に呼び出されるデリゲートメソッド.
*/
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
self.oneTableView?.deselectRow(at: indexPath, animated: true)
}
}
今回利用したメソッドをまとめてみました。
メソッド | メソッドの機能 | 実装必須 |
numberOfSections | セクション数を返すデータソースメソッド | ○ |
numberOfRowsInSection | Cellの総数を返すデータソースメソッド | ○ |
heightForRowAt | Cellの高さを返すデータソースメソッド | ○ |
cellForRowAt | 引数で与えられたindexPathに対するcellの設定 | |
didSelectRowAt | Cellが選択された際に呼び出されるデリゲートメソッド |
「cellForRowAt」内を解説します。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let workCellName : String! = NSString(format:"MyCell%d_%d",self.cellCounter,self.cellCounter) as String
self.cellCounter = self.cellCounter + 1
self.oneTableView.register(UITableViewCell.self, forCellReuseIdentifier: workCellName)
let cell = self.oneTableView.dequeueReusableCell(withIdentifier: workCellName, for: indexPath)
//これでセルをタップ時、色は変化しなくなる
cell.selectionStyle = UITableViewCell.SelectionStyle.none
//二重に表示されるのを防ぐ
for subview in cell.contentView.subviews{
subview.removeFromSuperview()
}
let cellView:UIView = self.makeTableView(cellHeight: cell.frame.height, cellIndex: indexPath.row)
cell.contentView.addSubview(cellView)
return cell
}
}
- 「cellCounter」というカウンタを利用してセル名をユニークに制御しています。
- セル再利用のため、セル上のUIを全て削除するようにしています。
- 「makeTableView」メソッドからUIViewを受け取り、セルに貼り付けています。
UITableViewcellのパーツ生成
「cellForRowAt」メソッドでセルに表示するUIを生成しています。
今回は同じclass内のメソッドで処理していますが、UITableViewcellの別classにして処理する方法もあります。
//MARK:UITabeView View生成
extension OneViewController
{
//Header View 生成
fileprivate func makeTableView(cellHeight:CGFloat,cellIndex:Int) -> UIView
{
//header用View
let headerView:UIView = UIView()
headerView.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: cellHeight)
headerView.backgroundColor = RGBAlpa(238, 245, 243, 1)
//detailLabel
let detailLabel = UILabel()
detailLabel.frame = CGRect(x:20, y: 10 , width: headerView.frame.width - 40, height: headerView.frame.height - 20)
detailLabel.font = UIFont(name: "HiraginoSans-W6",size:15)
detailLabel.text = "Heelo World!"
detailLabel.textColor = RGBAlpa(0,51,40, 1)
detailLabel.textAlignment = .center
detailLabel.adjustsFontSizeToFitWidth = true
detailLabel.minimumScaleFactor = 0.3
switch cellIndex {
case 0:
detailLabel.backgroundColor = .red
case 1:
detailLabel.backgroundColor = .blue
case 2:
detailLabel.backgroundColor = .yellow
case 3:
detailLabel.backgroundColor = .cyan
case 4:
detailLabel.backgroundColor = .gray
case 5:
detailLabel.backgroundColor = .red
case 6:
detailLabel.backgroundColor = .blue
case 7:
detailLabel.backgroundColor = .yellow
case 8:
detailLabel.backgroundColor = .cyan
case 9:
detailLabel.backgroundColor = .gray
default:
detailLabel.backgroundColor = .purple
}
headerView.addSubview(detailLabel)
return headerView
}
}
「headerView」というセルと同じサイズのUIViewに「detailLabel」というUILabelは貼り付けて、UIViewで返却しています。
まとめ
UITable Viewをロジックで簡単に実装できる例を上げてみました。
はやり、ロジックで開発をする最大のメリットは、以前のロジックを再利用できることで生産性を飛躍的に向上する事ができる事だと思います。
これを利用して、下記のような機能を追加する事も可能です。
- ヘッダーのついたUITableViewを作成
- セルのUIを増やす
- セルの登録、削除機能を追加
色々試していただきたいと思います。
UITableViewは画面作成の基礎になりますので、是非マスターしてくださいね!
それではまた!