[SwiftUI][Table] Table をソートできるようにする

SwiftUI2021

     
⌛️ 2 min.

Table のカラムを ソート可能にする方法を確認します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS15.1.1 Sequoia
  • Xcode 16.2RC
  • iOS 18.2RC
  • Swift 5.9

Table と TableColumn

データを表で表示たいときに使用するのが、Table です。


参考
TableApple Developer Documentation

Table 内で列を表現するのが、TableColumn です。


参考
TableColumnApple Developer Documentation

Table / TableColumn の使い方は何度か記事を作成しました。
SwiftUI2021 [SwiftUI][macOS] Table の使い方 SwiftUI2021 [SwiftUI][macOS] Table の使い方(2) SwiftUI2021 [SwiftUI] Table の使い方

サンプル Table

文字だけだと わかりにくいので、具体的なデータと Table を使用して動作させてみます。

以下のような 番号 ~ 名前を テーブルとして表示してみました。

import SwiftUI

struct TableData: Identifiable {
    var id: UUID = UUID()
    var index: Int
    var name: String
}

let tableData = [TableData(index: 1, name: "Suzuki"),
                 TableData(index: 2, name: "Sato"),
                 TableData(index: 3, name: "Yamamoto"),
                 TableData(index: 4, name: "Higashi"),
                 TableData(index: 5, name: "Kita"),
]

struct ContentView: View {
    @State private var sorts = [KeyPathComparator(\TableData.index)]

    var body: some View {
        VStack {
            Table(of: TableData.self, sortOrder: $sorts, columns: {
                TableColumn("Index", content: { data in
                    Text(data.index.formatted())
                })
                TableColumn("Name", value: \.name)
            }, rows: {
                ForEach(tableData) { data in
                    TableRow(data)
                }
            })
        }
        .padding()
    }
}

以下のような表示になります。

TableWithoutSort
特に設定もしていないので、ソートはできません。
(カラムのヘッダをクリックしても、反応しません。)

TableColumn をソート対象にする

Table をソートできるようにするには、以下の3段階に分かれます。(順不同です)

  1. Step1:Table に sortOrder を指定する
  2. Step2:指定したソートに沿って要素を表示する
  3. Step3:TableColumn のヘッダをクリックすることで、ソート指定されるようにする

Table には、自動でソート順を調整してくれる機能はありませんので、自分で ソートして表示するように調整することが必要です。

Table に sortOrder を指定する

まずは、Table に sortOrder を指定します。
この sortOrder は、SortComparator の配列の Binding であることが必要です。

[Swift] ソートと SortComparator

もちろん SortComparator に従って、Table の row をソートできることが必要です。

ここでは、KeyPathComparator を設定していきます。
最初に設定されている SortComparator は .index を使って、ソートするものにしました。

変更箇所の抜粋は、以下です。

struct ContentView: View {
    @State private var sorts = [KeyPathComparator(\TableData.index)] // Table の引数に渡せるように変数定義

    var body: some View {
        VStack {
            Table(of: TableData.self, sortOrder: $sorts, columns: {. // sortOrder 引数に、Binding<[SortComparator]> を渡す
                TableColumn("Index", content: { data in
                    Text(data.index.formatted())
                })

指定したソートに沿って要素を表示する

sortOrder に指定されている順で 表示されるように、rows での 渡し方を調整することも必要です。
(この調整が実際のソートです)

SortComparator を使用しているので、配列のソートは簡単です。sorted(using:) を使うだけです。


参考
sorted(using:)Apple Developer Documentation

変更箇所を抜粋すると以下のようになります。

                TableColumn("Index", content: { data in
                    Text(data.index.formatted())
                })
                TableColumn("Name", value: \.name)
            }, rows: {
                ForEach(tableData.sorted(using: sorts)) { data in. // sorted(using:) を使用してソートしたものを ForEach に渡す
                    TableRow(data)
                }
            })

TableColumn のヘッダをクリックすることで、ソート指定されるようにする

TableColumn のヘッダをクリックしたときに該当の SortComparator を Table がセットしてくれるには、TableColumn 作成時に、value: 引数を指定して その TableColumn がどのような値として扱われるかを設定することが必要です。

最初の例では、name 列 だけ value: 指定していました。index 列にも指定します。

変更後は以下のようになります。

        VStack {
            Table(of: TableData.self, sortOrder: $sorts, columns: {
                TableColumn("Index", value: \.index, content: { data in. // Index カラムは、.index の値相当すると設定
                    Text(data.index.formatted())
                })
                TableColumn("Name", value: \.name)                       // Name カラムは、.name の値に相当すると設定
            }, rows: {
                ForEach(tableData.sorted(using: sorts)) { data in
                    TableRow(data)
                }
            })
        }

動作確認

上記のように設定すると、以下の動画のように カラムヘッダをクリックすることでソートを変更することができます。

なお、全て変更したコードは以下です。

import SwiftUI

struct TableData: Identifiable {
    var id: UUID = UUID()
    var index: Int
    var name: String
}

let tableData = [TableData(index: 1, name: "Suzuki"),
                 TableData(index: 2, name: "Sato"),
                 TableData(index: 3, name: "Yamamoto"),
                 TableData(index: 4, name: "Higashi"),
                 TableData(index: 5, name: "Kita"),
]

struct ContentView: View {
    @State private var sorts = [KeyPathComparator(\TableData.index)]

    var body: some View {
        VStack {
            Table(of: TableData.self, sortOrder: $sorts, columns: {
                TableColumn("Index", value: \.index, content: { data in
                    Text(data.index.formatted())
                })
                TableColumn("Name", value: \.name)
            }, rows: {
                ForEach(tableData.sorted(using: sorts)) { data in
                    TableRow(data)
                }
            })
        }
        .padding()
    }
}

TableColumn をソート対象外にする

TableColumn を作成するときに、value: を指定しないとソート対象から除外されます。
該当 TableColumn のカラムヘッダをクリックしても、なにも反応しません。

まとめ

Table を ソートできるようにする方法を確認しました。

Table を ソートできるようにする
  • Table に sortOrder として Binding<[SortComparator]> を渡す
  • Table の row を指定に沿ったソートで表示する
  • TableColumn に value: 引数を指定して、ソートできるようにする

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

SwiftUI おすすめ本

SwiftUI を理解するには、以下の本がおすすめです。

SwiftUI ViewMatery

SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。

英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。

View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。

超便利です

SwiftUIViewsMastery

販売元のページは、こちらです。

SwiftUI 徹底入門

# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。

Swift学習におすすめの本

詳解Swift

Swift の学習には、詳解 Swift という書籍が、おすすめです。

著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。

最新版を購入するのがおすすめです。

現時点では、上記の Swift 5 に対応した第5版が最新版です。

コメントを残す

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