前節まででView部品をつくりListを色々な表示できるように工夫しました。しかし項目が増えるごとにコードが長くなりますし、View自体にそのまま値を書き込んでしまっています。Appとしてはもっと多くのデータを表示したいでしょう。そこで、今節ではViewに表示するデータの整理方法を紹介します。見た目は前節と同様のものを目指します。

構造体

はじめに、データをViewから独立させて扱いやすくします。 構造体 を使ってデータの容れ物をつくります。

  • SFSwiftUI02-4.swiftpm を開く (サンプルコード)

  • 新しいswiftファイルを追加する

    • ファイルリストで右クリック, New Fileを選択して新しいファイルを作る
    • File.swiftを PhotoData.swift に変更する

swiftui_2_4_1_1.png

  • PhotoData.swiftに以下のコードを記述する
    • struct構造体 を指す
    • id, imageName, title, message の4つの変数を追加
    • Identifiable を付与し、 idUUID() を代入
      • データをひとつひとつ識別できるようになる
      • 後述する ForEach のためにIDが必要

構造体は、複数の変数を詰め合わせた変数 です。その他の役割もありますが今はこの理解で十分です。構造体を使うことでまず複数の種類の値を整理することができます。今回のPhotoDataなら、リストの1項目に必要な画像名、タイトル、メッセージをまとめて扱えるようになります。

コード1

1
2
3
4
5
6
7
8
import Foundation

struct PhotoData : Identifiable {
    var id = UUID()
    var imageName:String
    var title:String
    var message:String
}

配列

これで PhotoData を変数として使うことができます。さっそく表示のために使ってみましょう。リストには複数の項目がありますからPhotoDataも複数必要です。しかしながらデータの数だけPhotoDataを作るのはやはり大変です。そこで変数をたくさん並べられる 配列 を紹介します。

  • ContentViewを開き、photos:[PhotoData] を追加
    • [] に包んだ値をphotosに代入する
    • [] の中に PhotoData を作って並べる(今回は5つ)
    • 項目を並べる時はカンマ(,)で区切る

[PhotoData]PhotoData配列型 です。コードに書いたように必要な数だけPhotoDataを入れることができます。配列に入れた値は photos[0] ではじめの値、 photos[1] で次の値と、番号で参照できます。

ForEach

構造体と配列を使うことでリストに使う値を整理できました。これをphotos[0], photos[1] という形で使えばリストに使えます。しかしデータの数が変わった時、毎度リストを書き直すのは大変です。ここではデータの数だけ自動的にリストを作ってくれるように ForEach を使った表示を考えてみます。

  • List内にSection(“景色写真”)を追加

    • Section内にForEachを書く
  • ForEach( photos ) { photo in … }

    • photos配列の中身を1つずつ取り出す
    • 配列の値が {}内のphoto に順に代入されるのでこれを使う
    • photosの中身の数だけ繰り返される
  • ForEachの中でSceneryRowに photoを渡す

コード2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import SwiftUI

struct ContentView: View {
    var photos:[PhotoData] = [
        PhotoData( imageName:"CC0_01", title:"景色1", message:"森と山です" ),
        PhotoData( imageName:"CC0_04", title:"景色2", message:"きれいな湖です" ),
        PhotoData( imageName:"CC0_06", title:"景色3", message:"フィレンツェです" ),
        PhotoData( imageName:"CC0_07", title:"景色4", message:"ひまわりです" ),
        PhotoData( imageName:"CC0_10", title:"景色5", message:"海に日が沈みます" )
    ]
    
    var body: some View {
        List {
            Section( "景色写真" ) {
                ForEach( photos ) { photo in
                    SceneryRow( imageName: photo.imageName, title: photo.title )
                }
            }
        }
        .listStyle( .grouped )
    }
}

これで前節のリストデータが整理できました。しかしせっかくなのでView部品も少し整理しましょう。上記のコードではSceneryRowにphotoの構造体の中身をバラバラに渡していましたが、photoそのものを渡せるようにしてみます。

  • SceneryRowの受け取る値を *data:PhotoData: に変更
    • 受け取る値にしていたimageName, titleは削除する
  • 値を使用している部分を, data.imageName, data.title に変える

コード3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import SwiftUI

struct SceneryRow : View {
    var data:PhotoData
    
    var body: some View {
        HStack {
            Image( data.imageName )
            .resizable()
            .scaledToFit()
            .frame( maxHeight:80 )
            
            Text( data.title )
        }
    }
}
  • ContentViewでSceneryRowに渡す値もPhotoDataに変えます。

コード4

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import SwiftUI

struct ContentView: View {
    var photos:[PhotoData] = [
        PhotoData( imageName:"CC0_01", title:"景色1", message:"森と山です" ),
        PhotoData( imageName:"CC0_04", title:"景色2", message:"きれいな湖です" ),
        PhotoData( imageName:"CC0_06", title:"景色3", message:"フィレンツェです" ),
        PhotoData( imageName:"CC0_07", title:"景色4", message:"ひまわりです" ),
        PhotoData( imageName:"CC0_10", title:"景色5", message:"海に日が沈みます" )
    ]
    
    var body: some View {
        List {
            Section( "景色写真" ) {
                ForEach( photos ) { photo in
                    SceneryRow( data:photo )
                }
            }
        }
        .listStyle( .grouped )
    }
}

見た目は前節と変わりませんが、データがphotos配列にまとまりList内に書かずに済むようになりました。またデータの値が何を指しているかがわかりやすくなりました。またForEachを使ったため、photosのデータを増やすだけでリストが自動で増えるようになりました。構造体・配列・ForEachと新しい要素を一挙に使いましたが、そのおかげで前節に比べてコードが整理され、かつ使い勝手が良くなりました。

結果

swiftui_2_4_1_2.png