๐ Onboarding
์ด๋ฒ MC3 ํ๋ก์ ํธ์์ ์จ๋ณด๋ฉํ๋ฉด ๊ตฌํ์ ๋งก์์ด์,
์๋ ์ ์จ๋ณด๋ฉ์ ๊ตฌํํ ๋๋ ๊ทธ๋ฅ ๋๋๋๋ก ๋ง ๊ตฌํ์ ํด์ ์ ๋ฆฌ๊ฐ ์๋์์ง๋ง
์ด๋ฒ์ ๋ค์ํ๋ ๊น์ ์ ๋ฆฌ๋ฅผ ํด๋ณด๋ ค ํฉ๋๋ค.
1. ์ฌ์์ ์ฌ๋ถ๋ฅผ ์ ์ฅํ ๋ก์ปฌ ๋ฐ์ดํฐ ๋ณ์ + ์ต์ด ์คํ์ ์จ๋ณด๋ฉ ํ๋ฉด ๋์ฐ๊ฒ ํ๋ ์์
์ฑ์ด ์ฌ์์ ์ฌ๋ถ๋ฅผ ๊ธฐ์ตํ๊ธฐ ์ํด์ , ๊ธฐ๊ธฐ์ ๋ก์ปฌ ๋ฐ์ดํฐ๋ก ์ ์ฅํฉ๋๋ค.
@AppStorage๋ฅผ ์ด์ฉํฉ๋๋ค.
์ต์ด ๊ตฌ๋์์๋ง ํด๋น ๋ฐ์ดํฐ ๊ฐ์ด true์ด๋ฏ๋ก ์จ๋ณด๋ฉํ๋ฉด์ด .fullScreen์ผ๋ก ๋์์ง๊ณ , ์ดํ์ false๋ก ๋ฐ๊ฟ์ฃผ๋ฉด์
์ฌ์คํ์์๋ ๋จ์ง ์๊ฒ ํฉ๋๋ค.
import SwiftUI
struct ContentView: View {
// ์จ๋ณด๋ฉ ํ๋ฉด์ ์ฑ ์ต์ด ์คํ๋ ํ๋ฒ๋ง ๋์ฐ๋๋ก ํ๋ ๋ก์ปฌ ๋ณ์
@AppStorage("isFirstOnboarding) var isFirstOnboarding: Bool = true
var body: some View {
Text("์ฑ ๋ฉ์ธํ๋ฉด")
// ์ฑ ์ต์ด ์คํ ์ full Screen์ผ๋ก ๋์์ง
.fullScreenCover(isPresented: $isFirstOnboarding) {
OnboardingTabView(isFirstOnboarding: $isFirstOnboarding)
}
}
}
2. ์จ๋ณด๋ฉ ๊ธฐ๋ณธ(๊ณตํต) ํ์ด์ง ๊ตฌ์ฑํ๊ธฐ
์จ๋ณด๋ฉ ํ๋ฉด์ ํ ํ์ด์ง๋ฅผ ๋ํ๋ผ ๋ทฐ๋ฅผ ์์ฑํฉ๋๋ค.
๋ณดํต ๋ง์ง๋ง ๋ทฐ๋ ์จ๋ณด๋ฉํ๋ฉด์ ๋๋ด๋ ๋ฒํผ์ด ์กด์ฌํ๊ธฐ ๋๋ฌธ์, ๋ง์ง๋ง ํ์ด์ง๋ฅผ ์ ์ธํ ๋๋จธ์ง ํ์ด์ง๋ค์ ๊ฒฝ์ฐ์ ๋๋ค.
import SwiftUI
struct OnboardingCommonView: View {
let imageName: String
let title: String
let subtitle: String
var body: some View {
VStack {
Image(systemName: ImageName)
.font(.system(size: 100))
.padding()
Text(title)
.bold()
.font(.largeTitle)
.padding()
Text(subtitle)
.font(.title2)
}
}
}
3. ์จ๋ณด๋ฉ ๋ง์ง๋ง ํ์ด์ง ๋ง๋ค๊ธฐ
๋ง์ง๋ง ํ์ด์ง์๋ ๋ณดํต ์จ๋ณด๋ฉ์ ๋ง์น๋ ์๋ฃ์ ๊ฐ์ ๋ฒํผ์ด ์๊ณ , ์ด๋ฅผ AppStorage์์ ์ ์ฅํ๋ Bool ๊ฐ์ false๋ก ๋ณ๊ฒฝํ๊ฒ ํ๋ฉด ๋ฉ๋๋ค.
import SwiftUI
struct OnboardingLastView: View {
let imageName: String
let title: String
let subtitle: String
@Binding var isFirstOnboarding: Bool
var body: some View {
VStack {
Image(systemName: imageName)
.font(.system(size: 100)
.padding()
Text(title)
.font(.largeTitle)
.bold()
.padding()
Text(subtitle)
.font(.title2)
Button {
isFirstOnboarding.toggle()
} label: {
Text("์ฑ ์์ํ๊ธฐ")
.bold()
}
}
}
}
4. ์จ๋ณด๋ฉ ํญ๋ทฐ ๋ง๋ค๊ธฐ
์ด์ ์์์ ๋ง๋ค์ด์ค ์จ๋ณด๋ฉ ํ์ด์ฆ๋ค์ ๋ชจ์ ์ฑ ์ฒ๋ผ ๋๊ธธ ์ ์๊ฒ ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
OnboardingTabView์์ TabView๋ฅผ ํ๋ ๋ง๋ค๊ณ , tabViewStyle์ PageTabViewStyle()๋ก ๋ฃ์ด ์ข์ฐ๋ก ๋๊ธธ ์ ์๋๋ก ํด์ค๋๋ค.
import SwiftUI
struct OnboardingTabView: View {
@Binding var isFirstLaunching: Bool
var body: some View {
TabView {
// ํ์ด์ง 1: ์จ๋ณด๋ฉ ์ฒซ๋ฒ์งธ ํ์ด์ง ( ์ฑ ์๊ฐ )
OnboardingPageView(
imageName: "person.3.fill",
title: "Alright",
subtitle: "๋ชฉ์๋ฆฌ ํฌ๊ธฐ ์กฐ์ ์ ๋์๋๋ฆฌ๋ ์ฑ์ด์์."
)
// ํ์ด์ง 2: ์จ๋ณด๋ฉ ๋๋ฒ์งธ ํ์ด์ง ( ๊ธฐ๋ฅ ์๊ฐ )
OnboardingPageView(
imageName: "note.text.badge.plus",
title: "์ํฉ ์ ํ",
subtitle: "์ํฉ์ ๋ง์ถ์ด ๋ชฉ์๋ฆฌ ํฌ๊ธฐ ์กฐ์ ์ ๋์๋๋ ค์!"
)
// ํ์ด์ง 3: ์ฝ๊ธฐ ํ์ด์ง ์๋ด + ์จ๋ณด๋ฉ ์๋ฃ
OnboardingLastPageView(
imageName: "house",
title: "๋ฐฑ๊ทธ๋ผ์ด๋",
subtitle: "๋ฐฑ๊ทธ๋ผ์ด๋๋ชจ๋๋ก ์คํํด๋ณด์ธ์!",
isFirstOnboarding: $isFirstOnboarding
)
}
// ์ฌ๊ธฐ indexDisplayMode์ never ํน์ always๋ก ๊ธฐ๋ณธ ์ธ๋์ผ์ดํฐ๋ฅผ ์์ฑํด์ค
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
>> ๋ง์ฝ์ SKIP ๋ฒํผ์ด ์๋ค๋ฉด
์ฑ ์ต์ด ์คํ์, Onboarding์ด fullScreen์ผ๋ก ๋์์ง๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ OnboardingTabView์์์ SKIP ๋ฒํผ์ ๋ง๋ค๊ณ ๊ทธ์์ dismiss๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ๋์ ๋๋ค.
import SwiftUI
struct OnboardingTabView: View {
@Environment(\.dismiss) private var dismiss
@Binding var isFirstOnboarding: Bool
private let totalPages = 4
@State private var selectedPage = 0
var body: some View {
ZStack {
Color.sgmGray2
.ignoresSafeArea()
VStack {
HStack {
Spacer()
Button {
isFirstOnboarding = false
dismiss()
} label: {
Text("Skip")
.font(.Pretendard.Light.size17)
.foregroundColor(.sgmGrayA)
}
.padding(.trailing)
.opacity(selectedPage == 3 ? 0 : 1)
}
TabView(selection: $selectedPage) {
...
}
.ignoresSafeArea()
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
}
}
>> Indicator๋ฅผ ์ปค์คํ ํด์ผ ํ๋ค๋ฉด
๋ง์ฝ ๊ธฐ๋ณธ์ผ๋ก ์๊ธฐ๋ indicator๋ฅผ .never๋ก ์ค์ ํ์ฌ ์ฌ์ฉ์ ํ์ง ์๊ณ , ๋ฐ๋ก indicator๋ฅผ ๋ง๋ค์ด ์ปค์คํ ํด์ผํ๋ค๋ฉด !
// ์ด๋ ๊ฒ ์ ์ฒด ํ์ด์ง์ ํ์ฌ ์ ํํ ํ์ด์ง์ ๋ณ์๋ฅผ ์ ์ธํด์ฃผ๊ณ
private let totalPages = 4
@State private var selectedPage = 0
// ์ด๋ ๊ฒ HStack์์ ForEach๋ก Circle()์ ์์ฑํด์ฃผ๊ณ , ์ปฌ๋ฌ๋ฅผ selectedPage์ ๋ฐ๋ผ
// ๊ตฌ๋ถํด์ฃผ๋ฉด ๋๋ค.
// ( ํ์ฌ ์ฝ๋์์ VStack์ดํ Spacer()๊ฐ ์๋ ์ด์ ๋, ์์ง์ผ๋ก ๋งจ ์๋์ชฝ์ ์ธ๋์ผ์ดํฐ๋ฅผ ์์น์ํค๋ ค๊ณ )
VStack {
Spacer()
HStack(spacing: 8) {
ForEach(0..<totalPages, id: \.self) { index in
Circle()
.frame(width: 8, height: 8)
.foregroundColor(index == selectedPage ? .sgmBlue1 : .sgmBlue1.opacity(0.3))
.opacity(selectedPage == 3 ? 0 : 1)
}
}
.padding(.bottom, 40)
}
๋ด๊ฐ ํ๋ก์ ํธ์์ ์ฌ์ฉํ๋, OnboardingTabView ์ฝ๋
import SwiftUI
struct AppOnboardingView: View {
@Environment(\.dismiss) private var dismiss
@Binding var isFirstOnboarding: Bool // ์ฑ Onboarding
@Binding var isCompletedOnboarding: Bool // ๋์๋ง Onboarding
private let totalPages = 4
@State private var selectedPage = 0
var body: some View {
ZStack {
Color.sgmGray2
.ignoresSafeArea()
VStack {
HStack {
Spacer()
Button {
isFirstOnboarding = false
dismiss()
} label: {
Text("Skip")
.font(.Pretendard.Light.size17)
.foregroundColor(.sgmGrayA)
}
.padding(.trailing)
.opacity(selectedPage == 3 ? 0 : 1)
}
TabView(selection: $selectedPage) {
OnboardingCommonPageView(
title: "Alright",
subtitle: "์ฒญ๊ฐ์ฅ์ ์ธ์ ๋ชฉ์๋ฆฌ ํฌ๊ธฐ ์กฐ์ ์\n๋์๋๋ฆฌ๋ ์ฑ ์ฌ๋ผ์์ด์์." ,
imageName: "OnboardingImage1"
)
.tag(0)
OnboardingCommonPageView(
title: "์ํฉ ์ ํ",
subtitle: "๋ํ, ๋ฐํ ๋ฑ ๋ค์ํ ์ํฉ์ ๋ง์ถ์ด\n๋ชฉ์๋ฆฌ ํฌ๊ธฐ ์กฐ์ ์ ๋์๋๋ ค์!" ,
imageName: "OnboardingImage2"
)
.tag(1)
OnboardingCommonPageView(
title: "์ค์๊ฐ ํผ๋๋ฐฑ",
subtitle: "์ ๋๋ฉ์ด์
, ์์ ๋ฑ ์๊ฐ์ ํผ๋๋ฐฑ์ ํตํด\n๋ชฉ์๋ฆฌ ํฌ๊ธฐ๋ฅผ ํ ๋์ ํ์ธํด์!" ,
imageName: "OnboardingImage3"
)
.tag(2)
OnboardingLastPageView(
title: "๋ฐฑ๊ทธ๋ผ์ด๋",
subtitle: "Dynamic Island, Live Activity๋ก\n๋ค๋ฅธ ํ๋ฉด์ ๋ณด๋ฉฐ ๋ชจ๋ํฐ๋ง ํ ์ ์์ด์!" ,
imageName: "OnboardingImage4",
isFirstOnboarding: $isFirstOnboarding,
isCompletedOnboarding: $isCompletedOnboarding
)
.tag(3)
}
.ignoresSafeArea()
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
// Indicator
VStack {
Spacer()
HStack(spacing: 8) {
ForEach(0..<totalPages, id: \.self) { index in
Circle()
.frame(width: 8, height: 8)
.foregroundColor(index == selectedPage ? .sgmBlue1 : .sgmBlue1.opacity(0.3))
.opacity(selectedPage == 3 ? 0 : 1)
}
}
.padding(.bottom, 40)
}
}
}
}
'๐ iOS > SwiftUI' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SwiftUI] In-App Review ์ฐ๋ (2) | 2024.10.12 |
---|---|
[SwiftUI] Invalid frame dimension (0) | 2024.08.19 |
[SwiftUI] Custom Alert View (0) | 2024.06.16 |
[SwiftUI] View -> Flip ๊ธฐ๋ฅ (1) | 2024.06.16 |
[SwiftUI] SwiftData (0) | 2024.04.22 |