Instagramのホーム画面を作る
Published by @SoNiceInfo at 6/24/2020
ホーム画面を作るための要素
ホーム画面を以下の要素に分解して順番に作り方を紹介します。
タイトルバーとタブ(青枠)
ContentView.swift
ストーリー(オレンジ枠)
StoryView.swift
タイムライン(緑枠)
TimelineView.swift
タイトルバーとタブ(青枠) - ContentView.swift
NavigationView
とTabView
を組み合わせることでタイトルバーとタブを再現します。
タイトルバーのフォントはSwiftUIでは直接変更できないのでイニシャライザ内でUINavigationBar.appearance()
で変更します。
//
// ContentView.swift
//
import SwiftUI
struct ContentView: View {
init() {
UINavigationBar.appearance().titleTextAttributes = [.font : UIFont(name: "Georgia", size: 26)!]
}
var body: some View {
TabView {
NavigationView {
Text("Here for Stories and Timelines")
// タイトルと左右のアイコンを指定
.navigationBarTitle(Text("Instagram"), displayMode: .inline)
.navigationBarItems(
leading: IconView(systemName: "camera"),
trailing: HStack{
IconView(systemName: "tv")
IconView(systemName: "paperplane")
.padding(.leading, 10)
}
.padding(.bottom, 10)
)
}
.tabItem {
IconView(systemName: "house")
}
IconView(systemName: "magnifyingglass")
.tabItem {
IconView(systemName: "magnifyingglass")
}
IconView(systemName: "plus.app")
.tabItem {
IconView(systemName: "plus.app")
}
IconView(systemName: "heart")
.tabItem {
IconView(systemName: "heart")
}
IconView(systemName: "person")
.tabItem {
IconView(systemName: "person")
}
}
// ノッチ部分まで使用
// Xcode11.4以降は不要
.edgesIgnoringSafeArea(.top)
// 選択されているアイコンの色を黒に変更
.accentColor(.black)
}
}
// Iconの形式をそろえる
struct IconView: View {
var systemName: String
var body: some View {
Image(systemName: systemName)
.font(.title)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ストーリー(オレンジ枠) - StoryView.swift
モックはstruct Story
で雛形を作りlet stories: [Story]
で定義しています。ScrollView(.horizonal)
で左右にスクロール可能にしています。ForEach
でstories
をひとつずつ順番に表示する処理をしています。
Instagramらしさを高めるためにStoryの枠をグラデーションにしています。
//
// StoryView.swift
//
import SwiftUI
struct Story {
let id: Int
let name: String
let image: String
}
let stories: [Story] = [
Story(id: 0, name: "Arupaka", image: "animal_arupaka"),
Story(id: 1, name: "Buta", image: "animal_buta"),
Story(id: 2, name: "Hamster", image: "animal_hamster"),
Story(id: 3, name: "Hiyoko", image: "animal_hiyoko"),
Story(id: 4, name: "Inu", image: "animal_inu")
]
struct StoryView: View {
let stories: [Story]
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
// 順番にStoryを処理
ForEach(stories, id: \.id) { (story) in
VStack(spacing: 0) {
ZStack {
Image(story.image)
.resizable()
.overlay(
// Instagramらしいグラデーション色に!!
Circle().stroke(LinearGradient(gradient: Gradient(colors: [.yellow, .red, .purple]), startPoint: .bottomLeading, endPoint: .topTrailing), lineWidth: 5))
.frame(width: 100, height: 100)
.clipShape(Circle())
}
Text(story.name)
}
}
}
.padding(.top, 5)
.padding(.leading, 5)
}
}
}
struct StoryView_Previews: PreviewProvider {
static var previews: some View {
StoryView(stories: stories)
}
}
タイムライン(緑枠) - TimelineView.swift
モックはstruct Timeline
で雛形を作りlet timelines: [Timeline]
で定義しています。ForEach
でtimelines
をひとつずつ順番に表示する処理をしています。
画面幅はUIScreen.main.bounds.width
で取得できるので、画像の高さに適用して正方形にしています。Stack
を使って要素を構築します。
//
// TimelineView.swift
//
import SwiftUI
struct Timeline {
let id: Int
let name: String
let image: String
let post: String
let post_image: String
}
let timelines: [Timeline] = [
Timeline(id: 0, name: "Arupaka", image: "animal_arupaka", post: "This is post content", post_image: "ice_1"),
Timeline(id: 1, name: "Buta", image: "animal_buta", post: "This is post content", post_image: "ice_2"),
Timeline(id: 2, name: "Hamster", image: "animal_hamster", post: "This is post content", post_image: "flower"),
Timeline(id: 3, name: "Hiyoko", image: "animal_hiyoko", post: "This is post content", post_image: "moon"),
Timeline(id: 4, name: "Inu", image: "animal_inu", post: "This is post content", post_image: "animal_inu")
]
struct TimelineView: View {
let timelines: [Timeline]
var body: some View {
VStack() {
ForEach(self.timelines, id: \.id) { (timeline) in
VStack(spacing: 0) {
HStack {
Image(timeline.image)
.resizable()
.clipShape(Circle())
.overlay(
Circle().stroke(Color.white, lineWidth: 4))
.frame(width: 50, height: 50, alignment: .leading)
Text(timeline.name)
.fontWeight(.bold)
Spacer()
Image(systemName: "list.bullet")
}
.padding(.horizontal, 5)
Divider()
Image(timeline.post_image)
.resizable()
.scaledToFill()
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width, alignment: .center)
.clipShape(Rectangle())
Divider()
Group {
Text("(timeline.name) ").fontWeight(.bold) +
Text(timeline.post)
}
.padding(.horizontal, 5)
.frame(width: UIScreen.main.bounds.width, alignment: .leading)
}
}
}
}
}
struct TimelineView_Previews: PreviewProvider {
static var previews: some View {
TimelineView(timelines: timelines)
}
}
組み合わせる - ContentView.swift
上記で作成したStoryView
とTimelineView
をVStack
で表示します。
//
// ContentView.swift
//
import SwiftUI
struct ContentView: View {
init() {
UINavigationBar.appearance().titleTextAttributes = [.font : UIFont(name: "Georgia", size: 26)!]
}
var body: some View {
TabView {
NavigationView {
// 縦スクロール可能にする
ScrollView(.vertical, showsIndicators: false) {
// VStackで作成したViewを構築
VStack {
StoryView(stories: stories)
Divider()
TimelineView(timelines: timelines)
}
}
.navigationBarTitle(Text("Instagram"), displayMode: .inline)
.navigationBarItems(
leading: IconView(systemName: "camera"),
trailing: HStack{
IconView(systemName: "tv")
IconView(systemName: "paperplane")
.padding(.leading, 10)
}
.padding(.bottom, 10)
)
}
.tabItem {
IconView(systemName: "house")
}
IconView(systemName: "magnifyingglass")
.tabItem {
IconView(systemName: "magnifyingglass")
}
IconView(systemName: "plus.app")
.tabItem {
IconView(systemName: "plus.app")
}
IconView(systemName: "heart")
.tabItem {
IconView(systemName: "heart")
}
IconView(systemName: "person")
.tabItem {
IconView(systemName: "person")
}
}
// Xcode11.4以降は不要
.edgesIgnoringSafeArea(.top)
.accentColor(.black)
}
}
struct IconView: View {
var systemName: String
var body: some View {
Image(systemName: systemName)
.font(.title)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
変更履歴
[2020/03/30] Xcode 11.4からTabView
とNavigationView
の組み合わせで.edgesIgnoringSafeArea
は不要となりました。
参考
navigationbar - How can the background or the color in the navigation bar be changed? - Stack OverflowHow to render a gradient - a free SwiftUI by Example tutorial
素材
かわいいフリー素材集 いらすとやBeautiful Free Images & Pictures | Unsplash