ForEachの繰り返し処理にて出力したデータを変更(削除、並び替え)できるようにするには、SwiftUIが該当データを識別する為に、各要素の一意性が保証されている必要があります。
本記事(2/2)では、各要素の一意性を保証したデータコレクションを渡す方法【データ指定】について解説します。
こちらの記事も合わせてお読み下さい。
環境
この記事の情報は次のバージョンで動作確認しています。
【Swift】5.1
【iOS】13.2.2
【macOS】Catalina バージョン 10.15.2
Identifiableプロトコルについて
Swiftにはデータを識別可能にするためにIdentifiableプロトコルが用意されています。
このプロトコルに準拠したクラスやStructは一意に識別できる事を保証します。
プロトコルの要件として、識別子であるプロパティ(id)を必ず有する必要があります。
idは手動で設定もできますが、ユニークなIDを生成するUUID()を利用して自動で設定するのがお勧めです。
1 2 3 4 5 6 |
struct Animal: Identifiable { var id = UUID() // ユニークなIDを自動で設定 var name : String } |
idを手動で設定する場合は、各要素が必ず一意になるように注意して下さい。
万が一idが重複した場合、それだけでエラーにはなりませんが、Swiftは一意である前提で動作しますので、予期しない問題が発生します。
Identifiableプロトコルに準拠したデータコレクションを渡す
ForEachにIdentifiableプロトコルに準拠したデータコレクションを渡すだけで、全要素に対して繰り返し処理を実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Identifiableに準拠したデータ型の定義 struct Animal: Identifiable { var id = UUID() // ユニークなIDを自動で設定 var name : String } struct ContentView: View { // データコレクションの準備 @State private var animals = [ Animal(name: "カピバラ"), Animal(name: "アルパカ"), Animal(name: "トナカイ"), Animal(name: "アルマジロ") ] var body: some View { Form { ForEach(animals) { animal in Text(animal.name) } } } } |
Identifiableプロトコルに準拠していないデータコレクションを渡す
Identifableに準拠していないデータコレクションを渡す場合は、idの代わりとなるプロパティを指定して、一意性の保証を担保します
次のサンプルソースは、プロパティ(code)をidの代わりに使用した例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Identifiableに準拠していないデータ型の定義 struct Animal { var code = UUID() // ユニークなIDを自動で設定 var name : String } struct ContentView: View { // データコレクションの準備 @State private var animals = [ Animal(name: "カピバラ"), Animal(name: "アルパカ"), Animal(name: "トナカイ"), Animal(name: "アルマジロ") ] var body: some View { Form { ForEach(animals, id: \.code) { animal in // プロパティcodeをidの代わりに指定 Text(animal.name) } } } } |
idの代わりに.selfを渡す
各要素のインスタンスを示す.selfも、該当要素を識別するidとして利用できます。
これを利用して、配列を一意性を保証したデータコレクションとして渡せます。
1 2 3 4 5 6 7 8 9 10 11 12 |
struct ContentView: View { var animals = ["カピバラ", "アルパカ", "トナカイ", "アルマジロ"] var body: some View { Form { ForEach(animals, id: \.self) { animal in Text(animal) } } } } |
structの.selfを渡す場合の注意点
ForEachの引数idに渡す値はHashableプロトコルに準拠している必要があります。
Swift標準の型は概ねHashableに準拠していますので、普段はあまり意識する必要がありませんが、自分で型を生成するstructに関しては、明示的に準拠しておかないとコンパイル時にエラーになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
struct Animal: Hashable { // Hashableに準拠する必要あり var name: String } struct ContentView: View { var animals = [ Animal(name: "カピバラ"), Animal(name: "アルパカ"), Animal(name: "トナカイ"), Animal(name: "アルマジロ") ] var body: some View { Form { ForEach(animals, id: \.self) { animal in Text(animal.name) } } } } |
対象のプロパティがHashableでない場合、次のようなエラーメッセージが出ます。
1 2 3 |
Generic struct 'ForEach' requires that 'XXXXX' conform to 'Hashable' |