はじめに
UdemyでSNS風アプリのレイアウト講座を行いました。
そこで学んだことや今後のアプリ開発への抱負を語ります。
学んだこと
- とにかくレイアウトを行うには、モディファイアに慣れることが大切
- このレイアウトにしたいなみ > じゃこのモディファイアだな
- ただ、いい感じのデザインが元から作成されているものや、そもそも生成AIあるし、出てきたらその都度調べるでもいいのか・・・?
- 画面遷移
- 設計段階で、ここはナビゲーション遷移、ここはモーダル遷移など、あらかじめ決めておくと良さそう
- 箱を作る
- struct
今後の動き
- 引数の渡し方
- 機能要件
- どういうiOSアプリを作成して、どういう機能を持たせたくて・・・を明確にする
- 上記を踏まえ、機能に応じた画面遷移や引数どう渡すか?、DBどうしようかを決めていく
- まずは、1画面1画面の機能とどういう感じのレイアウトを作成するか?を明確にしていくのが、大切っぽい
- 最低要件、自分のやりたいことを実現する最低限の要件を決める
- いきなり大きいものを作ろうとしない
コード
注意
今回の講座ではレイアウトに注力したため、投稿をしたからといって反映されるわけではない。
構成
LayoutSNS-Swift/
├── LayoutSNS_SwiftApp.swift # アプリのエントリーポイント。起動時に PostListView を表示する
├── PostListView.swift # 投稿一覧画面。投稿をリスト表示し、詳細画面・投稿フォームへ遷移する
├── PostDetailView.swift # 投稿詳細画面。タップした投稿の名前と本文を表示する
└── CreatePostView.swift # 投稿フォーム画面。シートとして表示され、テキスト入力UIを持つ
完成品
各種コード
LayoutSNS_SwiftApp.swift
//
// LayoutSNS_SwiftApp.swift
// LayoutSNS-Swift
//
// Created by なみふみ on 2026/03/17.
//
import SwiftUI
@main
struct LayoutSNS_SwiftApp: App {
var body: some Scene {
WindowGroup {
PostListView()
}
}
}
PostListView.swift
//
// PostListView.swift
// LayoutSNS-Swift
//
// Created by なみふみ on 2026/03/17.
//
import SwiftUI
// ポスト投稿の配列の構造体を作成
struct Post {
var id: UUID = UUID()
var name: String
var text: String
}
struct PostListView: View {
// 各種定義
let posts: [Post] = [ // ポストリスト
Post(name: "リクト", text: "おはようございます。\n今日も頑張っていきましょう。")
, Post(name: "山田", text: "SwiftUI楽しい。\n早く本アプリ作成したい。")
, Post(name: "花子", text: "本おもしろい!!\n今の現職や転職先の人脈を活かしつつ、リリースまで漕ぎ着けたい!!")
]
@State var isShowingPostView = false // 投稿フォーム画面判定
var body: some View {
NavigationStack {
// スクロールウィンドウで囲う
ZStack(alignment: .bottomTrailing) {
ScrollView {
// 投稿文のレイアウトを作成。posts定数をfor文で回す
ForEach(posts, id: \.id) { post in
NavigationLink {
PostDetailView(post: post) // for文で回した要素を、PostDetailViewへ渡す
} label: {
HStack(alignment: .top) {
Circle()
.frame(width: 50)
.foregroundStyle(.blue)
VStack(alignment: .leading) {
Text(post.name)
Text(post.text)
.multilineTextAlignment(.leading) // 複数行を左揃え
}.frame(maxWidth: .infinity, alignment: .leading) // 左揃え
}
.padding()
.background(Color.black.opacity(0.05))
.clipShape(RoundedRectangle(cornerRadius: 10))
.padding(.horizontal)
}.foregroundStyle(.primary)
}
}
// 投稿用のボタンを作成
Button() {
isShowingPostView = true
} label: { // ボタンのレイアウト
Image(systemName: "plus")
.font(.largeTitle.bold())
.padding()
.background(.blue)
.foregroundStyle(.white)
.clipShape(Circle())
}
.padding()
.sheet(isPresented: $isShowingPostView) {
CreatePostView()
.presentationDetents([.large])
}
}
.navigationTitle("投稿一覧") // ナビゲーションタイトルは、ナビゲーションスタックの中のオブジェクトに付ける
}
}
}
#Preview {
PostListView()
}
PostDetailView.swift
//
// PostDetailView.swift
// LayoutSNS-Swift
//
// Created by なみふみ on 2026/03/17.
//
import SwiftUI
struct PostDetailView: View {
var post: Post
var body: some View {
ScrollView {
VStack(alignment: .leading) {
HStack {
Circle()
.frame(width: 50)
.foregroundStyle(.blue)
Text(post.name)
.bold()
}
Text(post.text)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
.navigationTitle("投稿詳細")
.navigationBarTitleDisplayMode(.inline)
}
}
/*
#Preview ブロックはXcodeのキャンバスにプレビュー表示するためだけのコード
アプリの実際の動作には一切影響しない(本番コードではない)
*/
#Preview {
PostDetailView(post: Post(name: "", text: ""))
}
CreatePostView.swift
//
// CreatePostView.swift
// LayoutSNS-Swift
//
// Created by なみふみ on 2026/03/17.
//
import SwiftUI
struct CreatePostView: View {
@Environment(\.dismiss) var dismiss
@State var text: String = ""
var body: some View {
NavigationStack {
VStack {
HStack(alignment: .top) {
Circle()
.frame(width: 50)
.foregroundStyle(.blue)
TextEditor(text: $text) // 自動更新できるようにする
.frame(height: 150)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray)
)
}.padding()
Spacer()
}
// ナビゲーションバー
.navigationTitle("投稿する")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button("キャンセル") {
dismiss()
}
}
ToolbarItem(placement: .topBarTrailing) {
Button("投稿") {
dismiss()
}
}
}
}
}
}
#Preview {
CreatePostView()
}
