見出し画像

【Xcode】超初心者のためのSwiftUIチュートリアル14

Apple公式のSwiftUIチュートリアルを、プログラミング超初心者向けに優しく解説するシリーズ第14回。今回から「Composing Complex Interfaces(複雑なインターフェースの作成)」に入ります(前回の記事はこちら)。

この章ではアプリのホーム画面を作っていきます。データベースに入っているそれぞれのランドマークが、Lakes、Mountains、Riversの3つのカテゴリーに分かれて水平方向にスクロール表示されるビューができあがります。

スクリーンショット 2020-04-21 13.33.31

Appleの公式チュートリアルはこちらを参照してください。日本語で閲覧したい場合は、自動翻訳機能のあるGoogleChromeなどのブラウザを使用するといいでしょう。

1.セクション1/新しくCategoryHome.swiftを作る

まずこれまでと同様、プロジェクトファイルをダウンロードしましょう。

スクリーンショット 2020-04-18 12.07.27

ダウンロードしたZipファイルを開き、下図を参考に「StartingPoint」フォルダの中のプロジェクトファイルを開きます。

スクリーンショット 2020-04-18 12.50.35

プロジェクト画面を開いたら、まずは新しいSwiftUIファイルを作成します。左上のFile>Mew>File...の順にクリックしましょう。

スクリーンショット 2020-04-18 12.57.30

下のような画面が開きます。SwiftUI Viewファイルを選択したら右下のNextをクリックしましょう。

スクリーンショット 2020-04-18 13.00.54

一番上のSave As:のところにファイル名をCategoryHome.swiftと入力します。次にGroup欄右側の青い部分をクリックして、下図の「Landmarks」フォルダを選択。終わったら右下のCreateをクリックしましょう。

スクリーンショット 2020-04-18 13.05.39

画面右側のファイル一覧を見ると、新しいswiftファイルができています。ドラッグで移動して、わかりやすくファイル位置を整理しておきましょう。これから作る画面はアプリのホーム画面になります。

スクリーンショット 2020-04-18 13.10.38

 CategoryHome.swiftファイルをクリックして中身を表示したら、文字列"Hello World"を、いったん"Landmarks Content"に書き換えましょう。

スクリーンショット 2020-04-18 13.15.50

これで新しいビューファイルができました。

2.SceneDelegate.swiftでホーム画面を変更する

現段階では、第6回で作成したLandmarkListがアプリのホーム画面になっているので、SceneDelegate.swiftのrootViewを書き換えてCategyHomeをホーム画面に設定します。

スクリーンショット 2020-04-19 21.27.29

これでアプリを起動したとき、最初にCategoryHome.swiftのビューをホーム画面として表示するようになりました。

このホーム画面に、すべての項目が表示されるように書き換えていきます。まずは先ほどの「Text("Landmarks Content")」をNavigationViewの中に入れ込みましょう。そしてナビゲーションバーのタイトルを「Featured(おすすめ)」にします。下図の通り.navigationBarTitle(Text("Featured"))を追記しましょう。

スクリーンショット 2020-04-19 21.52.35

画面はこのようになりました。


スクリーンショット 2020-04-19 21.57.44

4.セクション2/カテゴリーリストを作成する

今度は、Dictionary型を用いて各ランドマークをLakes、Rivers、Mountainsの3種類に分類します。「Dictionary型」についてはこのセクションの後に説明しますので、ここではとりあえずコードを書き進めましょう。

CategoryHome.swiftに赤い四角の中身を入力してください。

スクリーンショット 2020-04-20 0.02.48

//LandmarkData内の「category」をkeyにしてvalueをグループ化する
var categories: [String: [Landmark]] { 
       Dictionary(
           grouping: landmarkData,
           by: { $0.category.rawValue}
       )
   }


ランドマークの元データLandmarkData.jsonには、それぞれのランドマークの名前や位置、州などの要素が書かれています。その中の「category」という要素には、Lakes、Rivers、Mountainsのいずれかの値が入っています。その値を「key」にして、ランドマークを3つにグループ化するわけです。

スクリーンショット 2020-04-19 22.06.40

次にNavigationViewの中にカテゴリーリストを作ります。Listを使って上で抽出したcategoryの値(key)をテキストで表示します。

スクリーンショット 2020-04-26 12.25.59

var body: some View {
       NavigationView {
           List {
               ForEach(categories.keys.sorted(), id: \.self) { key in
                   Text(key) //keyとなる値をテキストとしてリストに入れる
               }
           }
           .navigationBarTitle("Featured")
       }
   }

プレビューするとこうなります。

スクリーンショット 2020-04-28 13.59.07

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を作った時と同様です。
新しいビューファイルができたら、中身を下のように書き換えます。

スクリーンショット 2020-04-28 16.04.08

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」になっていますが、データに何らかの変更があったと思われます)

スクリーンショット 2020-04-28 17.54.30

試しにlandmarkData[0]の数値を変えると、対応する配列のcategory要素に表示が変わります。
例えば[1]にするとSilver Salmon Creekのcategory要素「Lakes」に、[5]にするとLake McDonaldのcategory要素「Mountains」になります。自分で数字を変えて、jsonファイルの中身と比較してみるとわかりやすいでしょう。

確認できたら今度はCategoryHome.swiftを開き、今作成したCategoryRowファイルで取得したcategoryの要素を、CategoryHomeの各行に表示する形に書き換えます。さっきとプレビューの表示は一緒ですが、こうすることで各行の中に多様なvalueを入れ込めるようになります。

スクリーンショット 2020-04-29 16.09.11

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でそのカテゴリーのランドマークの名前が横並びに表示されるようにします(プレビュー画面設定はそのまま)。

スクリーンショット 2020-04-29 5.51.35

var body: some View {
    HStack(alignment: .top, spacing: 0) {
        ForEach(self.items) { landmark in
            Text(landmark.name)
                .font(.headline)
        }
    }
}

この状態でプレビューすると、LandmarkDataの配列のうち最初の3つからnameのvalueが抽出され、横並びに表示されます。

スクリーンショット 2020-04-29 6.03.12

表示されるnameの数はプレビュー画面設定の最後の行

items: Array(landmarkData.prefix(3))

で設定しています。prefix()の中の数値を変えると、表示されるnameの数が変化します。例えば5にすると、下のような表示になります。

スクリーンショット 2020-04-29 6.16.16

数が増えるごとに1つ当たりの横幅が狭くなり、表示が崩れてしまいますね。

7.複数のランドマーク名をスクロール表示させる

そこで、ランドマーク名が増えても横スクロールで表示できるビューにします。CategoryRowを下図のように書き換えてください。プレビュー設定は4つの要素が表示されるように変更します。

スクリーンショット 2020-04-29 6.34.51

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行で連なり、画面に収まらない部分も横にスクロールしたら表示されます。

画像25

これで各行の横スクロールビューのベースができ上がりました。
現段階で、CategoryHomeのライブプレビューをみるとこんな感じです。

画像24


次回のセクション4からは、それぞれのカテゴリー行ごとにランドマーク名と画像を横に並べて表示して、タップすると詳細画面に遷移する動きを作っていきます。

関連記事は下のマガジンをご覧ください。


この記事が気に入ったらサポートをしてみませんか?