公開中のアプリ

[Swift/Xcode]初心者必見!iOSアプリ作成から公開まで徹底解説!#04 「ボタン自動生成編」

今回はボタンを365個生成して、スクロールビューに入れて表示させたいと思います!

動作環境は下記の通りです!
Xcode Version 12.5 Swift version 5.4

今回の実装をイメージしやすいように先に動画とコード全部載せておきます!

コードは下記です!
前回作ったスクロールビューのコードはもう必要ないので消すなりして大丈夫です!

import UIKit

class ViewController: UIViewController {

    //スクロールビュー
    let scrollView = UIScrollView()
    //ボタンを表示用のビュー
    let inView: UIView = UIView()
    //スタックビュー縦用
    var stackV: UIStackView = UIStackView()
    //ボタンの配列
    var buttonArray: [UIButton] = []
    //スタックビュー横を格納する為の配列
    var stkArray: [UIStackView] = []


    override func viewDidLoad() {
        super.viewDidLoad()

        //スクロールビューの表示
        view.addSubview(scrollView)
        //ボタン表示用ビューの表示
        scrollView.addSubview(inView)

        //スクロールビューの設定
        //AutosizingをAutoLayoutに変換しないようにしている(おまじない)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        //X軸
        scrollView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true
        //Y軸
        scrollView.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor).isActive = true
        //横幅
        scrollView.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, multiplier: 0.9).isActive = true
        //縦幅
        scrollView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.9).isActive = true


        //ボタン表示用ビューの設定
        //AutosizingをAutoLayoutに変換しないようにしている(おまじない)
        inView.translatesAutoresizingMaskIntoConstraints = false
        //横幅
        inView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 1.0).isActive = true
        //この4つの制約をつけて初めてスクロールするっぽい
        inView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
        inView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0.0).isActive = true
        inView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0.0).isActive = true
        inView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0.0).isActive = true



        //ボタンの生成と設定
        //ボタンにtagを付ける為の変数
        var tagNumber = 1
        //for文でボタンを生成
        for _ in 0...364 {
            //ボタン生成
            let button: UIButton = UIButton(type: .custom)
            //タイトルの色
            button.setTitleColor(UIColor.black, for: UIControl.State())
            //タイトルは数字なのでtagナンバーを指定
            button.setTitle(String(tagNumber), for: UIControl.State())
            //tag設定
            button.tag = tagNumber
            //背景色
            button.backgroundColor = UIColor.lightGray
            //ボーダーの横幅
            button.layer.borderWidth = 3
            //ボターの色
            button.layer.borderColor = UIColor.black.cgColor
            //AutosizingをAutoLayoutに変換しないようにしている(おまじない)
            button.translatesAutoresizingMaskIntoConstraints = false
            //ボタンの横幅が可変なのでボタンの高さは横幅と同じ長さを指定する事で正方形にしてる。
            button.heightAnchor.constraint(equalTo: button.widthAnchor, multiplier: 1.0).isActive = true
            //変数tagNumberに1追加
            tagNumber += 1
            //ボタンの配列に追加
            buttonArray.append(button)
        }



        //スタックビュー縦の設定
        //スタックビューの方向を縦に
        stackV.axis = .vertical
        //中のオブジェクトをどこに揃えて配置するか
        stackV.alignment = .fill
        //どう配置するか
        stackV.distribution = .fill
        //オブジェクト同士のスペース
        stackV.spacing = 4
        //おまじない
        stackV.translatesAutoresizingMaskIntoConstraints = false
        //stackVの背景色
        stackV.backgroundColor = UIColor.white
        //stackVを表示
        inView.addSubview(stackV)

        //X軸
        stackV.centerXAnchor.constraint(equalTo: inView.centerXAnchor).isActive = true
        //トップ位置
        stackV.topAnchor.constraint(equalTo: stackV.topAnchor, constant: 0.0).isActive = true
        //横幅
        stackV.widthAnchor.constraint(equalTo: inView.widthAnchor, multiplier: 1.0).isActive = true
        //縦幅
        stackV.heightAnchor.constraint(equalTo: inView.heightAnchor, multiplier: 1.0).isActive = true


        //スタックビュー横を自動生成
        for i in 0 ..< 73 {
            //StackHの生成
            let stackH:UIStackView = UIStackView()
            //スタックビューの方向を横に
            stackH.axis = .horizontal
            //オブジェクト同士のスペース
            stackH.spacing = 4
            //中のオブジェクトをどこに揃えて配置するか
            stackH.alignment = .fill
            //どう配置するか
            stackH.distribution = .fillEqually
            //スタックビュー配列に追加
            stkArray.append(stackH)
            //stackVの中にstackHを格納
            stackV.addArrangedSubview(stkArray[i])
        }


        //ボタンの配置
        //for文のカウンター変数
        var count = 0
        //for文で365個のボタンをどのstackHに入れるか決めている
        for i in 1 ..< 366 {
            //カウントの数に応じてどのstackHに入るか決定する。
            stkArray[count].addArrangedSubview(buttonArray[i - 1] as UIView)
            //ボタンは5列なので5の倍数でcountを一つ増やす。
            if i % 5 == 0 {
                count += 1
            }
        }
    }
}

今回の実装の流れはこんな感じです!
①スクロールビューの生成
②インビューの生成
③ボタンの生成
④スタックビュー縦の生成
⑤スタックビュー横の生成
⑥ボタンをスタックビュー横に配置する

スクロールビューの生成

前回の記事で説明しましたがまずはスクロールビューを生成します!
詳しい説明は省きますがスクロールビュー部分のコードだけまとめるとこちらです!

import UIKit

class ViewController: UIViewController {
    
    //スクロールビュー
    let scrollView = UIScrollView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //スクロールビューの表示
        view.addSubview(scrollView)
        
        //スクロールビューの設定
        //AutosizingをAutoLayoutに変換しないようにしている(おまじない)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        //X軸
        scrollView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true
        //Y軸
        scrollView.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor).isActive = true
        //横幅
        scrollView.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, multiplier: 0.9).isActive = true
        //縦幅
        scrollView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.9).isActive = true
    }
}

インビューの生成

今回、スクロールさせたいのは縦方向だけなのでインビューの横幅はスクロールビューと同じにして、縦幅の指定はしません。
縦幅の指定をしない理由は、ボタンの大きさがディスプレイサイズによって可変するので、ボタンの大きさによってインビューの縦幅も可変させないといけないからです。

import UIKit

class ViewController: UIViewController {
    
    //インビュー
    let inView: UIView = UIView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //インビューの表示
        scrollView.addSubview(inView)
        //インビューの設定
        //AutosizingをAutoLayoutに変換しないようにしている(おまじない)
        inView.translatesAutoresizingMaskIntoConstraints = false
        //横幅
        inView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 1.0).isActive = true
        //この4つの制約をつけて初めてスクロールするっぽい
        inView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
        inView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0.0).isActive = true
        inView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0.0).isActive = true
        inView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0.0).isActive = true
    }
}

ボタンの生成

次はボタンの生成部分について説明します!
ボタンは正方形で5列で表示したいと思ってます。
ボタンの横幅はディスプレイサイズに応じて可変させます。
今回は押した時の機能は実装せず、とりあえず表示させる事だけの実装です。
ではボタン部分のコードを見ていきましょう!

import UIKit

class ViewController: UIViewController {
    
    var buttonArray: [UIButton] = [] //ボタンの配列
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        var tagNumber = 1 //ボタンにtagを付ける為の変数
        
        //for文でボタンを生成
        for _ in 0...364 {
            let button: UIButton = UIButton(type: .custom)  //ボタン生成
            button.setTitleColor(UIColor.black, for: UIControl.State())  //タイトルの色
            button.setTitle(String(tagNumber), for: UIControl.State()) //タイトルは数字なのでtagナンバーを指定
            button.tag = tagNumber //tag設定
            button.backgroundColor = UIColor.lightGray //背景色
            button.layer.borderWidth = 3 //ボーダーの横幅
            button.layer.borderColor = UIColor.black.cgColor //ボターの色
            button.translatesAutoresizingMaskIntoConstraints = false //おまじない
            //ボタンの横幅が可変なのでボタンの高さは横幅と同じ長さを指定する事で正方形にしてる。
            button.heightAnchor.constraint(equalTo: button.widthAnchor, multiplier: 1.0).isActive = true
            tagNumber += 1 //変数tagNumberに1追加
            buttonArray.append(button) //ボタンの配列に追加
        }
    }
}

ボタンの自動生成方法ですが、ボタンを作りたい数だけfor文で回してあげます。
13行目からのfor文の中でボタンの設定をしています。
個々の設定はコードに書いてある通りです。

17行目のtagですが、ボタン一つ一つに番号を付けてあげる事でボタンの管理をしやすくしてます。
ボタンを生成する時は必ず付ける事になるかと思います!
24行目で変数tagNumberに1を足す事で1.2.3.4.5…..とtagナンバーを付けてます。

あとは、23行目ですかね!
ボタンの縦幅を横幅と同じになるように設定しています。
ボタンの横幅が可変なので、正方形にするにはこのように設定します。

スタックビュー縦の生成

ボタンを365個表示させる為に、スタックビューを使います。
スタックビューは使った事ありますか?
オートレイアウトする上で欠かせないものなので皆さん使った事があると思いますが、簡単に説明したいと思います。

図であるように青い背景がスタックビューの縦、黄色がスタックビューの横、白がボタンになってます。

スタックビュー縦は、スタックビュー横を縦に均等に並べてくれています。
スタックビュー横はボタンを横に均等に並べてくれています。

ではスタックビュー縦部分のコードを説明します。

import UIKit

class ViewController: UIViewController {
    
    //スタックビュー縦用
    var stackV: UIStackView = UIStackView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        stackV.axis = .vertical //スタックビューの方向を縦に
        stackV.alignment = .fill //中のオブジェクトをどこに揃えて配置するか
        stackV.distribution = .fill  //どう配置するか
        stackV.spacing = 4 //オブジェクト同士のスペース
        stackV.translatesAutoresizingMaskIntoConstraints = false //おまじない
        inView.addSubview(stackV) //stackVを表示
        stackV.centerXAnchor.constraint(equalTo: inView.centerXAnchor).isActive = true //X軸
        stackV.topAnchor.constraint(equalTo: stackV.topAnchor, constant: 0.0).isActive = true //トップ位置
        stackV.widthAnchor.constraint(equalTo: inView.widthAnchor, multiplier: 1.0).isActive = true //横幅
        stackV.heightAnchor.constraint(equalTo: inView.heightAnchor, multiplier: 1.0).isActive = true //縦幅
    }
}

特に説明するところがなさそうですね。
強いて言うならスタックビュー縦用の横幅と縦幅はインビューと同じにしましたよって事くらいでしょうか。

スタックビュー横

ボタン365個を5個ずつ並べるとスタックビュー横は73個必要になるので、
73個生成します。

コードは下記です。

import UIKit

class ViewController: UIViewController {
    
    //スタックビュー横を格納する為の配列
    var stkArray: [UIStackView] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //スタックビュー横を自動生成
        for i in 0 ..< 73 {
            let stackH:UIStackView = UIStackView() //StackHの生成
            stackH.axis = .horizontal //スタックビューの方向を横に
            stackH.spacing = 4 //オブジェクト同士のスペース
            stackH.alignment = .fill //中のオブジェクトをどこに揃えて配置するか
            stackH.distribution = .fillEqually //どう配置するか
            stkArray.append(stackH) //スタックビュー配列に追加
            stackV.addArrangedSubview(stkArray[i]) //stackVの中にstackHを格納
        }
    }
}

まずは6行目ですが、生成したスタックビュー横を格納する配列です。
あとは、ボタンを生成したようにfor文で回してあげます。
各種設定はコードに書いてある通りです。

補足するとしたら18行目でしょうか。
ここで生成したスタックビュー横をスタックビュー縦に格納してます。

ボタンをスタックビュー横に配置する

最後にスタックビュー横にボタンを5個ずつ配置すればOKです!
コードは下記です〜!

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //for文のカウンター変数
        var count = 0
        //for文で365個のボタンをどのstackHに入れるか決めている
        for i in 1 ..< 366 {
            //カウントの数に応じてどのstackHに入るか決定する。
            stkArray[count].addArrangedSubview(buttonArray[i - 1] as UIView)
            //ボタンは5列なので5の倍数でcountを一つ増やす。
            if i % 5 == 0 {
                count += 1
            }
        }
    }
}

ここはちょっと説明が難しいかもです。

なのでまずやりたい事を整理しましょう。
ボタンが365個あって、スタックビュー横に5個ずつ格納したいと思います。

for文を365回まわして1〜5まではスタックビュー横の1番目(配列stkArray[1])に格納、5〜10まではスタックビュー横の2番目(配列stkArray[2])に格納させるようにします。

13行目でボタンを一つずつスタックビュー横に格納してます。
for文をまわして5で割れる数がきたら、変数countに一つ数値をプラスして、配列stkArrayに格納する番号を変更していってます。

説明が下手で申し訳ないです(^^;
わからない点があればコメントいただければできる限りの回答をしたいと思ってます。

まとめ

なんとかボタンを配置する事ができました!
質問とか記事の内容に関する指摘がありましたらぜひコメント下さい!

次はボタンに機能を持たせたいですね!
その前にちょっとコードを綺麗にしたいと思います!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

アプリ