【Xcode】超初心者のためのSwiftUIチュートリアル14
Apple公式のSwiftUIチュートリアルを、プログラミング超初心者向けに優しく解説するシリーズ第14回。今回から「Composing Complex Interfaces(複雑なインターフェースの作成)」に入ります(前回の記事はこちら)。
この章ではアプリのホーム画面を作っていきます。データベースに入っているそれぞれのランドマークが、Lakes、Mountains、Riversの3つのカテゴリーに分かれて水平方向にスクロール表示されるビューができあがります。
Appleの公式チュートリアルはこちらを参照してください。日本語で閲覧したい場合は、自動翻訳機能のあるGoogleChromeなどのブラウザを使用するといいでしょう。
1.セクション1/新しくCategoryHome.swiftを作る
まずこれまでと同様、プロジェクトファイルをダウンロードしましょう。
ダウンロードしたZipファイルを開き、下図を参考に「StartingPoint」フォルダの中のプロジェクトファイルを開きます。
プロジェクト画面を開いたら、まずは新しいSwiftUIファイルを作成します。左上のFile>Mew>File...の順にクリックしましょう。
下のような画面が開きます。SwiftUI Viewファイルを選択したら右下のNextをクリックしましょう。
一番上のSave As:のところにファイル名をCategoryHome.swiftと入力します。次にGroup欄右側の青い部分をクリックして、下図の「Landmarks」フォルダを選択。終わったら右下のCreateをクリックしましょう。
画面右側のファイル一覧を見ると、新しいswiftファイルができています。ドラッグで移動して、わかりやすくファイル位置を整理しておきましょう。これから作る画面はアプリのホーム画面になります。
CategoryHome.swiftファイルをクリックして中身を表示したら、文字列"Hello World"を、いったん"Landmarks Content"に書き換えましょう。
これで新しいビューファイルができました。
2.SceneDelegate.swiftでホーム画面を変更する
現段階では、第6回で作成したLandmarkListがアプリのホーム画面になっているので、SceneDelegate.swiftのrootViewを書き換えてCategyHomeをホーム画面に設定します。
これでアプリを起動したとき、最初にCategoryHome.swiftのビューをホーム画面として表示するようになりました。
このホーム画面に、すべての項目が表示されるように書き換えていきます。まずは先ほどの「Text("Landmarks Content")」をNavigationViewの中に入れ込みましょう。そしてナビゲーションバーのタイトルを「Featured(おすすめ)」にします。下図の通り.navigationBarTitle(Text("Featured"))を追記しましょう。
画面はこのようになりました。
4.セクション2/カテゴリーリストを作成する
今度は、Dictionary型を用いて各ランドマークをLakes、Rivers、Mountainsの3種類に分類します。「Dictionary型」についてはこのセクションの後に説明しますので、ここではとりあえずコードを書き進めましょう。
CategoryHome.swiftに赤い四角の中身を入力してください。
//LandmarkData内の「category」をkeyにしてvalueをグループ化する
var categories: [String: [Landmark]] {
Dictionary(
grouping: landmarkData,
by: { $0.category.rawValue}
)
}
ランドマークの元データLandmarkData.jsonには、それぞれのランドマークの名前や位置、州などの要素が書かれています。その中の「category」という要素には、Lakes、Rivers、Mountainsのいずれかの値が入っています。その値を「key」にして、ランドマークを3つにグループ化するわけです。
次にNavigationViewの中にカテゴリーリストを作ります。Listを使って上で抽出したcategoryの値(key)をテキストで表示します。
var body: some View {
NavigationView {
List {
ForEach(categories.keys.sorted(), id: \.self) { key in
Text(key) //keyとなる値をテキストとしてリストに入れる
}
}
.navigationBarTitle("Featured")
}
}
プレビューするとこうなります。
Dictionary型とは
「key」と呼ばれる文字列などで「value」の値を取得するのがDictionary型です。...と言われても、初心者にはなんじゃそりゃ??という感じですね。
一通りコードを書いてフンワリと理解できた方もいるかもしれませんが、ここでlandmarkData.jsonを具体例としてDitionary型の使い方を説明します。
(jsonファイルを参照しながら読むとわかりやすいです)
このセクションでは「category」という項目の文字列を「key」にして、landmarkDataから「Turtle Rock(に属する名称、州、位置などの情報の集合体)」や「Silver Salmon Creek(に属する名称、州、位置などの情報の集合体)」に含まれている要素「value」を取得します。
例えばcategory「Rivers」というkeyによって取得するvalueは「Turtle Rock」や「Charley Rivers」などcategoryが「Rivers」になっているランドマークの要素です。
同様に「Mountains」というkeyで取得できるのは「Chilkoot Trail」や「Lake McDonald」などに属するvalueとなります。
なお、Lake McDonaldはランドマーク名に「Lake」が入っていますが、keyはMountainsなので、「Mountains」に分類されます。名前がどうあれ、分類はkeyであるcategoryが基準となるのです。
5.セクション3/CategoryRow.swiftでリストの各行を設定
ここからはLakes、Rivers、Mountainsのそれぞれのカテゴリーに属するランドマークを、1つの行に横並びに表示するための作業をします。
CategoryRow.swiftという名前の新しいビューファイルを作ります。手順は先ほどCategoryHome.swiftを作った時と同様です。
新しいビューファイルができたら、中身を下のように書き換えます。
import SwiftUI
struct CategoryRow: View {
var categoryName: String
var items: [Landmark]
var body: some View {
Text(self.categoryName)
.font(.headline)
}
}
struct CategoryRow_Previews: PreviewProvider {
static var previews: some View {
CategoryRow(
//LandmarkData[0]内の「category」項目を表示
categoryName: landmarkData[0].category.rawValue,
items: Array(landmarkData.prefix(3))
)
}
}
プレビューを表示する時は、表示する中身を具体的に指定する必要があります。ここでは「Turtle Rock」に関する要素が入っている配列[0]のcategory要素「Rivers」が表示されます。(※チュートリアルでは「Mountain」になっていますが、データに何らかの変更があったと思われます)
試しにlandmarkData[0]の数値を変えると、対応する配列のcategory要素に表示が変わります。
例えば[1]にするとSilver Salmon Creekのcategory要素「Lakes」に、[5]にするとLake McDonaldのcategory要素「Mountains」になります。自分で数字を変えて、jsonファイルの中身と比較してみるとわかりやすいでしょう。
確認できたら今度はCategoryHome.swiftを開き、今作成したCategoryRowファイルで取得したcategoryの要素を、CategoryHomeの各行に表示する形に書き換えます。さっきとプレビューの表示は一緒ですが、こうすることで各行の中に多様なvalueを入れ込めるようになります。
var body: some View {
NavigationView {
List {
ForEach(categories.keys.sorted(), id: \.self) { key in
CategoryRow(categoryName: key, items: self.categories[key]!)
}
}
.navigationBarTitle("Featured")
}
}
6.ForEachで各カテゴリーのランドマーク名を一覧表示
CategoryHomeファイルの書き換えが終わったら、再びCategoryRowに戻ります。
下記のように書き換え、HStackでそのカテゴリーのランドマークの名前が横並びに表示されるようにします(プレビュー画面設定はそのまま)。
var body: some View {
HStack(alignment: .top, spacing: 0) {
ForEach(self.items) { landmark in
Text(landmark.name)
.font(.headline)
}
}
}
この状態でプレビューすると、LandmarkDataの配列のうち最初の3つからnameのvalueが抽出され、横並びに表示されます。
表示されるnameの数はプレビュー画面設定の最後の行
items: Array(landmarkData.prefix(3))
で設定しています。prefix()の中の数値を変えると、表示されるnameの数が変化します。例えば5にすると、下のような表示になります。
数が増えるごとに1つ当たりの横幅が狭くなり、表示が崩れてしまいますね。
7.複数のランドマーク名をスクロール表示させる
そこで、ランドマーク名が増えても横スクロールで表示できるビューにします。CategoryRowを下図のように書き換えてください。プレビュー設定は4つの要素が表示されるように変更します。
import SwiftUI
struct CategoryRow: View {
var categoryName: String
var items: [Landmark]
var body: some View {
VStack(alignment: .leading) {
//カテゴリー名
Text(self.categoryName)
.font(.headline)
.padding(.leading, 15)
.padding(.top, 5)
//配列要素のスクロールビュー
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top, spacing: 0) {
ForEach(self.items) { landmark in
Text(landmark.name)
}
}
}
.frame(height: 185) //スクロールビューのフレームの高さ
}
}
}
struct CategoryRow_Previews: PreviewProvider {
static var previews: some View {
CategoryRow(
//LandmarkData配列0内の「category」項目を表示
categoryName: landmarkData[0].category.rawValue,
//LandmarkData配列の最初の4つを表示
items: Array(landmarkData.prefix(4))
)
}
}
これをライブプレビューで確認すると、このようになります。ランドマーク名が1行で連なり、画面に収まらない部分も横にスクロールしたら表示されます。
これで各行の横スクロールビューのベースができ上がりました。
現段階で、CategoryHomeのライブプレビューをみるとこんな感じです。
次回のセクション4からは、それぞれのカテゴリー行ごとにランドマーク名と画像を横に並べて表示して、タップすると詳細画面に遷移する動きを作っていきます。
関連記事は下のマガジンをご覧ください。
この記事が気に入ったらサポートをしてみませんか?