MENU

【swift アプリ開発】UITableViewをロジックで実装(「Interface Builder」、「Storyboard」を使わない)

  • URLをコピーしました!
ios-appdevelopment-0005-03

こんにちは!Popoです。

こんな方へのお勧めの記事です。

  • ロジックでUITableViewを実装してみたい。

表題に「「Interface Builder」、「Storyboard」を使わない」とあります!

詳細な位置やサイズの調整もできますし、アプリの「もっさり感」も少なくなります。

また、今回の記事を作成して気づいたのですが、以前作成したロジックをコピーして作成する事ができるので、生産性がかなり向上します。

メリットは大きいと感じています!

それでは、早速始めてみましょう!

目次

アプリ開発の動作環境

今回のアプリ開発環境です。

項目バージョン
XcodeVersion 14.3.1 (14E300c)
SwiftSwift version 5.8.1
MacOSmacOS 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セクション数を返すデータソースメソッド
numberOfRowsInSectionCellの総数を返すデータソースメソッド
heightForRowAtCellの高さを返すデータソースメソッド
cellForRowAt引数で与えられたindexPathに対するcellの設定
didSelectRowAtCellが選択された際に呼び出されるデリゲートメソッド

その他のメソッドについては下記サイトを参考にしてください。

「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は画面作成の基礎になりますので、是非マスターしてくださいね!

それではまた!

よかったらシェアしてね!
  • URLをコピーしました!
目次