見出し画像

【じっくりSw1ftUI53】実践編23〜第35章 SwiftUI リスト、OutlineGroup、DisclosureGroup チュートリアル

さてと、前回

でやった、

SwiftUIリスト、アウトライングループ、ディスクロージャーグループ

実践例

を今回はやってく〜〜〜🕺

毎度、オイラの学びなんざ関係ないって人は、

を見れば、サンプルコードも載ってるみたいだからどうぞ
じゃ、早速、


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

概要では、

階層リストの実践例をこれからやってくぜ

って感じだね👀💦

早速コードを

まずは、前回も作った

struct YaoyaInfo: Identifiable {
    var id = UUID()
    var name: String
    var image: String
    var children: [YaoyaInfo]?
}

なんだけど、今回のレッスン用にできるように、、、

struct E35YaoyaInfo: Identifiable {
    var id = UUID()
    var name: String
    var image: String
    var children: [E35YaoyaInfo]?
}

てな感じで、構造体名を変更して〜〜〜

ここでポイント①:これまでにも結構出てきてるデータモデル

なんだけど、インスタンスとしては、

  • id – 各Info インスタンスを一意に識別する UUID。

  • name – 名前を含む文字列。

  • image – 表示されるイメージを参照する文字列。

  • children – 現在のインスタンスの子オブジェクトの配列。

ってことで😝

でそこに、インスタンスに格納する各値を取り扱う配列が必要なので〜〜〜

ここも前回すでにやった

let yaoyaItems: [YaoyaInfo] = [
    
    YaoyaInfo(name: "野菜と果物", image: "野菜と果物", children: [
        YaoyaInfo(name: "果物", image: "果物", children : [
            YaoyaInfo(name: "りんご", image: "りんごちゃん"),
            YaoyaInfo(name: "みかん", image: "みかんちゃん"),
            YaoyaInfo(name: "バナナ", image: "バナナさん"),
            YaoyaInfo(name: "葡萄", image: "ブドウ先輩"),
            YaoyaInfo(name: "桃", image: "ピーチ姫笑")
        ]),
        YaoyaInfo(name: "お野菜", image: "野菜", children : [
            YaoyaInfo(name: "苺", image: "苺"),
            YaoyaInfo(name: "檸檬", image: "レモン"),
            YaoyaInfo(name: "キャベツ", image: "キャベツ", children: [
                YaoyaInfo(name: "ホール", image: "キャベツ"),
                YaoyaInfo(name: "スライス", image: "キャベツスライス")])
        ]),
    ]),
    
    YaoyaInfo(name: "その他", image: "", children: [
        YaoyaInfo(name: "漬物", image: "漬物", children : [
            YaoyaInfo(name: "野沢菜", image: "野沢菜")
        ]),
        YaoyaInfo(name: "ドライフルーツ", image: "ドライフルーツ", children : [
            YaoyaInfo(name: "いちじく", image: "トルコ産いちじく")
        ])
    ])
]

を今回用に、

let e35YaoyaItems: [E35YaoyaInfo] = [
    
    E35YaoyaInfo(name: "野菜と果物", image: "野菜と果物", children: [
        E35YaoyaInfo(name: "果物", image: "果物", children : [
            E35YaoyaInfo(name: "りんご", image: "りんごちゃん"),
            E35YaoyaInfo(name: "みかん", image: "みかんちゃん"),
            E35YaoyaInfo(name: "バナナ", image: "バナナさん"),
            E35YaoyaInfo(name: "葡萄", image: "ブドウ先輩"),
            E35YaoyaInfo(name: "桃", image: "ピーチ姫笑")
        ]),
        E35YaoyaInfo(name: "お野菜", image: "野菜", children : [
            E35YaoyaInfo(name: "苺", image: "苺"),
            E35YaoyaInfo(name: "檸檬", image: "レモン"),
            E35YaoyaInfo(name: "キャベツ", image: "キャベツ", children: [
                E35YaoyaInfo(name: "ホール", image: "キャベツ"),
                E35YaoyaInfo(name: "スライス", image: "キャベツスライス")])
        ]),
    ]),
    
    E35YaoyaInfo(name: "その他", image: "", children: [
        E35YaoyaInfo(name: "漬物", image: "漬物", children : [
            E35YaoyaInfo(name: "野沢菜", image: "野沢菜")
        ]),
        E35YaoyaInfo(name: "ドライフルーツ", image: "ドライフルーツ", children : [
            E35YaoyaInfo(name: "いちじく", image: "トルコ産いちじく")
        ])
    ])
]

てな感じで今回用のSwiftUIファイルに追記してと。

あ、ここで

別に前回やったデータモデルと配列を使いたいって人は別にここまでの操作はやらなくてもOK

オイラは便宜的に後で管理しやすいように変更してるだけ

だから、

  • 同じ物を流用して使うのがオブジェクト指向言語の旨味だし、

  • 似通ったものが同じプロジェクトファイル内に2つある方がやりにくい

って人はやらなくてOK

リストセルビューを追加

struct E35CellView: View {
    var menu: E35YaoyaInfo
    
    var body: some View {
        HStack{
            Image(menu.image)
                .resizable()
                .scaledToFit()
                .frame(width: 25, height: 25)
                .foregroundStyle(Color.green)
            Text(menu.name)
        }
    }
}

てな感じでとりあえず追加して〜〜〜

コンテンツビューにリストを追加

struct Essentials35ContentsView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
    }
}

で、プレビューを見ると、、、

出来ちゃってんね👀

リストスタイルを設定

てな感じでいくつかあるみたいだから、試してみよ🕺
struct Essentials35ContentsView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(.automatic)
    }
}
.automatic
struct Essentials35ContentsView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(.inset)
    }
}
.inset
おっちゃんには今んところ違いがわからん🧐
struct Essentials35ContentsView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(.plain)
    }
}
.plain
笑何が違うんだ😝
struct Essentials35ContentsView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(.sidebar)
    }
}
.sidebar
わっから〜〜〜ん。同じじゃね?
struct Essentials35ContentsView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(.grouped)
    }
}
.grouped
もはや同じ🤣
struct Essentials35ContentsView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(.insetGrouped)
    }
}
.insetGrouped
全部一緒やん藁

ってことがわかっただけでもいいね藁🤣さてと本のとおり、SidebarListStyle()にしておこう🧐

struct Essentials35ContentsView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(SidebarListStyle())
    }
}
SidebarListStyle()
違いがわからん 藁

アウトライングループ

ちょっと全体を

import SwiftUI

struct E35YaoyaInfo: Identifiable {
    var id = UUID()
    var name: String
    var image: String
    var children: [E35YaoyaInfo]?
}

let e35YaoyaItems: [E35YaoyaInfo] = [
    
    E35YaoyaInfo(name: "野菜と果物", image: "野菜と果物", children: [
        E35YaoyaInfo(name: "果物", image: "果物", children : [
            E35YaoyaInfo(name: "りんご", image: "りんごちゃん"),
            E35YaoyaInfo(name: "みかん", image: "みかんちゃん"),
            E35YaoyaInfo(name: "バナナ", image: "バナナさん"),
            E35YaoyaInfo(name: "葡萄", image: "ブドウ先輩"),
            E35YaoyaInfo(name: "桃", image: "ピーチ姫笑")
        ]),
        E35YaoyaInfo(name: "お野菜", image: "野菜", children : [
            E35YaoyaInfo(name: "苺", image: "苺"),
            E35YaoyaInfo(name: "檸檬", image: "レモン"),
            E35YaoyaInfo(name: "キャベツ", image: "キャベツ", children: [
                E35YaoyaInfo(name: "ホール", image: "キャベツ"),
                E35YaoyaInfo(name: "スライス", image: "キャベツスライス")])
        ]),
    ]),
    
    E35YaoyaInfo(name: "その他", image: "", children: [
        E35YaoyaInfo(name: "漬物", image: "漬物", children : [
            E35YaoyaInfo(name: "野沢菜", image: "野沢菜")
        ]),
        E35YaoyaInfo(name: "ドライフルーツ", image: "ドライフルーツ", children : [
            E35YaoyaInfo(name: "いちじく", image: "トルコ産いちじく")
        ])
    ])
]

struct Essentials35ContentsView: View {
    var body: some View {
        E35StandardListView()
        E35OutlineGroupView()
    }
}

struct E35StandardListView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(.sidebar)
    }
}

struct E35OutlineGroupView: View {
    var body: some View {
        List{
            ForEach(e35YaoyaItems) { yaoyaItem in
                Section(header: Text(yaoyaItem.name)){
                    OutlineGroup(yaoyaItem.children ?? [E35YaoyaInfo](), children: \.children){child in
                        E35CellView(menu: child)
                    }
                }
            }
            .listStyle(SidebarListStyle())
        }
    }
}

struct E35CellView: View {
    var menu: E35YaoyaInfo
    
    var body: some View {
        HStack{
            Image(menu.image)
                .resizable()
                .scaledToFit()
                .frame(width: 25, height: 25)
                .foregroundStyle(Color.green)
            Text(menu.name)
        }
    }
}

#Preview {
    Essentials35ContentsView()
}

てな感じにしておいて、

良い感じでセクションも付いたね👀

ディスクロージャーグループ

これをやるために今回はいくつかのビューを繋いでくみたいだね👀
まずは、テケトーに

全体を取り扱うビュー

struct E35SettingsView: View {
    @State private var isNotification: Bool = false
    @State private var isBag: Bool = false
    @State private var isEssense: Bool = false
    @State private var isColor: Bool = false
    
    var body: some View {
        VStack{
            
        }
    }
}

次は、色を制御するビューを追加

struct E35ColorControlView: View {
    var colors: Color
    var tag: String
    
    var body: some View {
        HStack{
            Text(tag)
            Spacer()
            Circle()
                .fill(colors)
                .frame(width: 30, height: 30)
        }
        .padding(.trailing)
        .scaledToFill()
    }
}

さらに、トグルを制御するビューも追加

struct E35ToggleCotrolView: View {
    var title: String
    @State var state: Bool
    
    var body: some View {
        Toggle(title, isOn: $state)
            .padding(.trailing)
    }
}

で下準備完了🕺

コンテンツビューを色々遊んでく

struct E35SettingsView: View {
    @State private var isNotification: Bool = false
    @State private var isBag: Bool = false
    @State private var isEssense: Bool = false
    @State private var isColor: Bool = false
    
    var body: some View {
        Form{
            E35ToggleCotrolView(title: "通知を許可", state: isNotification)
            E35ToggleCotrolView(title: "バッグ付", state: isBag)
            E35ToggleCotrolView(title: "エッセンス付", state: isEssense)
            E35ColorControlView(colors: .green, tag: "青味増")
            E35ColorControlView(colors: .orange, tag: "熟度増")
            E35ToggleCotrolView(title: "色合い", state: isColor)
            
        }
    }
}

てな感じで追加すると、、、

てな感じ

ディスクロージャーグループを追加してく

struct E35SettingsView: View {
    @State private var isNotification: Bool = false
    @State private var isBag: Bool = false
    @State private var isEssense: Bool = false
    @State private var isColor: Bool = false
    
    var body: some View {
        Form{
            DisclosureGroup{
                E35ToggleCotrolView(title: "通知を許可", state: isNotification)
                E35ToggleCotrolView(title: "バッグ付", state: isBag)
                E35ToggleCotrolView(title: "エッセンス付", state: isEssense)
            }label: {
                Text("買い物オプション")
            }
            DisclosureGroup{
                E35ColorControlView(colors: .green, tag: "青味増")
                E35ColorControlView(colors: .orange, tag: "熟度増")
                E35ToggleCotrolView(title: "色合い", state: isColor)
            } label: {
                Text("購入商品オプション")
            }
        }
    }
}
てな感じ👀💦

で、前回から思ってたんだけど、

最初から開いた状態で表示したいのであれば

struct E35SettingsView: View {
    @State private var isNotification: Bool = false
    @State private var isBag: Bool = false
    @State private var isEssense: Bool = false
    @State private var isColor: Bool = false
    @State private var isExpander: Bool = true
    
    var body: some View {
        Form{
            DisclosureGroup(isExpanded: $isExpander){
                E35ToggleCotrolView(title: "通知を許可", state: isNotification)
                E35ToggleCotrolView(title: "バッグ付", state: isBag)
                E35ToggleCotrolView(title: "エッセンス付", state: isEssense)
            }label: {
                Text("買い物オプション")
            }
            DisclosureGroup(isExpanded: $isExpander){
                E35ColorControlView(colors: .green, tag: "青味増")
                E35ColorControlView(colors: .orange, tag: "熟度増")
                E35ToggleCotrolView(title: "色合い", state: isColor)
            } label: {
                Text("購入商品オプション")
            }
        }
    }
}

てな感じにしてあげると、、、

開いた状態になった👀

以上。

今回のコード(まとめ)

import SwiftUI

struct E35YaoyaInfo: Identifiable {
    var id = UUID()
    var name: String
    var image: String
    var children: [E35YaoyaInfo]?
}

let e35YaoyaItems: [E35YaoyaInfo] = [
    
    E35YaoyaInfo(name: "野菜と果物", image: "野菜と果物", children: [
        E35YaoyaInfo(name: "果物", image: "果物", children : [
            E35YaoyaInfo(name: "りんご", image: "りんごちゃん"),
            E35YaoyaInfo(name: "みかん", image: "みかんちゃん"),
            E35YaoyaInfo(name: "バナナ", image: "バナナさん"),
            E35YaoyaInfo(name: "葡萄", image: "ブドウ先輩"),
            E35YaoyaInfo(name: "桃", image: "ピーチ姫笑")
        ]),
        E35YaoyaInfo(name: "お野菜", image: "野菜", children : [
            E35YaoyaInfo(name: "苺", image: "苺"),
            E35YaoyaInfo(name: "檸檬", image: "レモン"),
            E35YaoyaInfo(name: "キャベツ", image: "キャベツ", children: [
                E35YaoyaInfo(name: "ホール", image: "キャベツ"),
                E35YaoyaInfo(name: "スライス", image: "キャベツスライス")])
        ]),
    ]),
    
    E35YaoyaInfo(name: "その他", image: "", children: [
        E35YaoyaInfo(name: "漬物", image: "漬物", children : [
            E35YaoyaInfo(name: "野沢菜", image: "野沢菜")
        ]),
        E35YaoyaInfo(name: "ドライフルーツ", image: "ドライフルーツ", children : [
            E35YaoyaInfo(name: "いちじく", image: "トルコ産いちじく")
        ])
    ])
]

struct Essentials35ContentsView: View {
    var body: some View {
        E35StandardListView()
        E35OutlineGroupView()
        E35SettingsView()
    }
}

struct E35StandardListView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(.sidebar)
    }
}

struct E35OutlineGroupView: View {
    var body: some View {
        List{
            ForEach(e35YaoyaItems) { yaoyaItem in
                Section(header: Text(yaoyaItem.name)){
                    OutlineGroup(yaoyaItem.children ?? [E35YaoyaInfo](), children: \.children){child in
                        E35CellView(menu: child)
                    }
                }
            }
            .listStyle(SidebarListStyle())
        }
    }
}

struct E35CellView: View {
    var menu: E35YaoyaInfo
    
    var body: some View {
        HStack{
            Image(menu.image)
                .resizable()
                .scaledToFit()
                .frame(width: 25, height: 25)
                .foregroundStyle(Color.green)
            Text(menu.name)
        }
    }
}

struct E35SettingsView: View {
    @State private var isNotification: Bool = false
    @State private var isBag: Bool = false
    @State private var isEssense: Bool = false
    @State private var isColor: Bool = false
    @State private var isExpander: Bool = true
    
    var body: some View {
        Form{
            DisclosureGroup(isExpanded: $isExpander){
                E35ToggleCotrolView(title: "通知を許可", state: isNotification)
                E35ToggleCotrolView(title: "バッグ付", state: isBag)
                E35ToggleCotrolView(title: "エッセンス付", state: isEssense)
            }label: {
                Text("買い物オプション")
            }
            DisclosureGroup(isExpanded: $isExpander){
                E35ColorControlView(colors: .green, tag: "青味増")
                E35ColorControlView(colors: .orange, tag: "熟度増")
                E35ToggleCotrolView(title: "色合い", state: isColor)
            } label: {
                Text("購入商品オプション")
            }
        }
    }
}

struct E35ColorControlView: View {
    var colors: Color
    var tag: String
    
    var body: some View {
        HStack{
            Text(tag)
            Spacer()
            Circle()
                .fill(colors)
                .frame(width: 30, height: 30)
        }
        .padding(.trailing)
        .scaledToFill()
    }
}

struct E35ToggleCotrolView: View {
    var title: String
    @State var state: Bool
    
    var body: some View {
        Toggle(title, isOn: $state)
            .padding(.trailing)
    }
}


#Preview {
    Essentials35ContentsView()
}

Apple公式

さてと、次回は

データの配列のつながりで日本の市販本では滅多にお目にかかれないLazyGrid

第36章 LazyVGrid と LazyHGrid を使用した SwiftUI グリッドの構築

に入ってく〜〜〜

正直、LazyGridがある意味、一番WEBとかスマホアプリのアプリらしいアプリの配列って感じだねえ🧐
👉知ってて、使えるようになっておいて損はない機能

記事公開後、

いつもどおり、

でやった操作を〜〜〜

今回も〜〜〜
前回と〜〜〜
動きは〜〜〜
ほぼ
一緒なことを
🙇‍♂️お断りしておきます💦🙇‍♀️

サンプルコード

◆Essentials35.swift

import SwiftUI
import WebKit

//タイトル
let essentialsChapter35NavigationTitle = "第35章"
let essentialsChapter35Title = "第35章 SwiftUI リスト、OutlineGroup、DisclosureGroup チュートリアル"
let essentialsChapter35SubTitle = "第1節 SwiftUI リスト、OutlineGroup、DisclosureGroup チュートリアル"

//コード
let codeEssentials35 = """
struct E35YaoyaInfo: Identifiable {
    var id = UUID()
    var name: String
    var image: String
    var children: [E35YaoyaInfo]?
}

let e35YaoyaItems: [E35YaoyaInfo] = [
    
    E35YaoyaInfo(name: "野菜と果物", image: "野菜と果物", children: [
        E35YaoyaInfo(name: "果物", image: "果物", children : [
            E35YaoyaInfo(name: "りんご", image: "りんごちゃん"),
            E35YaoyaInfo(name: "みかん", image: "みかんちゃん"),
            E35YaoyaInfo(name: "バナナ", image: "バナナさん"),
            E35YaoyaInfo(name: "葡萄", image: "ブドウ先輩"),
            E35YaoyaInfo(name: "桃", image: "ピーチ姫笑")
        ]),
        E35YaoyaInfo(name: "お野菜", image: "野菜", children : [
            E35YaoyaInfo(name: "苺", image: "苺"),
            E35YaoyaInfo(name: "檸檬", image: "レモン"),
            E35YaoyaInfo(name: "キャベツ", image: "キャベツ", children: [
                E35YaoyaInfo(name: "ホール", image: "キャベツ"),
                E35YaoyaInfo(name: "スライス", image: "キャベツスライス")])
        ]),
    ]),
    
    E35YaoyaInfo(name: "その他", image: "", children: [
        E35YaoyaInfo(name: "漬物", image: "漬物", children : [
            E35YaoyaInfo(name: "野沢菜", image: "野沢菜")
        ]),
        E35YaoyaInfo(name: "ドライフルーツ", image: "ドライフルーツ", children : [
            E35YaoyaInfo(name: "いちじく", image: "トルコ産いちじく")
        ])
    ])
]

struct Essentials35ContentsView: View {
    var body: some View {
        E35StandardListView()
        E35OutlineGroupView()
        E35SettingsView()
    }
}

struct E35StandardListView: View {
    var body: some View {
        List(e35YaoyaItems, children: \\.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(.sidebar)
    }
}

struct E35OutlineGroupView: View {
    var body: some View {
        List{
            ForEach(e35YaoyaItems) { yaoyaItem in
                Section(header: Text(yaoyaItem.name)){
                    OutlineGroup(yaoyaItem.children ?? [E35YaoyaInfo](), children: \\.children){child in
                        E35CellView(menu: child)
                    }
                }
            }
            .listStyle(SidebarListStyle())
        }
    }
}

struct E35CellView: View {
    var menu: E35YaoyaInfo
    
    var body: some View {
        HStack{
            Image(menu.image)
                .resizable()
                .scaledToFit()
                .frame(width: 25, height: 25)
                .foregroundStyle(Color.green)
            Text(menu.name)
        }
    }
}

struct E35SettingsView: View {
    @State private var isNotification: Bool = false
    @State private var isBag: Bool = false
    @State private var isEssense: Bool = false
    @State private var isColor: Bool = false
    @State private var isExpander: Bool = true
    
    var body: some View {
        Form{
            DisclosureGroup(isExpanded: $isExpander){
                E35ToggleCotrolView(title: "通知を許可", state: isNotification)
                E35ToggleCotrolView(title: "バッグ付", state: isBag)
                E35ToggleCotrolView(title: "エッセンス付", state: isEssense)
            }label: {
                Text("買い物オプション")
            }
            DisclosureGroup(isExpanded: $isExpander){
                E35ColorControlView(colors: .green, tag: "青味増")
                E35ColorControlView(colors: .orange, tag: "熟度増")
                E35ToggleCotrolView(title: "色合い", state: isColor)
            } label: {
                Text("購入商品オプション")
            }
        }
    }
}

struct E35ColorControlView: View {
    var colors: Color
    var tag: String
    
    var body: some View {
        HStack{
            Text(tag)
            Spacer()
            Circle()
                .fill(colors)
                .frame(width: 30, height: 30)
        }
        .padding(.trailing)
        .scaledToFill()
    }
}

struct E35ToggleCotrolView: View {
    var title: String
    @State var state: Bool
    
    var body: some View {
        Toggle(title, isOn: $state)
            .padding(.trailing)
    }
}


#Preview {
    Essentials35ContentsView()
}
"""

//ポイント
let pointEssentials35 = """
これまでにも結構出てきてるデータモデルなんだけど、インスタンスとしては、
◆id – 各Infoインスタンスを一意に識別する UUID。
◆name – 名前を含む文字列。
◆image – 表示されるイメージを参照する文字列。
◆children – 現在のインスタンスの子オブジェクトの配列。
ってことで😝
"""
//URL
let urlEssentials35 = "https://note.com/m_kakudo/n/n9b6a7cf0cc57"

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

struct Essentials35: View {
    var body: some View {
        VStack{
            TabView {
                Essentials35ContentsView()
                    .tabItem {
                        Image(systemName: contentsImageTab)
                        Text(contentsTextTab)
                    }
                Essentials35Code()
                    .tabItem {
                        Image(systemName: codeImageTab)
                        Text(codeTextTab)
                    }
                Essentials35Points()
                    .tabItem {
                        Image(systemName: pointImageTab)
                        Text(pointTextTab)
                    }
                Essentials35WEB()
                    .tabItem {
                        Image(systemName: webImageTab)
                        Text(webTextTab)
                    }
            }
        }
    }
}
#Preview {
    Essentials35()
}

struct Essentials35Code: View {
    var body: some View {
        ScrollView{
            Text(codeEssentials35)
        }
    }
}
#Preview {
    Essentials35Code()
}
struct Essentials35Points: View {
    var body: some View {
        ScrollView{
            Text(pointEssentials35)
        }
    }
}
#Preview {
    Essentials35Points()
}
struct Essentials35WebView: 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 Essentials35WEB: View {
    private var url:URL = URL(string: urlEssentials35)!
    var body: some View {Essentials35WebView(searchURL: url)
    }
}
#Preview {
    Essentials35WEB()
}

struct E35YaoyaInfo: Identifiable {
    var id = UUID()
    var name: String
    var image: String
    var children: [E35YaoyaInfo]?
}

let e35YaoyaItems: [E35YaoyaInfo] = [
    
    E35YaoyaInfo(name: "野菜と果物", image: "野菜と果物", children: [
        E35YaoyaInfo(name: "果物", image: "果物", children : [
            E35YaoyaInfo(name: "りんご", image: "りんごちゃん"),
            E35YaoyaInfo(name: "みかん", image: "みかんちゃん"),
            E35YaoyaInfo(name: "バナナ", image: "バナナさん"),
            E35YaoyaInfo(name: "葡萄", image: "ブドウ先輩"),
            E35YaoyaInfo(name: "桃", image: "ピーチ姫笑")
        ]),
        E35YaoyaInfo(name: "お野菜", image: "野菜", children : [
            E35YaoyaInfo(name: "苺", image: "苺"),
            E35YaoyaInfo(name: "檸檬", image: "レモン"),
            E35YaoyaInfo(name: "キャベツ", image: "キャベツ", children: [
                E35YaoyaInfo(name: "ホール", image: "キャベツ"),
                E35YaoyaInfo(name: "スライス", image: "キャベツスライス")])
        ]),
    ]),
    
    E35YaoyaInfo(name: "その他", image: "", children: [
        E35YaoyaInfo(name: "漬物", image: "漬物", children : [
            E35YaoyaInfo(name: "野沢菜", image: "野沢菜")
        ]),
        E35YaoyaInfo(name: "ドライフルーツ", image: "ドライフルーツ", children : [
            E35YaoyaInfo(name: "いちじく", image: "トルコ産いちじく")
        ])
    ])
]

struct Essentials35ContentsView: View {
    var body: some View {
        E35StandardListView()
        E35OutlineGroupView()
        E35SettingsView()
    }
}

struct E35StandardListView: View {
    var body: some View {
        List(e35YaoyaItems, children: \.children){ menu in
            E35CellView(menu: menu)
        }
        .listStyle(.sidebar)
    }
}

struct E35OutlineGroupView: View {
    var body: some View {
        List{
            ForEach(e35YaoyaItems) { yaoyaItem in
                Section(header: Text(yaoyaItem.name)){
                    OutlineGroup(yaoyaItem.children ?? [E35YaoyaInfo](), children: \.children){child in
                        E35CellView(menu: child)
                    }
                }
            }
            .listStyle(SidebarListStyle())
        }
    }
}

struct E35CellView: View {
    var menu: E35YaoyaInfo
    
    var body: some View {
        HStack{
            Image(menu.image)
                .resizable()
                .scaledToFit()
                .frame(width: 25, height: 25)
                .foregroundStyle(Color.green)
            Text(menu.name)
        }
    }
}

struct E35SettingsView: View {
    @State private var isNotification: Bool = false
    @State private var isBag: Bool = false
    @State private var isEssense: Bool = false
    @State private var isColor: Bool = false
    @State private var isExpander: Bool = true
    
    var body: some View {
        Form{
            DisclosureGroup(isExpanded: $isExpander){
                E35ToggleCotrolView(title: "通知を許可", state: isNotification)
                E35ToggleCotrolView(title: "バッグ付", state: isBag)
                E35ToggleCotrolView(title: "エッセンス付", state: isEssense)
            }label: {
                Text("買い物オプション")
            }
            DisclosureGroup(isExpanded: $isExpander){
                E35ColorControlView(colors: .green, tag: "青味増")
                E35ColorControlView(colors: .orange, tag: "熟度増")
                E35ToggleCotrolView(title: "色合い", state: isColor)
            } label: {
                Text("購入商品オプション")
            }
        }
    }
}

struct E35ColorControlView: View {
    var colors: Color
    var tag: String
    
    var body: some View {
        HStack{
            Text(tag)
            Spacer()
            Circle()
                .fill(colors)
                .frame(width: 30, height: 30)
        }
        .padding(.trailing)
        .scaledToFill()
    }
}

struct E35ToggleCotrolView: View {
    var title: String
    @State var state: Bool
    
    var body: some View {
        Toggle(title, isOn: $state)
            .padding(.trailing)
    }
}


#Preview {
    Essentials35ContentsView()
}

◆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
}
//各項目に表示する文字列
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),
]

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)
            })
        }
    }
}

#Preview {
    iOSApp17DevelopmentEssentials()
}

以上。

さてと、明日も時間があれば書くかな😛
みなさんも3連休、有意義に楽しんでくださいね〜〜〜

この記事が気に入ったらサポートをしてみませんか?