MENU

【swift アプリ開発】UITableView応用編 3階層アコーディオン画面を作成してみよう!

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

こんにちは!Popoです。

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

  • UITableViewでアコーディオンのように開閉する画面を作成してみたい
  • 3階層以上のアコーディオン画面を作成してみたい
  • UITableViewの応用機能を学習したい

前回記事で、UITableView応用編という事でサイドメニューを作成してみました。

今回もさらに応用編として、3階層のアコーディオン画面を作成してみたいと思います。

アコーディオン画面については他の方も色々記事にされていました。

3階層以上のアコーディオン画面を紹介されている方がほとんどいなかったので記事にしてみました。

目次

アプリの動作環境

アプリの動作環境

今回のアプリ動作環境です。

項目バージョン
XcodeVersion 14.3.1 (14E300c)
SwiftSwift version 5.8.1
MacOSmacOS Ventura バージョン13.4(22F66)

アプリの設計

アプリの設計

はじめにアプリ開発の方針について説明をしてみたいと思います。

アコーディオンの考え方

考え方
  • ①:1階層目はUITableViewのヘッダーを利用します。
  • ②:ヘッダーを選択すると、2階層目が表示されます。これはセルが表示される事になります。
  • ③:さらにセルを選択する事で、3階層目を表示させます。
  • 3階層目以降のアコーディオンはセルの高さを変更して、あたかもセル数が増えているように見せていきます。

表示用配列の考え方

構造体を利用して配列を作成します。

  • 「oneArray」という配列に「oneHeader」という構造体を格納します。
  • 「oneHeader」構造体の配下に「cellDetaile」という配列の構造体を設定します。

実際の配列の中身です。1配列目だけ詳細を表示しています。

「headerName」「cellName」「subName」の名称で3階層を構成しています。

アプリのソースコード全体

アプリのソースコード全体

では、ソースコードを見ていきましょう!!

UIViewController (OneViewController)

主な機能

  • ナビゲーションバーカスタマイズ
  • UITableView生成
  • 表示用配列作成
  • UITableViewDataSourceメソッド
  • UITableViewヘッダー選択時の制御

import UIKit

class OneViewController: UIViewController {

    //header構造体
    struct oneHeader {
        var isShown: Bool
        var headerName: String
        var detaileArray:[cellDetaile]
    }
    //cell構造体
    struct cellDetaile {
        var isShown: Bool
        var cellName: String
        var subName: String
    }
    //表示用配列
    fileprivate var oneArray:[oneHeader] = []
    
    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.init(frame: CGRect.zero, style: .grouped)
        let dummyView:UIView = UIView()
        dummyView.frame = CGRect(x: 0, y: 0, width: 1, height: 1)
        self.oneTableView.tableFooterView = dummyView
        self.oneTableView.tableHeaderView = dummyView
        self.oneTableView.sectionFooterHeight = 0.0
        //境界線を全て消す
        self.oneTableView.separatorStyle = .none
        //背景色
        self.oneTableView.backgroundColor = RGBAlpa(238, 245, 243, 1)
        
        self.oneTableView.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT)
        // DataSourceの設定をする.
        self.oneTableView.dataSource = self
        // Delegateを設定する.
        self.oneTableView.delegate = self
        UITableView.appearance().separatorInset = UIEdgeInsets.zero
        UITableViewCell.appearance().separatorInset = UIEdgeInsets.zero
        
        self.view.addSubview(self.oneTableView)
        //表示用配列生成
        for index1 in 0 ..< 10
        {
            let index1String:String = String(index1)
            let saveHeaderName:String = "headerName " + index1String
            let saveCellName:String = "cellName " + index1String
            var saveDetaileArray:[cellDetaile] = []
            
            for index2 in 0 ..< 5
            {
                let index2String:String = String(index2)
                let saveSubName:String = "subName " + index2String
                //let saveOneArray:oneHeader = oneHeader(isShown: false, headerName: saveHeaderName, detaileArray: [cellDetaile(isShown: false, cellName: saveCellName,subName: saveSubName)])
                saveDetaileArray.append(cellDetaile(isShown: false, cellName: saveCellName,subName: saveSubName))
            }
            let saveOneArray:oneHeader = oneHeader(isShown: false, headerName: saveHeaderName, detaileArray: saveDetaileArray)
            self.oneArray.append(saveOneArray)
        }
    }


}
//MARK: UITableView
extension OneViewController:  UITableViewDelegate, UITableViewDataSource
{
    /*
     セクション数を返すデータソースメソッド.
     (実装必須)
     */
    func numberOfSections(in tableView: UITableView) -> Int
    {
        return self.oneArray.count
    }
    /*
     Cellの総数を返すデータソースメソッド.
     (実装必須)
     */
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        
        if self.oneArray[section].isShown
        {
            return self.oneArray[section].detaileArray.count
        } else {
            return 0
        }
        
    }
    /*
     ヘッダーの高さを返すデータソースメソッド.
     */
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
    {
         return 30
    }
    /*
     ヘッダーのViewを返すデータソースメソッド.
    */
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
    {
        //header用View
        let headerView:UIView = UIView()
        headerView.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 30)
        headerView.backgroundColor =  .white

        //headerLabel
        let headerLabel:UILabel = UILabel()
        headerLabel.frame = CGRect(x: 35, y: 0, width: SCREEN_WIDTH - 80, height:30)
        headerLabel.textAlignment = .left//整列
        headerLabel.font = UIFont(name: "HiraKakuProN-W6",size:17)!//文字サイズ
        headerLabel.textColor = RGBAlpa(138,138,138,1)//文字色
        headerLabel.text = self.oneArray[section].headerName
        headerLabel.numberOfLines = 0
        headerLabel.backgroundColor =  .white
        
        headerView.addSubview(headerLabel)
        
        let gesture = UITapGestureRecognizer(target: self,action: #selector(headertapped(sender:)))
        headerView.addGestureRecognizer(gesture)
        headerView.tag = section
        
        return headerView
    }
    /*
     フッターのViewを返すデータソースメソッド.
    */
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
    {
        return CGFloat.leastNormalMagnitude
    }
    /*
     Cellの高さを返すデータソースメソッド.
     (実装必須)
     */
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
    {
        let saveIsShow:Bool = self.oneArray[indexPath.section].detaileArray[indexPath.row].isShown
        
        if saveIsShow
        {
            return 60
        } else {
            return 30
        }
    }
    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 saveSection:Int = indexPath.section
        let saveIndex:Int = indexPath.row
        
        if self.oneArray[saveSection].detaileArray[saveIndex].isShown
        {
            //header用View
            let cellView:UIView = UIView()
            cellView.frame = CGRect(x: 70, y: 0, width: SCREEN_WIDTH - 70, height: 60)
            cellView.backgroundColor =  .white
            //cellLabel
            let cellLabel:UILabel = UILabel()
            cellLabel.frame = CGRect(x: 0, y: 0, width: cellView.frame.width, height:30)
            cellLabel.textAlignment = .left//整列
            cellLabel.font = UIFont(name: "HiraKakuProN-W6",size:17)!//文字サイズ
            cellLabel.textColor = RGBAlpa(138,138,138,1)//文字色
            cellLabel.text = self.oneArray[saveSection].detaileArray[saveIndex].cellName
            cellLabel.numberOfLines = 0
            cellLabel.backgroundColor =  .white
            cellView.addSubview(cellLabel)
            
            //subLabel
            let subLabel:UILabel = UILabel()
            subLabel.frame = CGRect(x: 35, y: cellLabel.frame.height, width: cellView.frame.width - 35, height:30)
            subLabel.textAlignment = .left//整列
            subLabel.font = UIFont(name: "HiraKakuProN-W6",size:17)!//文字サイズ
            subLabel.textColor = RGBAlpa(138,138,138,1)//文字色
            subLabel.text = self.oneArray[saveSection].detaileArray[saveIndex].subName
            subLabel.numberOfLines = 0
            subLabel.backgroundColor =  .white
            cellView.addSubview(subLabel)
            
            cell.contentView.addSubview(cellView)
        } else {
            //header用View
            let cellView:UIView = UIView()
            if self.oneArray[saveSection].isShown
            {
                cellView.frame = CGRect(x: 70, y: 0, width: SCREEN_WIDTH - 70, height: 30)
                cellView.backgroundColor =  .white
                //cellLabel
                let cellLabel:UILabel = UILabel()
                cellLabel.frame = CGRect(x: 0, y: 0, width: cellView.frame.width, height:30)
                cellLabel.textAlignment = .left//整列
                cellLabel.font = UIFont(name: "HiraKakuProN-W6",size:17)!//文字サイズ
                cellLabel.textColor = RGBAlpa(138,138,138,1)//文字色
                cellLabel.text = self.oneArray[saveSection].detaileArray[saveIndex].cellName
                cellLabel.numberOfLines = 0
                cellLabel.backgroundColor =  .white
                cellView.addSubview(cellLabel)
            } else {
                cellView.frame = CGRect(x: 70, y: 0, width: SCREEN_WIDTH - 70, height: 0)
            }
            cell.contentView.addSubview(cellView)
        }

        
        return cell
    }
    /*
     Cellが選択された際に呼び出されるデリゲートメソッド.
     */
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        self.oneTableView?.deselectRow(at: indexPath, animated: true)
        let saveSection:Int = indexPath.section
        let saveIndex:Int = indexPath.row
        //Boolを反転
        self.oneArray[saveSection].detaileArray[saveIndex].isShown.toggle()
        //選択セルだけ更新
        self.oneTableView.beginUpdates()
        self.oneTableView.reloadSections([saveSection], with: .automatic)
        self.oneTableView.endUpdates()
    }
}
//MARK: UITableView Header タップ
extension OneViewController
{
    //Header Click
    @objc func headertapped(sender: UITapGestureRecognizer) {
        guard let section = sender.view?.tag else {
            return
        }
        //Boolを反転
        self.oneArray[section].isShown.toggle()
        
        self.oneTableView.beginUpdates()
        self.oneTableView.reloadSections([section], with: .automatic)
        self.oneTableView.endUpdates()
    }
}

画面動作

アコーディオンの動作はこんな感じになります。

各ロジック解説

各ロジック解説

各処理の解説を行なっていきます。

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生成

UITableViewも今までの通りですが、スタイルを「.grouped」の指定にしています。

        //UITableView

        self.oneTableView = UITableView.init(frame: CGRect.zero, style: .grouped)

        let dummyView:UIView = UIView()

        dummyView.frame = CGRect(x: 0, y: 0, width: 1, height: 1)

        self.oneTableView.tableFooterView = dummyView

        self.oneTableView.tableHeaderView = dummyView

        self.oneTableView.sectionFooterHeight = 0.0

        //境界線を全て消す

        self.oneTableView.separatorStyle = .none

        //背景色

        self.oneTableView.backgroundColor = RGBAlpa(238, 245, 243, 1)

        self.oneTableView.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT)

        // DataSourceの設定をする.

        self.oneTableView.dataSource = self

        // Delegateを設定する.

        self.oneTableView.delegate = self

        UITableView.appearance().separatorInset = UIEdgeInsets.zero

        UITableViewCell.appearance().separatorInset = UIEdgeInsets.zero

        self.view.addSubview(self.oneTableView)

  • 表示用配列作成

「isShown」という項目で、各階層の表示・非表示を制御します。

  • 「isShown」がtrueで表示
  • 「isShown」がfalse非表示

    //header構造体

    struct oneHeader {

        var isShown: Bool

        var headerName: String

        var detaileArray:[cellDetaile]

    }

    //cell構造体

    struct cellDetaile {

        var isShown: Bool

        var cellName: String

        var subName: String

    }

ループのカウンタを利用して「headerName」「cellName」「subName」名称を設定します。

        //表示用配列生成

        for index1 in 0 ..< 10

        {

            let index1String:String = String(index1)

            let saveHeaderName:String = “headerName ” + index1String

            let saveCellName:String = “cellName ” + index1String

            var saveDetaileArray:[cellDetaile] = []

            for index2 in 0 ..< 5

            {

                let index2String:String = String(index2)

                let saveSubName:String = “subName ” + index2String

                //let saveOneArray:oneHeader = oneHeader(isShown: false, headerName: saveHeaderName, detaileArray: [cellDetaile(isShown: false, cellName: saveCellName,subName: saveSubName)])

                saveDetaileArray.append(cellDetaile(isShown: false, cellName: saveCellName,subName: saveSubName))

            }

            let saveOneArray:oneHeader = oneHeader(isShown: false, headerName: saveHeaderName, detaileArray: saveDetaileArray)

            self.oneArray.append(saveOneArray)

        }

UITableViewDataSourceメソッド

UITableViewDataSourceメソッドについては下記記事を参考にしてください。

  • セクション数、セル数
  • func numberOfSections(in tableView: UITableView) -> Int」
    セクション数はoneArrayの配列数になります。
  • func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int」
    セル選択時の開閉をisShownで制御しています。
    oneArrayのdetaileArray配列数がセル数になり、「isShown」がfalse非表示時は、高さをZEROで返却してやります。

   /*

     セクション数を返すデータソースメソッド.

     (実装必須)

     */

    func numberOfSections(in tableView: UITableView) -> Int

    {

        return self.oneArray.count

    }

    /*

     Cellの総数を返すデータソースメソッド.

     (実装必須)

     */

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int

    {

        if self.oneArray[section].isShown

        {

            return self.oneArray[section].detaileArray.count

        } else {

            return 0

        }

    }

  • ヘッダーの高さ、ヘッダーView返却
  • func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat」
    ヘッダーの高さは「30」にしています。(お好きな高さに)
  • 「func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?」
    oneArrayの「headerName」をUILabelに設定し、UIViewに貼り付けて返却しています。

    /*

     ヘッダーの高さを返すデータソースメソッド.

     */

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat

    {

         return 30

    }

    /*

     ヘッダーのViewを返すデータソースメソッド.

    */

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?

    {

        //header用View

        let headerView:UIView = UIView()

        headerView.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 30)

        headerView.backgroundColor =  .white

        //headerLabel

        let headerLabel:UILabel = UILabel()

        headerLabel.frame = CGRect(x: 35, y: 0, width: SCREEN_WIDTH – 80, height:30)

        headerLabel.textAlignment = .left//整列

        headerLabel.font = UIFont(name: “HiraKakuProN-W6”,size:17)!//文字サイズ

        headerLabel.textColor = RGBAlpa(138,138,138,1)//文字色

        headerLabel.text = self.oneArray[section].headerName

        headerLabel.numberOfLines = 0

        headerLabel.backgroundColor =  .white

        headerView.addSubview(headerLabel)

        let gesture = UITapGestureRecognizer(target: self,action: #selector(headertapped(sender:)))

        headerView.addGestureRecognizer(gesture)

        headerView.tag = section

        return headerView

    }

  • フッターView返却

フッターは表示させたくないので、「CGFloat.leastNormalMagnitude」で指定してやります。

    /*

     フッターのViewを返すデータソースメソッド.

    */

    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat

    {

        return CGFloat.leastNormalMagnitude

    }

下記を参考に!

  • セルの高さ

detaileArrayのisShowmで高さを変更しています。

  • 開いた場合は「60」
  • 閉じた場合は「30」

セルの高さで3階層目の表示制御を行います。

    /*

     Cellの高さを返すデータソースメソッド.

     (実装必須)

     */

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat

    {

        let saveIsShow:Bool = self.oneArray[indexPath.section].detaileArray[indexPath.row].isShown

        if saveIsShow

        {

            return 60

        } else {

            return 30

        }

    }

  • セル内容表示

   今回は、メソッド内で直接UIView、UILabelを生成しました。

下記の制御を行なっています。

if self.oneArray[saveSection].detaileArray[saveIndex].isShown

  {

2階層目、3階層目とも表示。

} else {

            if self.oneArray[saveSection].isShown

            {

    3階層目は閉じて、2階層目は表示。

} else {

    2階層目、3階層目とも閉じる

}

}

    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 saveSection:Int = indexPath.section

        let saveIndex:Int = indexPath.row

        if self.oneArray[saveSection].detaileArray[saveIndex].isShown

        {

            //header用View

            let cellView:UIView = UIView()

            cellView.frame = CGRect(x: 70, y: 0, width: SCREEN_WIDTH – 70, height: 60)

            cellView.backgroundColor =  .white

            //cellLabel

            let cellLabel:UILabel = UILabel()

            cellLabel.frame = CGRect(x: 0, y: 0, width: cellView.frame.width, height:30)

            cellLabel.textAlignment = .left//整列

            cellLabel.font = UIFont(name: “HiraKakuProN-W6”,size:17)!//文字サイズ

            cellLabel.textColor = RGBAlpa(138,138,138,1)//文字色

            cellLabel.text = self.oneArray[saveSection].detaileArray[saveIndex].cellName

            cellLabel.numberOfLines = 0

            cellLabel.backgroundColor =  .white

            cellView.addSubview(cellLabel)

            //subLabel

            let subLabel:UILabel = UILabel()

            subLabel.frame = CGRect(x: 35, y: cellLabel.frame.height, width: cellView.frame.width – 35, height:30)

            subLabel.textAlignment = .left//整列

            subLabel.font = UIFont(name: “HiraKakuProN-W6”,size:17)!//文字サイズ

            subLabel.textColor = RGBAlpa(138,138,138,1)//文字色

            subLabel.text = self.oneArray[saveSection].detaileArray[saveIndex].subName

            subLabel.numberOfLines = 0

            subLabel.backgroundColor =  .white

            cellView.addSubview(subLabel)

            cell.contentView.addSubview(cellView)

        } else {

            //header用View

            let cellView:UIView = UIView()

            if self.oneArray[saveSection].isShown

            {

                cellView.frame = CGRect(x: 70, y: 0, width: SCREEN_WIDTH – 70, height: 30)

                cellView.backgroundColor =  .white

                //cellLabel

                let cellLabel:UILabel = UILabel()

                cellLabel.frame = CGRect(x: 0, y: 0, width: cellView.frame.width, height:30)

                cellLabel.textAlignment = .left//整列

                cellLabel.font = UIFont(name: “HiraKakuProN-W6”,size:17)!//文字サイズ

                cellLabel.textColor = RGBAlpa(138,138,138,1)//文字色

                cellLabel.text = self.oneArray[saveSection].detaileArray[saveIndex].cellName

                cellLabel.numberOfLines = 0

                cellLabel.backgroundColor =  .white

                cellView.addSubview(cellLabel)

            } else {

                cellView.frame = CGRect(x: 70, y: 0, width: SCREEN_WIDTH – 70, height: 0)

            }

            cell.contentView.addSubview(cellView)

        }

        return cell

    }

  • セル選択
  • detaileArrayのisShownを反転させます。
    (3階層目の開閉を制御)
  • 選択したセクションを更新します。

    /*

     Cellが選択された際に呼び出されるデリゲートメソッド.

     */

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)

    {

        self.oneTableView?.deselectRow(at: indexPath, animated: true)

        let saveSection:Int = indexPath.section

        let saveIndex:Int = indexPath.row

        //Boolを反転

        self.oneArray[saveSection].detaileArray[saveIndex].isShown.toggle()

        //選択セルだけ更新

        self.oneTableView.beginUpdates()

        self.oneTableView.reloadSections([saveSection], with: .automatic)

        self.oneTableView.endUpdates()

    }

HeaderViewタップイベント

  • oneArrayのisShownを反転させます。
    (2階層目の開閉を制御)
  • 選択したセクションを更新します。

    //Header Click

    @objc func headertapped(sender: UITapGestureRecognizer) {

        guard let section = sender.view?.tag else {

            return

        }

        //Boolを反転

        self.oneArray[section].isShown.toggle()

        self.oneTableView.beginUpdates()

        self.oneTableView.reloadSections([section], with: .automatic)

        self.oneTableView.endUpdates()

    }

まとめ

まとめ

UITableViewの応用編として、3階層のアコーディオン画面を作成してみました。

4階層以上を作成したい場合は、下記のロジックを追加する必要があると思います。

  • 「subName」以降を配列にする
  • セルの高さの制御if文を追加する
  • セルの内容表示の制御if文を追加する。
  • セル選択時の制御を追加する。

また、お気づきかもしれませんが今回のロジックでは、ヘッダーを押下すればセクション配下の開閉が制御できます。

UITableViewを利用して色々な画面が作成できます。

こんな画面を作ってみたいと思っている画面があれば、一度チャレンジしてみてはいかがでしょうか。

それではまた!

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