SwiftUIとGraphQLを使ったInstagramクローン – ProfileView
今日は、GraphQLを使ったInstagramクローンアプリのパート3で、SwiftUIに深く入り込み、プロフィールビューを構築します。
SwiftUIで構造体を再利用することを学び、いくつかのコントロールについて説明します:VStack、HStack、GeometryReader、Text、Button、Spacer、Image、Dividerなどです。
以前の記事では、InstagramクローンアプリのそれぞれのUIで、ユーザーをサインアップする方法と、ユーザーにログインする方法を学びました。今日はそれをもっと素敵なものにしましょう。
シートベルトを締めて、さあ始めましょう!
より良い学習のために、ソースコード付きのiOS Instagramクローンプロジェクトをダウンロードしてください。
Contents
すぐに始めたいですか?
私たちのハブからこのアプリをクローンして、何の苦労もなく使い始めましょう!
具体的に何を作るか
私たちは以下のビューを構築します:
SwiftUIは、多くのコンポーネントをとても簡単かつストレートに再利用できるので、このような複雑なインターフェイスを構築するのがとても簡単です。
唯一のことは、これらの再利用可能なコンポーネントを作成する方法を学ばなければならないということです。 1つのビューは実際には1つの最終結果に統合される複数のビューの組み合わせです。
簡略化したコンポーネントのリストは以下の通り:
どこで
- VStack
- HStack
- 実際のコントロール(ボタン、仕切りなど)
HStack、Vstack、Zstackとは?
SwiftUI は VStack (Vertical Stack) を使ってビューを垂直方向に、HStack (Horizontal Stack) を使って水平方向に整列させ、Zstack (Z は z-index) を使ってビューを重ねることでUIを構築します。
あるビューを別のビューの下に置く必要がある場合は、必ずVStackを使用します。
あるビューを別のビューと並べる必要がある場合は、常にHStackを使用する必要があります。
あるビューを別のビューに重ねる必要がある場合は、ZStackを使用します。
私たちのメインビューは、水平に配置された多くのビューで構成されるので、すべてをメインのVStackに入れ、そこから始めます。
これは、その(GeometryReaderの)サイズと座標の関数としてすべてをレンダリングすることを可能にします。
再利用性
再利用性はSwiftUIの美点の1つで、複雑なコントロールを作成し、必要な場所で再利用することができます。
このアプリでは、異なるビューで再利用するコンポーネントが主に2つあります: タイムラインとボトムバーです:
このため、これらのUIコードを別々に作成し、より整理します。そのため、これらのUIコードを別々に作成し、より整理できるようにします。ビューに固有のその他のUIコードは、そのビューのコードファイルにまとめておきます。
それでは始めましょう
プロフィールビューの作成
新しいSwiftUIファイルを追加し、ProfileView.swiftと名付けます。
画面いっぱいにコンポーネントを並べるので、GeometryReaderを追加して、その中のすべてのコントロールがそのサイズと座標を使用することを保証できるようにしましょう:
struct ProfileView:ビュー var body: あるビュー {。 GeometryReader { geometry in } } }
さて、私たちのビューは、すべてのコントロールを含むメインVStackに組み込まれるので、それも追加しましょう。
struct ProfileView:ビュー var body: あるビュー {。 GeometryReader { geometry in VStack{ } } } }
そして、コントロールの一番最初の “行 “が一番上になる:
単純なコントロールの行に見えるが、実は複数のコントロールがある:
- ユーザー名」テキスト
- ユーザー名」テキスト
- 小さな矢印と次のコントロールの間のスペースを埋めるスペーサー
- 右側のハンバーガー・ボタン
ボタンにはアイコンを使うので、フリーのアイコンを探してください。フリーアイコンを探すには、Freepikが便利です。
では、HStackを追加してみよう:
構造体 ProfileView:ビュー var body: いくつかのView {。 GeometryReader { geometry in VStack{ HStack{ } } } } }
その中に、最初のテキストがあります:
struct ProfileView:ビュー var body: あるView {。 GeometryReader { geometry in VStack{ HStack{ テキスト("ユーザー名") } } } } }
ここまでで、おそらくここまで来ただろう:
よし。スタートだ。
これを左側に揃えましょう。
(アライメント: .leading)
コードをVStackに追加しよう:
struct ProfileView:ビュー var body: あるビュー {。 GeometryReader { geometry in VStack(アライメント: .leading){ HStack{ テキスト("ユーザー名") } } } } }
また、Textプロパティを追加して、デザインとの一貫性を持たせることもできます:
AppDelegateで定義されているlightBlueColorに色を設定する:
.foregroundColor(lightBlueColor)
フォントの太さを変更します:
.fontWeight(.semibold)
そして最後に、先頭側にパディング(スペース)を追加します:
.padding(.leading, 10)
これで、コードは次のようになります:
struct ProfileView:ビュー var body: あるビュー {。 GeometryReader { geometry in VStack(alignment: .leading){ HStack{ テキスト("username") .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.leading, 10) } } } } }
UIはこのようになります:
それでは、小さな矢印ボタンを追加しましょう。
この記事のパート2で学んだように、ボタンの矢印の画像を追加して、SwiftUIのコードにボタンを追加しましょう:
ボタン(action: {}){ }
そして、そこに画像を追加します:
Button(action: {}){ 画像("arrow-down") .resizable() .frame(width: 10, height: 10) }
そして、整列のために、上部にパディングを追加しましょう:
ボタン(アクション: {}){ 画像("arrow-down") .resizable() .frame(width: 10, height: 10) } .padding(.top, 5)
完全なコードは次のようになります:
struct ProfileView:ビュー var body: あるビュー {。 GeometryReader { geometry in VStack(alignment: .leading){ HStack{ テキスト("username") .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.leading, 10) ボタン(アクション: {}){ 画像("arrow-down") .resizable() .frame(width: 10, height: 10) } .padding(.top, 5) } } } } }
そしてUI:
同じようにハンバーガー・ボタンを追加してみましょう:
Button(action: {}){ 画像("メニュー") .resizable() .frame(width: 20, height: 20) }.padding()
私たちの完全なコード
struct ProfileView:ビュー var body: あるView {。 GeometryReader { geometry in VStack(alignment: .leading){ HStack{ テキスト("username") .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.leading, 10) ボタン(アクション: {}){ 画像("arrow-down") .resizable() .frame(width: 10, height: 10) } .padding(.top, 5) ボタン(アクション: {}){ 画像("メニュー") .resizable() .frame(width: 20, height: 20) }.padding() } } } } }
そして私たちのビュー:
もし、この2つのボタンの間に、ボタンとボタンの間のスペースをすべて取り、すべてを整列させるものがあれば……。
スペーサー()
これでいい感じになりました!
ここまでのコード
struct ProfileView:ビュー var body: あるView {。 GeometryReader { geometry in VStack(alignment: .leading){ HStack{ テキスト("username") .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.leading, 10) ボタン(アクション: {}){ 画像("arrow-down") .resizable() .frame(width: 10, height: 10) } .padding(.top, 5) スペーサー() ボタン(action: {}){ 画像("menu") .resizable() .frame(width: 20, height: 20) }.padding() } } } } }
あとは、HStackの高さを固定し、先頭側にパディングを与えればOKだ:
.frame(height: 50) .padding(.leading, 10)
全コード
struct ProfileView:ビュー var body: あるView {。 GeometryReader { geometry in VStack(alignment: .leading){ HStack{ テキスト("username") .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.leading, 10) ボタン(アクション: {}){ 画像("arrow-down") .resizable() .frame(width: 10, height: 10) } .padding(.top, 5) スペーサー() ボタン(action: {}){ 画像("menu") .resizable() .frame(width: 20, height: 20) }.padding() }.frame(height: 50) .padding(.leading, 10) } } } }
これで…
つ目のHStack
メインVStackの中にすべてを作成したので、最初のHStackの外に追加する新しいコントロールはすべて、自動的にその下に置かれる。
そこで、2つ目のHStackを作成し、スクリーンの2つ目の部分を構築しよう:
これもHStackで、中に4つのVStackを含みます:1つはイメージとその下のテキスト、3つは数字とその下のテキストです。
もうコンセプトはわかったと思うので、この新しいHStackの完全なコードをお見せしよう:
HStack{ VStack{ イメージ("logo-social") .resizable() .frame(width: 90, height: 90) .clipShape(Circle()) .shadow(radius: 3) .overlay(Circle().stroke(Color.pink, lineWidth: 1)) テキスト("お名前") .foregroundColor(lightBlueColor) .fontWeight(.semibold) }.padding(.leading, 10) VStack{ テキスト("10") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("出版物") .font(.system(size: 13)) .foregroundColor(lightBlueColor) }.padding(.leading, 30) VStack{ テキスト("100") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("フォロワー") .font(.system(size: 13)) .foregroundColor(lightBlueColor) }.padding() VStack{ テキスト("1000") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("以下") .font(.system(size: 13)) .foregroundColor(lightBlueColor) } }.frame(height: 100) .padding(.leading, 10)
そして、完全なコードは次のようになる:
struct ProfileView:ビュー var body: あるビュー {。 GeometryReader { geometry in VStack(alignment: .leading){ HStack{ テキスト("username") .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.leading, 10) ボタン(アクション: {}){ 画像("arrow-down") .resizable() .frame(width: 10, height: 10) } .padding(.top, 5) スペーサー() ボタン(action: {}){ 画像("menu") .resizable() .frame(width: 20, height: 20) }.padding() }.frame(height: 50) .padding(.leading, 10) HStack{ VStack{ 画像("logo-social") .resizable() .frame(width: 90, height: 90) .clipShape(Circle()) .shadow(radius: 3) .overlay(Circle().stroke(Color.pink, lineWidth: 1)) テキスト("お名前") .foregroundColor(lightBlueColor) .fontWeight(.semibold) }.padding(.leading, 10) VStack{ テキスト("10") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("出版物") .font(.system(size: 13)) .foregroundColor(lightBlueColor) }.padding(.leading, 30) VStack{ テキスト("100") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("フォロワー") .font(.system(size: 13)) .foregroundColor(lightBlueColor) }.padding() VStack{ テキスト("1000") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("以下") .font(.system(size: 13)) .foregroundColor(lightBlueColor) } }.frame(height: 100) .padding(.leading, 10) } } } }
ビューはこのようになる:
すっきりしました!
プロフィール編集ボタンとディバイダーを追加する
プロフィールの編集ボタンとその下のディバイダーはVStackの中にあるべきだと思いがちです:
しかし、View全体はメインのVStackの中にあるので、その必要はありません:
Button(action: {}){ テキスト("プロフィールの編集") .fontWeight(.bold) .foregroundColor(lightBlueColor) }.frame(width: 400) .padding() ディバイダー()
を使うと次のようになります:
struct ProfileView:ビュー var body: あるView {。 GeometryReader { geometry in VStack(alignment: .leading){ HStack{ テキスト("username") .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.leading, 10) ボタン(アクション: {}){ 画像("arrow-down") .resizable() .frame(width: 10, height: 10) } .padding(.top, 5) スペーサー() ボタン(action: {}){ 画像("menu") .resizable() .frame(width: 20, height: 20) }.padding() }.frame(height: 50) .padding(.leading, 10) HStack{ VStack{ 画像("logo-social") .resizable() .frame(width: 90, height: 90) .clipShape(Circle()) .shadow(radius: 3) .overlay(Circle().stroke(Color.pink, lineWidth: 1)) テキスト("お名前") .foregroundColor(lightBlueColor) .fontWeight(.semibold) }.padding(.leading, 10) VStack{ テキスト("10") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("出版物") .font(.system(size: 13)) .foregroundColor(lightBlueColor) }.padding(.leading, 30) VStack{ テキスト("100") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("フォロワー") .font(.system(size: 13)) .foregroundColor(lightBlueColor) }.padding() VStack{ テキスト("1000") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("以下") .font(.system(size: 13)) .foregroundColor(lightBlueColor) } }.frame(height: 100) .padding(.leading, 10) ボタン(アクション: {}){ テキスト("プロフィール編集") .fontWeight(.bold) .foregroundColor(lightBlueColor) }.frame(width: 400) .padding() ディバイダー() } } } }
そしてUIです:
タイムラインのモック
タイムラインビューはアプリケーションの他の部分でも使われるので、別のファイルに分割するのは理にかなっています。
ProfileViewの中からも同じように再利用できますが、コードを分割した方が整理されます。
TimelineView.swiftファイルを作成します。
SwiftUIでデータを表示する多くの異なる方法がありますが、私は自分のアプリのためにこれを選びました:
- タイムラインビューは LineViewのVStackです。
- 各LineViewは3つのPreviewViewによって構成されるHStackです。
- 各PreviewViewには画像がある
まず最初に、データを保持するための構造体を作成します。この構造体をPreviewと呼ぶことにし、2つのパラメータを持つことにします:
構造体プレビュー var id: Int let imageURL:文字列 }
PreviewViewの構造体を追加してみましょう。
この構造体には、後で設定するPreviewプロパティがありますが、画像のレンダリングにはimageURLプロパティを使用します:
struct PreviewView:ビュー let preview:プレビュー var body: あるビュー {。 画像(preview.imageUrl) .resizable() .frame(width: 136, height: 136) } }
これで、LineViewの構造体を追加することができます。この構造体は、行に表示する3つのプレビューの配列を受け取ります。
将来的には実際のデータを反映するように変更するつもりだが、今のところはこれでOKだろう:
struct LineView:ビュー let previewArray:[Preview] プレビュー配列 var body: あるビュー{。 HStack(spacing: 2){ プレビュービュー(プレビュー: previewArray[0]) PreviewView(preview: previewArray[1]) PreviewView(preview: previewArray[2]) } } }
最後に、Previewオブジェクトの配列を作成し、それをループさせます:
プレビュー:[プレビュー] = [ プレビュー(id: 0, imageUrl: "1")、 Preview(id: 1, imageUrl: "2")、 プレビュー(id: 2, imageUrl: "3")、 プレビュー(id: 3, imageUrl: "4")、 プレビュー(id: 4, imageUrl: "5")、 プレビュー(id: 5, imageUrl: "6")、 プレビュー(id: 6, imageUrl: "7")、 プレビュー(id: 7, imageUrl: "8")、 プレビュー(id: 8, imageUrl: "9")、 プレビュー(id: 9, imageUrl: "10")、 プレビュー(id: 10, imageUrl: "11")、 プレビュー(id: 11, imageUrl: "12")、 プレビュー(id: 12, imageUrl: "13") ]
このArrayには13個のオブジェクトがあり、使用する画像を1から13までの名前で参照した。また、これらの画像をAssetsフォルダに保存しましたが、これも将来変更するつもりです:
これですべての作業が終わったので、Arrayを繰り返し、3つのプレビュー・オブジェクトを渡してLinePreviewsを作成します。
同じオブジェクトを渡していることに注目してほしいが、繰り返しになるが、これは表示のための一時的なものであり、変更される予定だ:
var body: あるビュー { ScrollView{ VStack(alignment: .leading, spacing: 2){ ForEach(previews, id: \.id) { preview in LineView(previewArray: [preview, preview, preview]) } } } }
つまり、完全なコードは次のようになる:
struct TimelineView:ビュー プレビュー:[プレビュー] = [ プレビュー(id: 0, imageUrl: "1")、 プレビュー(id: 1, imageUrl: "2")、 プレビュー(id: 2, imageUrl: "3")、 プレビュー(id: 3, imageUrl: "4")、 プレビュー(id: 4, imageUrl: "5")、 プレビュー(id: 5, imageUrl: "6")、 プレビュー(id: 6, imageUrl: "7")、 プレビュー(id: 7, imageUrl: "8")、 プレビュー(id: 8, imageUrl: "9")、 プレビュー(id: 9, imageUrl: "10")、 プレビュー(id: 10, imageUrl: "11")、 プレビュー(id: 11, imageUrl: "12")、 プレビュー(id: 12, imageUrl: "13") ] var body: あるビュー {. スクロールビュー VStack(alignment: .leading, spacing: 2){ ForEach(previews, id: \.id) { preview in LineView(previewArray: [preview, preview, preview]) } } } } } struct TimelineView_Previews:プレビュープロバイダ static var previews: あるビュー {. TimelineView() } } struct プレビュー { } }. var id: Int let imageUrl:文字列 } struct LineView:ビュー プレビュー配列:[プレビュー] var body: あるビュー { [プレビュー HStack(spacing: 2){ プレビュービュー(プレビュー: previewArray[0]) PreviewView(preview: previewArray[1]) PreviewView(preview: previewArray[2]) } } } struct PreviewView:ビュー プレビュー:プレビュー var body: あるビュー { }. 画像(preview.imageUrl) .resizable() .frame(width: 136, height: 136) } }
そして、Dividerのすぐ下にあるProfileView.swiftからこれを呼び出すと、次のようになります:
... ボタン(アクション: {}){ テキスト("プロフィールを編集") .fontWeight(.bold) .foregroundColor(lightBlueColor) }.frame(width: 400) .padding() ディバイダー() TimelineView().padding(.leading, 10) ...
また、そのすぐ下に別のDividerを追加することで、ほぼ最終的な結果を得ることができます:
... ボタン(アクション: {}){ テキスト("プロフィールを編集") .fontWeight(.bold) .foregroundColor(lightBlueColor) }.frame(width: 400) .padding() ディバイダー() TimelineView().padding(.leading, 10) ディバイダー() ...
どうですか?
もう素敵に見えますか?
では、最後に…
ボトムビュー
ボトムビューはアプリケーションの複数の部分で使うので、また別のファイルになります。
BottomView.swiftファイルを作成し、その中に4つのボタンとその間のスペーサーからなるHStackを作成します。アイコンを忘れずに!
struct BottomView:ビュー var body: いくつかのView {。 HStack{ ボタン(アクション: {}){ 画像("home") .resizable() .frame(width: 30, height: 30) }.padding() スペーサー() ボタン(action: {}){ 画像("検索") .resizable() .frame(width: 30, height: 30) }.padding() スペーサー() ボタン(action: {}){ 画像("プラスボタン") .resizable() .frame(width: 30, height: 30) }.padding() スペーサー() ボタン(action: {}){ 画像("ハート") .resizable() .frame(width: 30, height: 30) }.padding() スペーサー() ボタン(action: {}){ 画像("ユーザー") .resizable() .frame(width: 30, height: 30) }.padding() }.frame(height: 35) } }
これは簡単だった!最後のDividerのすぐ下にあるProfileView.swiftに組み込んでみましょう:
... Divider() TimelineView().padding(.leading, 10) ディバイダー() ボトムビュー() ...
ProfileViewの完全なコードは次のようになります:
インポート SwiftUI struct ProfileView:ビュー var body: いくつかのView {。 GeometryReader { geometry in VStack(アライメント: .leading){ HStack{ テキスト("username") .foregroundColor(lightBlueColor) .fontWeight(.semibold) .padding(.leading, 10) ボタン(アクション: {}){ 画像("arrow-down") .resizable() .frame(width: 10, height: 10) } .padding(.top, 5) スペーサー() ボタン(action: {}){ 画像("menu") .resizable() .frame(width: 20, height: 20) }.padding() }.frame(height: 50) .padding(.leading, 10) HStack{ VStack{ 画像("logo-social") .resizable() .frame(width: 90, height: 90) .clipShape(Circle()) .shadow(radius: 3) .overlay(Circle().stroke(Color.pink, lineWidth: 1)) テキスト("お名前") .foregroundColor(lightBlueColor) .fontWeight(.semibold) }.padding(.leading, 10) VStack{ テキスト("10") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("出版物") .font(.system(size: 13)) .foregroundColor(lightBlueColor) }.padding(.leading, 30) VStack{ テキスト("100") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("フォロワー") .font(.system(size: 13)) .foregroundColor(lightBlueColor) }.padding() VStack{ テキスト("1000") .font(.system(size: 30)) .foregroundColor(lightBlueColor) .fontWeight(.bold) テキスト("以下") .font(.system(size: 13)) .foregroundColor(lightBlueColor) } }.frame(height: 100) .padding(.leading, 10) ボタン(アクション: {}){ テキスト("プロフィール編集") .fontWeight(.bold) .foregroundColor(lightBlueColor) }.frame(width: 400) .padding() ディバイダー() TimelineView().padding(.leading, 10) ディバイダー() ボトムビュー() } } } } struct ProfileView_Previews:プレビュープロバイダ static var previews: あるビュー {. プロファイルビュー() } }
そして最後に…
ProfileViewの完成です:
さて、どうでしたか?
まとめ
今日は、アプリにProfile Viewをモックする方法を学びました。これはまだただのモックですが、時間をかけて機能を持たせていきます。
SwiftUIでコンポーネントを作成して再利用する方法と、複雑なビューを作成するためにコンポーネントを美しく使用する方法を学びました。素晴らしい!
次回は他のビューをいくつか作成する予定です!
ご期待ください!
参考
- このシリーズのパート1はInstagram Cloneの記事です。
- 第2回は、スウィフト・Instagram・クローンです。
- 第3回は、Instagramクローンプロフィールです。
- パート4はInsta Clone Homeviewです。
- iOS Instagram Cloneプロジェクトをソースコード付きでダウンロードして、Back4Appを使い始めてください。
今すぐBack4Appにサインアップして、 Instagramクローンアプリの開発を始めましょう。
SwiftUI の利点は何ですか?
Instagramアプリのようなクローンアプリを開発したいなら、SwiftUIが力を発揮します。
複雑なインターフェースの構築に役立ちます
コンポーネントの再利用
インターフェースの再利用プロセスはシンプルです
必要なのは、これらのコンポーネントをまとめて再利用するための知識だけです。
Hstack、Vstack、Zstack とは何ですか?
これらの名前は、US表記のためつながっています。SwiftUIは以下のパターンでUIを構築します。
UIはVertical Stackを用いて垂直方向に構築されます。
UIはHorizontal Stackを用いて水平方向に構築されます。
オーバーラップするビューはZStackを用いて構築されます。
SwiftUI の最も優れた機能は何ですか?
SwiftUIの最大の特徴は再利用性です。コンポーネントを何度でも再利用してビューを構築できます。これはすべてのアプリに当てはまります。これらのコンポーネントの使い方を知っておく必要があります。