1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【SwiftUI】InsetGroupedListStyle()を使って、開閉可能なListを作りたい

Last updated at Posted at 2021-09-04

はじめに

Listには色々種類がありますが、中でもInsetGroupedListStyle()が気に入っています。
他にどんなUIのListがあるのかは、「【SwiftUI】Listの使い方」とかを確認ください。

SwiftUIで実装する上でいつも参考にさせていただいてます。

今回は、UIはInsetGroupedListStyle()なのですが、SidebarListStyle()のように開閉できるListを実装したかったので、それを紹介します。

SidebarListStyle()をそのまま使用するとセルの右にchevronが存在すると思うのですが、そのアイコンや挙動もカスタマイズしたかったので、結局InsetGroupedListStyle()を使用した実装になりました。

成果物

以下ができます。

  • Sectionを押下した時に開閉する
  • 開いているときと閉まっている時で、表示するアイコン(矢印)を変更する
  • 開いているときと閉まっている時で、Section下の(コード上だとSectionの中になりますが)線の表示を切り替える

動画で見たい場合は、以下のGithubリポジトリで公開しているので、そのREADMEを確認ください。

Simulator Screen Shot - iPhone 12 - 2021-09-04 at 16.54.12.png

実装

struct ContentView: View {
    // テスト用データ(APIから取得したデータを格納したり、、今回は辞書を使っているので順番が保証されないのに注意)
    private let testData = ["食べ物": ["明太子パスタ", "ショートケーキ", "プロシュート", "パウンドケーキ"], "飲み物": ["コーラ", "ルイボスティー"]]
    
    // 選択されたSectionがどれかを格納しておくためのSet
    @State private var selection: Set<String> = []

    var body: some View {
        let keys: [String] = testData.map { $0.key }
        let values: [[String]] = keys.compactMap { testData[$0] }
        
        VStack {
            List {
                ForEach(keys.indices) { keyIndex in
                    Section(header: sectionHeader(title: keys[keyIndex], isExpanded: self.selection.contains(keys[keyIndex]))) {
                        
                        if self.selection.contains(keys[keyIndex]) {
                            ForEach(values[keyIndex].indices) { valueIndex in
                                Text(values[keyIndex][valueIndex])
                                    .foregroundColor(Color.black)
                            }
                        }
                    }
                }
                
            }
            .listStyle(InsetGroupedListStyle())
        }
    }
}

extension ContentView {
    private func sectionHeader(title: String, isExpanded: Bool) -> some View {
        Button(action: {self.selectDeselect(selectedSection: title)}) {
            VStack {
                HStack {
                    Text(title)
                    
                    Spacer()
                    
                    Image(systemName: isExpanded ? "chevron.up" : "chevron.down")
                }
                
                if !isExpanded {
                    Divider()
                } else {
                    Divider()
                        .frame(height: 0)
                }
            }
        }
        .foregroundColor(Color.gray)
    }
    
    // Sectionが押下された時に、Setの中身を変更するメソッド
    private func selectDeselect(selectedSection: String) {
        if selection.contains(selectedSection) {
            selection.remove(selectedSection)
        } else {
            selection.insert(selectedSection)
        }
    }
}

コメントにも書いてますが、今回は表示するデータの格納する型が、Dictionaryなので順番は保証されていないです。
順番を保証したい場合は、別のデータ型を使用しないといけないですが、今回はサンプルなのでご了承。。

Set型を準備して、そこに選択されたSectionの文字列を保持しています。
今回は「食べ物」「飲み物」とかが入ってますが、多分IDとかもっと一意っぽい値の方が良いとは思います。

参考にした記事

終わりに

まだまだSwiftUI勉強中の身なので、実はSidebarListStyle()を使用してもっと上手くできるのかもしれないです。より良い方法をご存知の方、ぜひコメントで教えてください! :blush:

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?