見出し画像

【じっくりSw1ftUI54】実践編24〜第36章 LazyVGrid と LazyHGrid を使用した SwiftUI グリッドの構築

さてと、前回

で、

階層リストの実践例

まではやったので、前回の予告でも書いた

もっともアプリらしいレイアウトなんじゃないかと個人的に思ってる
👉GridLayout

についてやってこう🕺

毎度、オイラの学びなんざ要らねって人は、

に全部載ってそうだからそっちでやればいんじゃね藁😝
では、早速〜〜〜


じっくり第36章を読んでく👓

概要としては、

Gridってのは、要は、複数列のテーブル(表形式)のレイアウト

で、それをこれまでにやった章の

FrameとかStackなんかを使わずにやる

みたいなことを書いてんね。

構文としては、

LazyVGrid(columns: [GridItem], alignment: <horizontalalignment>, spacing: CGFloat?, pinnedViews: <views>) { 
  // Content Views 
}
LazyHGrid(rows: [GridItem], alignment: <vertical alignment>, spacing: CGFloat?, pinnedViews: <views>) { 
  // Content Views 
}

らしい藁😝

ここでポイント①:GridItemちゃあ何なん👀❓

定義の仕方=構文

GridItem(sizing, spacing: CGFloat?, alignment: <alignment>)

てな感じらしく、解説としては

サイズ指定引数は GridItemSize 型であり、次のいずれかとして宣言する必要あり。

  • Flexible() – グリッド内の行または列の数は、LazyVGrid または LazyHGrid ビューに渡される配列内の GridItem インスタンスの数によって決まる。

  • adaptive(minimum: CGFloat) – 行または列のサイズは、使用可能なスペースにできるだけ多くの項目が収まるように調整される。項目を縮小できる最小サイズは、オプションの minimum 引数を使用して指定できる。

  • fixed(size: CGFloat ) – アイテムの固定サイズを指定する。

なんだとさ👀💦

でまあ、実際に作った方が早いんじゃね?って感じなんで〜〜〜

import SwiftUI

struct Essentials36ContentsView: View {
    var body: some View {
        E36GridStandardView()
    }
}

struct E36GridStandardView: View {
    private var fruitsColors: [Color] = [
        .red,
        .green,
        .orange,
        .purple,
        .pink
    ]
    private var fruitsGridItems = [
        GridItem(.fixed(44)),
        GridItem(.fixed(44)),
        GridItem(.fixed(44)),
        GridItem(.fixed(44)),
        GridItem(.fixed(44)),
    ]
    var body: some View {
        ScrollView{
            LazyVGrid(columns: fruitsGridItems, spacing: 10){
                ForEach(1...50, id: \.self){ fruitsIndex in
                    E36CellContentsView(fruitsIndex: fruitsIndex, fruitsColor: fruitsColors[fruitsIndex % fruitsColors.count])
                }
            }
            .padding()
        }
    }
}

struct E36CellContentsView: View {
    var fruitsIndex: Int
    var fruitsColor: Color
    
    var body: some View {
        Text("\(fruitsIndex)")
            .frame(minWidth: 44, maxWidth: .infinity,minHeight: 44)
            .background(fruitsColor)
            .presentationCornerRadius(44)
            .font(.largeTitle)
    }
}

#Preview {
    Essentials36ContentsView()
}

まずはてな感じで

可愛い😍🤤

さらに〜〜〜

    private var fruitsGridItems = [
        GridItem(.fixed(44)),
        GridItem(.fixed(44)),
        GridItem(.fixed(44)),
        GridItem(.fixed(44)),
        GridItem(.fixed(44)),
        GridItem(.fixed(100)),
    ]

てな感じで遊びで増やしてあげるだけで〜〜〜

こんな感じで変更もできるし🕺

ここでポイント②:アプリらしいアプリってオイラが言ってんのが、、、

ちょうど今、UiPathの連載記事

の方で、

無味乾燥なデザインの表なんざ、WEBとかアプリに馴染まない
👉別にみんな業務でそんな面白くない画面を触る人ばかりじゃない

みたいな話をしてんだけど、前者と後者、どちらの方が

  • 読みやすいか

  • 可愛いか

  • 人が見ようとするか

  • 触った人の記憶に残る=また使おうと思うか

って視点で考えるのが、アプリ開発の

UI/UXデザイン

なんだよねえ🧐

ま、そんなこと関係ないって人にはどうでも良いことだけど藁😛
こーゆー微妙な違いを動かしながら知っておくことが
後々、何かの要求の時に活きてくる🧐

さてと、本題に戻って

これを一気に5000にしちゃっても、ScrollViewを入れてるので〜〜〜

    var body: some View {
        ScrollView{
            LazyVGrid(columns: fruitsGridItems, spacing: 10){
                ForEach(1...5000, id: \.self){ fruitsIndex in
                    E36CellContentsView(fruitsIndex: fruitsIndex, fruitsColor: fruitsColors[fruitsIndex % fruitsColors.count])
                }
            }
            .padding()
        }
    }
}
てな感じで実現できちゃうんだよねえ〜〜〜
てか、5000の前の6の倍数って4998なんだね😛

ちょいとアダプティブ(動的)にしてみよう

    private var fruitsGridItems = [
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
    ]

てな感じで変更して〜〜〜

おもろ〜〜〜👀💦

横に向けても〜〜〜

ますます可愛い🕺

ここでポイント③:ちなみに、別に項目ごとのGridItemは必要なくて、、、

    private var fruitsGridItems = [
        GridItem(.fixed(400)),
//        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
//        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
//        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
//        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
//        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
    ]

みたいな感じでコメントアウトしても〜〜〜

てな感じで表示されるし、
    private var fruitsGridItems = [
        GridItem(.fixed(44)),
//        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
//        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
//        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
//        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
//        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
    ]

でやっても、

てな感じ

ここでポイント④:LazyVGridの場合、GridItemの配列の要素数が、

横に並ぶ数

ってのがここまで来るとわかると思う。
じゃ、コメントアウトを元に戻して〜〜〜

    private var fruitsGridItems = [
        GridItem(),
        GridItem(),
        GridItem(),
        GridItem(),
        GridItem(),
        GridItem(),
        GridItem(),
    ]

てな感じで中身を空欄にして、7つの要素にしてみると、、、

てな感じで自動で調整してんね藁😝
    private var fruitsGridItems = [
        GridItem(.fixed(120)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
    ]

てな感じにしてみると、、、

てな感じになってんね👀

ここでポイント⑤:なんで44って数字を使ってるの?

オイラのこれまでのコードが44って数字を使ってるんだけど、なぜかと言えば実は、

タップのターゲット:指でのタップが正確に行えるよう、コントロール要素は44 x 44ポイント以上の大きさで作成してください。

って、

  • デザインの規約の方でAppleが推奨してる

  • 👉44x44が人が親指でタップできる最低限の大きさ

  • =それよりも小さいとタップしづらいかミスる

からなだけ藁😝

で各プラットフォーマーのそう言ったデザイン規約はまとめてくれてるサイトもあるみたいだから参考にしてみるといいよ〜〜〜

さてと、じゃ最後にLazyHGridの基本を見ていこう

struct E36GridHStandardView: View {
    private var fruitsColors: [Color] = [
        .red,
        .green,
        .orange,
        .purple,
        .pink
    ]
    private var fruitsGridItems = [
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
    ]
    var body: some View {
        ScrollView(.horizontal){
            LazyHGrid(rows: fruitsGridItems, spacing: 10){
                ForEach(1...5000, id: \.self){ fruitsIndex in
                    E36CellContentsView(fruitsIndex: fruitsIndex, fruitsColor: fruitsColors[fruitsIndex % fruitsColors.count])
                }
            }
            .padding()
        }
    }
}

てな感じで〜〜〜

横スクロールで行ける感じにでけた〜〜〜🕺

後は、

    private var fruitsGridItems = [
        GridItem(.fixed(200)),
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
        GridItem(.adaptive(minimum: 76, maximum: .infinity)),
    ]

みたいな感じで微調整をLazyVGridと同じ感じでしてあげると、、、

てな感じで調整が出来るだけだし〜〜〜

と、内容は以上なんだけど。

ここでポイント⑥:UIKit時代からスマホアプリを経験してる人ならわかるんだけど、

UIKitまでiOSアプリのフレームワークには

(Objective-C時代の名残り)
AutoLayout

って沼る設定が必要で、これが非常に面倒くさかった💦で、結局、最終的にほとんどの人がどうしてたかといえば、
これまでにやったStackを駆使して、結局、アプリの画面を

(今回やってる)
GridLayout

に近いものにしてた藁🤣だから、HCD(ヒューマンインターフェースガイドライン)の解説も踏まえ、前回の予告でも話した通り、

Grid Layoutがアプリらしいアプリの画面

ってゆーてたんだよね〜〜〜。。。💦ここで経験も踏まえて、初めて触れる人でも読み終われば一定の知識や作法が身につくように解説は順を追ってしてるつもりなんだけど、実際に知らない人ばかりが集まった現場を見てみ?👀

未だにAutoLayoutで云々の唸ってるか、逆に自分はAutoLayoutの神みたいに自称してるかのどちらかだから。
👉そもそも最新のフレームワークですでに捨ててる概念なんか使わないし学ばねーっつーの藁🤣効率悪いからApple自体がSwiftUIで捨ててんのに💦

そんな自己満足で悦に浸る暇があったら、
最新を学びましょう🕺

Apple公式

今回のコードまとめ

import SwiftUI

struct Essentials36ContentsView: View {
    var body: some View {
        E36GridStandardView()
        E36GridHStandardView()
    }
}

struct E36GridStandardView: View {
    private var fruitsColors: [Color] = [
        .red,
        .green,
        .orange,
        .purple,
        .pink
    ]
    private var fruitsGridItems = [
        GridItem(.fixed(120)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
    ]
    var body: some View {
        ScrollView{
            LazyVGrid(columns: fruitsGridItems, spacing: 10){
                ForEach(1...5000, id: \.self){ fruitsIndex in
                    E36CellContentsView(fruitsIndex: fruitsIndex, fruitsColor: fruitsColors[fruitsIndex % fruitsColors.count])
                }
            }
            .padding()
        }
    }
}

struct E36GridHStandardView: View {
    private var fruitsColors: [Color] = [
        .red,
        .green,
        .orange,
        .purple,
        .pink
    ]
    private var fruitsGridItems = [
        GridItem(.fixed(200)),
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
        GridItem(.adaptive(minimum: 76, maximum: .infinity)),
    ]
    var body: some View {
        ScrollView(.horizontal){
            LazyHGrid(rows: fruitsGridItems, spacing: 10){
                ForEach(1...5000, id: \.self){ fruitsIndex in
                    E36CellContentsView(fruitsIndex: fruitsIndex, fruitsColor: fruitsColors[fruitsIndex % fruitsColors.count])
                }
            }
            .padding()
        }
    }
}

struct E36CellContentsView: View {
    var fruitsIndex: Int
    var fruitsColor: Color
    
    var body: some View {
        Text("\(fruitsIndex)")
            .frame(minWidth: 44, maxWidth: .infinity,minHeight: 44)
            .background(fruitsColor)
            .presentationCornerRadius(44)
            .font(.largeTitle)
    }
}

#Preview {
    Essentials36ContentsView()
}

さてと、次回は

変則的なGridを組めるようになる、Gridの活用例を示した

第37章 SwiftUI Grid と GridRow のチュートリアル

をやってく🕺

記事公開後、

いつもどおり、

でやった操作を〜〜〜

今回も
自動でふたつに分けてくれました藁👀
Xcodeの自動機能は
ホント、ありがたいものばかり🕺
バッチし〜〜〜

サンプルコード

◆Essentials36.swift

import SwiftUI
import WebKit

//タイトル
let essentialsChapter36NavigationTitle = "第36章"
let essentialsChapter36Title = "第36章 LazyVGrid と LazyHGrid を使用した SwiftUI グリッドの構築"
let essentialsChapter36SubTitle = "第1節 LazyVGrid と LazyHGrid を使用した SwiftUI グリッドの構築"

//コード
let codeEssentials36 = """
struct Essentials36ContentsView: View {
    var body: some View {
        E36GridStandardView()
        E36GridHStandardView()
    }
}

struct E36GridStandardView: View {
    private var fruitsColors: [Color] = [
        .red,
        .green,
        .orange,
        .purple,
        .pink
    ]
    private var fruitsGridItems = [
        GridItem(.fixed(120)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
    ]
    var body: some View {
        ScrollView{
            LazyVGrid(columns: fruitsGridItems, spacing: 10){
                ForEach(1...5000, id: \\.self){ fruitsIndex in
                    E36CellContentsView(fruitsIndex: fruitsIndex, fruitsColor: fruitsColors[fruitsIndex % fruitsColors.count])
                }
            }
            .padding()
        }
    }
}

struct E36GridHStandardView: View {
    private var fruitsColors: [Color] = [
        .red,
        .green,
        .orange,
        .purple,
        .pink
    ]
    private var fruitsGridItems = [
        GridItem(.fixed(200)),
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
        GridItem(.adaptive(minimum: 76, maximum: .infinity)),
    ]
    var body: some View {
        ScrollView(.horizontal){
            LazyHGrid(rows: fruitsGridItems, spacing: 10){
                ForEach(1...5000, id: \\.self){ fruitsIndex in
                    E36CellContentsView(fruitsIndex: fruitsIndex, fruitsColor: fruitsColors[fruitsIndex % fruitsColors.count])
                }
            }
            .padding()
        }
    }
}

struct E36CellContentsView: View {
    var fruitsIndex: Int
    var fruitsColor: Color
    
    var body: some View {
        Text("\\(fruitsIndex)")
            .frame(minWidth: 44, maxWidth: .infinity,minHeight: 44)
            .background(fruitsColor)
            .presentationCornerRadius(44)
            .font(.largeTitle)
    }
}

#Preview {
    Essentials36ContentsView()
}
"""

//ポイント
let pointEssentials36 = """
⑴GridItemちゃあ何なん👀❓
構文:
GridItem(sizing, spacing: CGFloat?, alignment: <alignment>)
てな感じらしく、解説としては
・サイズ指定引数はGridItemSize型であり、次のいずれかとして宣言する必要あり。
・Flexible()–グリッド内の行または列の数は、LazyVGridまたはLazyHGridビューに渡される配列内のGridItemインスタンスの数によって決まる。
・adaptive(minimum: CGFloat) –行または列のサイズは、使用可能なスペースにできるだけ多くの項目が収まるように調整される。項目を縮小できる最小サイズは、オプションの minimum 引数を使用して指定できる。
・fixed(size: CGFloat ) – アイテムの固定サイズを指定する。
なんだとさ👀💦

⑵アプリらしいアプリってオイラが言ってんのが、、、
ちょうど今、UiPathの連載記事の方で、無味乾燥なデザインの表なんざ、WEBとかアプリに馴染まない👉別にみんな業務でそんな面白くない画面を触る人ばかりじゃないみたいな話をしてんだけど、前者と後者、どちらの方が
・読みやすいか
・可愛いか
・人が見ようとするか
・触った人の記憶に残る=また使おうと思うか
って視点で考えるのが、アプリ開発のUI/UXデザインなんだよねえ🧐
ま、そんなこと関係ないって人にはどうでも良いことだけど藁😛こーゆー微妙な違いを動かしながら知っておくことが後々、何かの要求の時に活きてくる🧐

⑶ちなみに、別に項目ごとのGridItemは必要ない

⑷LazyVGridの場合、GridItemの配列の要素数が、
横に並ぶ数
ってのがここまで来るとわかると思う。

⑸なんで44って数字を使ってるの?
オイラのこれまでのコードが44って数字を使ってるんだけど、なぜかと言えば実は
タップのターゲット:指でのタップが正確に行えるよう、コントロール要素は44 x 44ポイント以上の大きさで作成してください。
って、デザインの規約の方でAppleが推奨してる
👉44x44が人が親指でタップできる最低限の大きさ
=それよりも小さいとタップしづらいかミスる
からなだけ藁😝
各プラットフォーマーのそう言ったデザイン規約はまとめてくれてるサイトもあるみたいだから参考にしてみるといいよ〜〜〜

⑹UIKit時代からスマホアプリを経験してる人ならわかるんだけど、
UIKitまでiOSアプリのフレームワークには
(Objective-C時代の名残り)AutoLayout
って沼る設定が必要で、これが非常に面倒くさかった💦で、結局、最終的にほとんどの人がどうしてたかといえば、
これまでにやったStackを駆使して、結局、アプリの画面を
(今回やってる)GridLayout
に近いものにしてた藁🤣だから、HCD(ヒューマンインターフェースガイドライン)の解説も踏まえ、前回の予告でも話した通り、
Grid Layoutがアプリらしいアプリの画面
ってゆーてたんだよね〜〜〜。。。💦ここで経験も踏まえて、初めて触れる人でも読み終われば一定の知識や作法が身につくように解説は順を追ってしてるつもりなんだけど、実際に知らない人ばかりが集まった現場を見てみ?👀
未だにAutoLayoutで云々の唸ってるか、逆に自分はAutoLayoutの神みたいに自称してるかのどちらかだから。👉そもそも最新のフレームワークですでに捨ててる概念なんか使わないし学ばねーっつーの藁🤣効率悪いからApple自体がSwiftUIで捨ててんのに💦
そんな自己満足で悦に浸る暇があったら、最新を学びましょう🕺
"""

//URL
let urlEssentials36 = "https://note.com/m_kakudo/n/n6fac5d510add"

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentialsCh36: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentialsCh36
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentialsCh36{
    case Sec1
}
//各項目に表示するリスト項目
let dataiOSApp17DevelopmentEssentialsCh36: [ListiOSApp17DevelopmentEssentialsCh36] = [
    ListiOSApp17DevelopmentEssentialsCh36(id: 1, title: essentialsChapter36SubTitle, view: .Sec1),
]
struct iOSApp17DevelopmentEssentialsCh36: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentialsCh36) { data in
                self.containedViewiOSApp17DevelopmentEssentialsCh36(dataiOSApp17DevelopmentEssentialsCh36: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle(essentialsChapter36NavigationTitle)
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentialsCh36(dataiOSApp17DevelopmentEssentialsCh36: ListiOSApp17DevelopmentEssentialsCh36) -> AnyView {
        switch dataiOSApp17DevelopmentEssentialsCh36.view {
        case .Sec1:
            return AnyView(NavigationLink (destination: Essentials36()) {
                Text(dataiOSApp17DevelopmentEssentialsCh36.title)
            })
        }
    }
}
#Preview {
    iOSApp17DevelopmentEssentialsCh36()
}

struct Essentials36: View {
    var body: some View {
        VStack{
            TabView {
                Essentials36ContentsView()
                    .tabItem {
                        Image(systemName: contentsImageTab)
                        Text(contentsTextTab)
                    }
                Essentials36Code()
                    .tabItem {
                        Image(systemName: codeImageTab)
                        Text(codeTextTab)
                    }
                Essentials36Points()
                    .tabItem {
                        Image(systemName: pointImageTab)
                        Text(pointTextTab)
                    }
                Essentials36WEB()
                    .tabItem {
                        Image(systemName: webImageTab)
                        Text(webTextTab)
                    }
            }
        }
    }
}
#Preview {
    Essentials36()
}

struct Essentials36Code: View {
    var body: some View {
        ScrollView{
            Text(codeEssentials36)
        }
    }
}
#Preview {
    Essentials36Code()
}
struct Essentials36Points: View {
    var body: some View {
        ScrollView{
            Text(pointEssentials36)
        }
    }
}
#Preview {
    Essentials36Points()
}
struct Essentials36WebView: UIViewRepresentable {
    let searchURL: URL
    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        let request = URLRequest(url: searchURL)
        view.load(request)
        return view
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}
struct Essentials36WEB: View {
    private var url:URL = URL(string: urlEssentials36)!
    var body: some View {Essentials36WebView(searchURL: url)
    }
}
#Preview {
    Essentials36WEB()
}

struct Essentials36ContentsView: View {
    var body: some View {
        E36GridStandardView()
        E36GridHStandardView()
    }
}

struct E36GridStandardView: View {
    private var fruitsColors: [Color] = [
        .red,
        .green,
        .orange,
        .purple,
        .pink
    ]
    private var fruitsGridItems = [
        GridItem(.fixed(120)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
        GridItem(.adaptive(minimum: 44)),
    ]
    var body: some View {
        ScrollView{
            LazyVGrid(columns: fruitsGridItems, spacing: 10){
                ForEach(1...5000, id: \.self){ fruitsIndex in
                    E36CellContentsView(fruitsIndex: fruitsIndex, fruitsColor: fruitsColors[fruitsIndex % fruitsColors.count])
                }
            }
            .padding()
        }
    }
}

struct E36GridHStandardView: View {
    private var fruitsColors: [Color] = [
        .red,
        .green,
        .orange,
        .purple,
        .pink
    ]
    private var fruitsGridItems = [
        GridItem(.fixed(200)),
        GridItem(.adaptive(minimum: 44, maximum: .infinity)),
        GridItem(.adaptive(minimum: 76, maximum: .infinity)),
    ]
    var body: some View {
        ScrollView(.horizontal){
            LazyHGrid(rows: fruitsGridItems, spacing: 10){
                ForEach(1...5000, id: \.self){ fruitsIndex in
                    E36CellContentsView(fruitsIndex: fruitsIndex, fruitsColor: fruitsColors[fruitsIndex % fruitsColors.count])
                }
            }
            .padding()
        }
    }
}

struct E36CellContentsView: View {
    var fruitsIndex: Int
    var fruitsColor: Color
    
    var body: some View {
        Text("\(fruitsIndex)")
            .frame(minWidth: 44, maxWidth: .infinity,minHeight: 44)
            .background(fruitsColor)
            .presentationCornerRadius(44)
            .font(.largeTitle)
    }
}

#Preview {
    Essentials36ContentsView()
}

◆EssentialsMenu.swift

//フレームワーク
import SwiftUI
import WebKit

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentials: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentials
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentials {
    case Ch1
    //じっくり13で追加
    case Ch2
    //じっくり14で追加
    case Ch3
    //じっくり15で追加
    case Ch4
    //じっくり16で追加
    case Ch5
    //じっくり17で追加
    case Ch6
    //じっくり18で追加
    case Ch7
    //じっくり19で追加
    case Ch8
    //じっくり20、21で追加
    case Ch9
    //じっくり22、23で追加
    case Ch10
    //じっくり24で追加
    case Ch11
    //じっくり25で追加
    case Ch12
    //じっくり26で追加
    case Ch13
    //じっくり27,28で追加
    case Ch14
    //じっくり29で追加
    case Ch15
    //じっくり31で追加
    case Ch16
    //じっくり32で追加
    case Ch17
    //じっくり33で追加
    case Ch18
    //じっくり34で追加
    case Ch19
    //じっくり35で追加
    case Ch20
    //じっくり36で追加
    case Ch21
    //じっくり37で追加
    case Ch22
    //じっくり40で追加
    case Ch23
    //じっくり41で追加
    case Ch24
    //じっくり43で追加
    case Ch25
    //じっくり44で追加
    case Ch26
    //じっくり45で追加
    case Ch27
    //じっくり46で追加
    case Ch28
    //じっくり47で追加
    case Ch29
    //じっくり48で追加
    case Ch30
    //じっくり49で追加
    case Ch31
    //じっくり50で追加
    case Ch32
    //じっくり51で追加
    case Ch33
    //じっくり52で追加
    case Ch34
    //じっくり53で追加
    case Ch35
    //じっくり54で追加
    case Ch36
}
//各項目に表示する文字列
let dataiOSApp17DevelopmentEssentials: [ListiOSApp17DevelopmentEssentials] = [
    ListiOSApp17DevelopmentEssentials(id: 1, title: essentialsChapter1Title, view: .Ch1),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 2, title: essentialsChapter2Title, view: .Ch2),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 3, title: essentialsChapter3Title, view: .Ch3),
    //じっくり15で追加
    ListiOSApp17DevelopmentEssentials(id: 4, title: essentialsChapter4Title, view: .Ch4),
    //じっくり16で追加
    ListiOSApp17DevelopmentEssentials(id: 5, title: essentialsChapter5Title, view: .Ch5),
    //じっくり17で追加
    ListiOSApp17DevelopmentEssentials(id: 6, title: essentialsChapter6Title, view: .Ch6),
    //じっくり18で追加
    ListiOSApp17DevelopmentEssentials(id: 7, title: essentialsChapter7Title, view: .Ch7),
    //じっくり19で追加
    ListiOSApp17DevelopmentEssentials(id: 8, title: essentialsChapter8Title, view: .Ch8),
    //じっくり20、21で追加
    ListiOSApp17DevelopmentEssentials(id: 9, title: essentialsChapter9Title, view: .Ch9),
    //じっくり22、23で追加
    ListiOSApp17DevelopmentEssentials(id: 10, title: essentialsChapter10Title, view: .Ch10),
    //じっくり24で追加
    ListiOSApp17DevelopmentEssentials(id: 11, title: essentialsChapter11Title, view: .Ch11),
    //じっくり25で追加
    ListiOSApp17DevelopmentEssentials(id: 12, title: essentialsChapter12Title, view: .Ch12),
    //じっくり26で追加
    ListiOSApp17DevelopmentEssentials(id: 13, title: essentialsChapter13Title, view: .Ch13),
    //じっくり27,28で追加
    ListiOSApp17DevelopmentEssentials(id: 14, title: essentialsChapter14Title, view: .Ch14),
    //じっくり29で追加
    ListiOSApp17DevelopmentEssentials(id: 15, title: essentialsChapter15Title, view: .Ch15),
    //じっくり31で追加
    ListiOSApp17DevelopmentEssentials(id: 16, title: essentialsChapter16Title, view: .Ch16),
    //じっくり32で追加
    ListiOSApp17DevelopmentEssentials(id: 17, title: essentialsChapter17Title, view: .Ch17),
    //じっくり33で追加
    ListiOSApp17DevelopmentEssentials(id: 18, title: essentialsChapter18Title, view: .Ch18),
    //じっくり34で追加
    ListiOSApp17DevelopmentEssentials(id: 19, title: essentialsChapter19Title, view: .Ch19),
    //じっくり35で追加
    ListiOSApp17DevelopmentEssentials(id: 20, title: essentialsChapter20Title, view: .Ch20),
    //じっくり36で追加
    ListiOSApp17DevelopmentEssentials(id: 21, title: essentialsChapter21Title, view: .Ch21),
    //じっくり37で追加
    ListiOSApp17DevelopmentEssentials(id: 22, title: essentialsChapter22Title, view: .Ch22),
    //じっくり40で追加
    ListiOSApp17DevelopmentEssentials(id: 23, title: essentialsChapter23Title, view: .Ch23),
    //じっくり41で追加
    ListiOSApp17DevelopmentEssentials(id: 24, title: essentialsChapter24Title, view: .Ch24),
    //じっくり43で追加
    ListiOSApp17DevelopmentEssentials(id: 25, title: essentialsChapter25Title, view: .Ch25),
    //じっくり44で追加
    ListiOSApp17DevelopmentEssentials(id: 26, title: essentialsChapter26Title, view: .Ch26),
    //じっくり45で追加
    ListiOSApp17DevelopmentEssentials(id: 27, title: essentialsChapter27Title, view: .Ch27),
    //じっくり46で追加
    ListiOSApp17DevelopmentEssentials(id: 28, title: essentialsChapter28Title, view: .Ch28),
    //じっくり47で追加
    ListiOSApp17DevelopmentEssentials(id: 29, title: essentialsChapter29Title, view: .Ch29),
    //じっくり48で追加
    ListiOSApp17DevelopmentEssentials(id: 30, title: essentialsChapter30Title, view: .Ch30),
    //じっくり49で追加
    ListiOSApp17DevelopmentEssentials(id: 31, title: essentialsChapter31Title, view: .Ch31),
    //じっくり50で追加
    ListiOSApp17DevelopmentEssentials(id: 32, title: essentialsChapter32Title, view: .Ch32),
    //じっくり51で追加
    ListiOSApp17DevelopmentEssentials(id: 33, title: essentialsChapter33Title, view: .Ch33),
    //じっくり52で追加
    ListiOSApp17DevelopmentEssentials(id: 34, title: essentialsChapter34Title, view: .Ch34),
    //じっくり53で追加
    ListiOSApp17DevelopmentEssentials(id: 35, title: essentialsChapter35Title, view: .Ch35),
    //じっくり54で追加
    ListiOSApp17DevelopmentEssentials(id: 36, title: essentialsChapter36Title, view: .Ch36),
]

struct iOSApp17DevelopmentEssentials: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentials) { data in
                self.containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle("iOS開発の章目次")
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: ListiOSApp17DevelopmentEssentials) -> AnyView {
        switch dataiOSApp17DevelopmentEssentials.view {
        case .Ch1:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh1()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch2:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh2()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch3:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh3()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり15で追加
        case .Ch4:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh4()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり16で追加
        case .Ch5:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh5()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり17で追加
        case .Ch6:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh6()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり18で追加
        case .Ch7:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh7()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり19で追加
        case .Ch8:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh8()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり20、21で追加
        case .Ch9:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh9()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり22、23で追加
        case .Ch10:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh10()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり24で追加
        case .Ch11:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh11()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり25で追加
        case .Ch12:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh12()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり26で追加
        case .Ch13:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh13()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり27,28で追加
        case .Ch14:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh14()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり29で追加
        case .Ch15:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh15()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり31で追加
        case .Ch16:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh16()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり32で追加
        case .Ch17:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh17()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり33で追加
        case .Ch18:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh18()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり34で追加
        case .Ch19:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh19()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり35で追加
        case .Ch20:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh20()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり36で追加
        case .Ch21:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh21()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり37で追加
        case .Ch22:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh22()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり40で追加
        case .Ch23:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh23()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり41で追加
        case .Ch24:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh24()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり43で追加
        case .Ch25:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh25()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり44で追加
        case .Ch26:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh26()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり45で追加
        case .Ch27:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh27()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり46で追加
        case .Ch28:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh28()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり47で追加
        case .Ch29:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh29()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり48で追加
        case .Ch30:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh30()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり49で追加
        case .Ch31:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh31()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり50で追加
        case .Ch32:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh32()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり51で追加
        case .Ch33:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh33()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり52で追加
        case .Ch34:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh34()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり53で追加
        case .Ch35:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh35()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり54で追加
        case .Ch36:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh36()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
        }
    }
}

#Preview {
    iOSApp17DevelopmentEssentials()
}

以上。

いやあ、3日連続で記事を書いてたから疲れた藁🤣
後は、ゆっくり文藝春秋ちゃんを読みながら、明日からの仕事の英気を養お
(ぐうたら酒飲んで寝るだけ〜〜〜😛)

では、皆さんも3連休の良い締めくくりを〜〜〜

いいなと思ったら応援しよう!