[SwiftUI][macOS] Table の使い方(2)

SwiftUI2021

     
macOS12 から使用可能になっている Table の使い方を説明します。

環境&対象

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

  • macOS Monterey 12.1 Beta
  • Xcode 13.1
  • iOS 15

この記事は、以下の記事の続きです。
SwiftUI2021[SwiftUI][macOS] Table の使い方

前回記事のまとめ

前回の記事では、Table の使い方として以下を説明しました。

  • 行を KeyPath 指定で表示する方法
  • 行に表示データを データ要素から生成する方法
  • Table 要素を選択できるようにする方法

ソートできる Table ビュー

集合要素に含まれる要素を、指定したソート順に表示させることができます。

ソート自体は別途実装する必要がありますが、関連する KeyPath を使用している行にソートのインディケータが表示されるようになります。


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/06/10
//  © 2021  SmallDeskSoftware
//

import SwiftUI

struct Person: Identifiable {
    public let id = UUID()
    public var givenName: String
    public var familyName: String
}

private var personList = [
    Person(givenName: "Juan", familyName: "Chavez"),
    Person(givenName: "Mei", familyName: "Chen"),
    Person(givenName: "Tom", familyName: "Clark"),
    Person(givenName: "Gita", familyName: "Kumar"),
]
struct ContentView: View {
    @State private var persons = personList
    @State private var sortOrder = [KeyPathComparator(\Person.givenName), KeyPathComparator(\Person.familyName)]

    var body: some View {
        VStack {
            // (1)
            Table(persons, sortOrder: $sortOrder) {
                TableColumn("Given Name", value: \.givenName)
                TableColumn("Family Name", value: \.familyName)
            }
            // (2)
            .onChange(of: sortOrder) { newSortOrder in
                persons.sort(using: newSortOrder)
            }
        }
    }
コード解説
  1. sortOrders を Table の引数として指定します
  2. 実際のソート操作は自分で実装する必要があります

ソートについては、NSTableView での実装と変わらない実装が可能になりそうです。

一部のデータ行要素についてのみ表示する Table ビュー

前回の記事では、集合要素(例えば Array)に含まれる要素全てについて表示していましたが、特定の要素についてのみ表示させることもできます。

例えば、前回から使用しているデータのうち、1番目、0番目、3番目の要素のみの表示にしてみます。(指定された通りの順番になります)

この例では、配列中の要素を指定していますが、配列に含まれている必要はありません。

# わかりにくいですが、personList の 1,0,3番目の要素が表示されています。

Only103


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/06/10
//  © 2021  SmallDeskSoftware
//

import SwiftUI

struct Person: Identifiable {
    public let id = UUID()
    public var givenName: String
    public var familyName: String
}

private var personList = [
    Person(givenName: "Juan", familyName: "Chavez"),
    Person(givenName: "Mei", familyName: "Chen"),
    Person(givenName: "Tom", familyName: "Clark"),
    Person(givenName: "Gita", familyName: "Kumar"),
]
struct ContentView: View {
    @State private var persons = personList

    var body: some View {
        VStack {
            Table {
                TableColumn("Given Name", value:\.givenName)
                TableColumn("Family Name", value:\.familyName)
            } rows: {
                // (1)
                TableRow(persons[1])
                TableRow(persons[0])
                TableRow(persons[3])
            }
        }
    }
}
コード解説
  1. rows: 指定で、表示対象とするデータを指定します
MEMO

表示したいデータをどの段階でフィルターするのかは、アプリ全体のアーキテクチャから決定する必要がありそうです。

現時点で提供されている機能で、外部から渡されたデータがすでにフィルターされていてもOKですし、自分でフィルターして表示することもできます。

一部のデータ行要素について動的に構成して表示する Table ビュー

rows 部分は、ResultBuilder になっているので、ループを使用して複数要素を指定するときは、ForEach を使用する必要があります。


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/06/10
//  © 2021  SmallDeskSoftware
//

import SwiftUI

struct Person: Identifiable {
    public let id = UUID()
    public var givenName: String
    public var familyName: String
}

private var personList = [
    Person(givenName: "Juan", familyName: "Chavez"),
    Person(givenName: "Mei", familyName: "Chen"),
    Person(givenName: "Tom", familyName: "Clark"),
    Person(givenName: "Gita", familyName: "Kumar"),
]
struct ContentView: View {
    @State private var persons = personList

    var body: some View {
        VStack {
            Table {
                TableColumn("Given Name", value:\.givenName)
                TableColumn("Family Name", value:\.familyName)
            } rows: {
                // (1)
                ForEach(persons.reversed()) { person in
                    TableRow(person)
                }
            }
        }
    }
}
コード解説
  1. rows にて、persons を逆順にしたものから TableRow を ForEach を使って生成する

データ要素について動的に列を構成して表示する Table ビュー は、できない?!

上記の rows と同様に、column についても動的に生成させられそうなのですが、column に対して ForEach は使えないようです・・・・

ForEach を column 内で使用するとコンパイルエラーとなります。

Xcode13.1 時点では、以下のエラーとなりました。


The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

エラーの発生するコードは以下の通りです。


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/06/10
//  © 2021  SmallDeskSoftware
//

import SwiftUI

struct Person: Identifiable {
    public let id = UUID()
    public var givenName: String
    public var familyName: String
    public var memo: String = ""
}

private var personList = [
    Person(givenName: "Juan", familyName: "Chavez"),
    Person(givenName: "Mei", familyName: "Chen"),
    Person(givenName: "Tom", familyName: "Clark"),
    Person(givenName: "Gita", familyName: "Kumar"),
]
struct ContentView: View {
    @State private var persons = personList

    var body: some View {
        VStack {
            Table(persons) {
                TableColumn("Given Name", value: \.givenName)
                TableColumn("Family Name", value: \.familyName)
                ForEach(1..3) { index in
                    TableColumn("many columns(\(index))") { person in
                        Text("Unknown column")
                    }
                }
            }
        }
    }
}

まとめ:SwiftUI Table の使い方(2)

SwiftUI Table の使い方(2)
  • Table 定義時に sortOrder を指定すると、ソートのインディケータ表示含め ソート対応が可能となる(ソート自体の実装は別途必要)
  • rows を使用することで、特定データの表示が可能となる
  • TableColumn を動的に構成することは、難しそう(不具合かも??)

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

コメントを残す

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