MapKit (4)
이렇게 온보딩 페이지를 만들어 본다.
LogoView 새로운 파일로 이전하기
기존에 LocationMapView에 있던 LogoView를 새로운 파일에 옮겨준다.
1
2
3
4
5
6
7
8
9
10
11
struct LogoView: View {
var frameWith: CGFloat
var body: some View {
Image("ddg-map-logo")
.resizable()
.scaledToFit()
.frame(width: frameWith)
}
}
기존에는 frameWidth를 파라미터로 받지 않았는데 이제는 로고뷰를 두군데에 사용하기에 그에 맞게 사용하기위해 파라미터를 받는다.
OnBoardingView 만들기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct OnboardView: View {
var body: some View {
VStack {
LogoView(frameWith: 250)
.padding(.bottom)
VStack(alignment: .leading, spacing: 32) {
OnboardInfoView(
imageName: "building.2.crop.circle",
title: "Restaurant Locations",
description: "Fine places to dine around the convention center"
)
OnboardInfoView(
imageName: "checkmark.circle",
title: "Check In",
description: "Let other iOS Devs know where you are"
)
OnboardInfoView(
imageName: "person.2.circle",
title: "Find Friends",
description: "See where other iOS Devs are and join the party"
)
}
}
.padding(.horizontal, 40)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct OnboardInfoView: View {
var imageName: String
var title: String
var description: String
var body: some View {
HStack(spacing: 26) {
Image(systemName: imageName)
.resizable()
.frame(width: 50, height: 50)
.foregroundStyle(.brandPrimary)
VStack(alignment: .leading, spacing: 4) {
Text(title).bold()
Text(description)
.foregroundStyle(.secondary)
.lineLimit(2)
.minimumScaleFactor(0.75)
}
}
}
}
여긴 딱히 언급할게 없어 보인다.
x 버튼 만들기
온보딩 페이지를 닫기 위한 버튼을 만들어 준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
struct XDismissButton: View {
var body: some View {
ZStack {
Circle()
.frame(width: 30, height: 30)
.foregroundStyle(.brandPrimary)
Image(systemName: "xmark")
.foregroundStyle(.white)
.imageScale(.small)
.frame(width: 44, height: 44)
}
}
}
이렇게 x 버튼을 별도의 view로 관리를 해준다.
온보딩뷰 상태 관리
처음에 실행할때만 보여주고 그 이후엔 안나오게 관리를 할것이다. (지금은 일단 보여지게만 하는게 우선)
LocationMapViewModel 관리를 한다.
@Published var isShowingOnboardView = true
를 만들어서 true / false에 따라 보여지게 하면 된다.
그리고 LocationMapView의 sheet Modifier를 활용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct LocationMapView: View {
var body: some View {
ZStack {
// 생략
}
.sheet(isPresented: $viewModel.isShowingOnboardView) { // new
OnboardView(isShowingOnboardView: $viewModel.isShowingOnboardView)
}
struct OnboardView: View {
@Binding var isShowingOnboardView: Bool / new
var body: some View {
VStack {
HStack {
Spacer()
Button {
isShowingOnboardView = false // new
} label: {
XDismissButton()
}
}
그러면 이렇게 온보딩 페이지가 보이게 된다.
그리고 지금은 onAppear를 통해 위치서비스가 활성화 되었는지 체크를 하는데
sheet의 onDismiss를 사용하여 Dismiss가 될때 바로 체크를 하게 할 수 있다.
1
2
3
4
.sheet(isPresented: $viewModel.isShowingOnboardView, onDismiss: viewModel.checkIfLocationServiceIsEnabled) {
OnboardView(isShowingOnboardView: $viewModel.isShowingOnboardView)
}
UserDefaults를 사용한 상태 관리
이전에 HealthKit을 할때 사용을 해보았는데, 이번에도 이걸 기반으로 해본다.
우선 ViewModel에 변수를 만들어 준다.
1
2
3
4
5
let kHasSeenOnboardView = "hasSeenOnboardView" // key
var hasSeenOnboardView: Bool {
return UserDefaults.standard.bool(forKey: kHasSeenOnboardView)
}
그리고 함수를 하나 만들어 준다.
1
2
3
4
5
6
7
8
func runStartupChecks() {
if !hasSeenOnboardView {
isShowingOnboardView = true
UserDefaults.standard.set(true, forKey: kHasSeenOnboardView)
} else {
checkIfLocationServiceIsEnabled()
}
}
처음에 hasSeenOnboardView가 false 일때 즉, 앱을 처음에 설치를하고 바로 실행을 했을때 온보딩 페이지가 보여지면서 userDefaults에 true로 바꾸면서, 이후에는 온보딩 페에지를 보이지 않게 해준다.
보고난 이후부터는 유저위치를 확인하는것만 작동하게 된다.
LocationMapView의 onAppear를 수정해준다.
1
2
3
4
5
6
7
8
.onAppear {
viewModel.runStartupChecks() // modified
if locationManager.locations.isEmpty {
viewModel.getLocations(for: locationManager)
}
}
@Published var isShowingOnboardView = false // changed
그리고 이젠 true에서 false로 바꿔준다.
처음에 true로 해줬던건, 온보딩 페이지가 제대로 나오는지를 확인하기 위함이었다.
이제 제대로 되는걸 확인 했으니 false로 default를 해줘야 계속 나오지 않고 앱을 처음에 설치했을때만 작동하게 된다.
1
2
3
4
5
6
7
8
9
10
11
12
.sheet(isPresented: $viewModel.isShowingOnboardView, onDismiss: viewModel.checkIfLocationServiceIsEnabled) {
OnboardView(isShowingOnboardView: $viewModel.isShowingOnboardView)
}
.alert(item: $viewModel.alertItem, content: { alertItem in
Alert(title: alertItem.title, message: alertItem.message, dismissButton: alertItem.dismissButton)
})
.onAppear {
viewModel.runStartupChecks()
if locationManager.locations.isEmpty {
viewModel.getLocations(for: locationManager)
}
}
코드 순서때문에 혹시나 sheet가 먼저 작동하는건가? 라고 생각을 할 수 있기에 적어둔다.
먼저 LocationMapView가 렌더링 될때 onAppear 모디파이어가 먼저 작동한다.
그리고 hasSeenOnboardView
가 true / false 인지를 확인한다.
처음에 설치를 했다면
1
2
3
var hasSeenOnboardView: Bool {
return UserDefaults.standard.bool(forKey: kHasSeenOnboardView)
}
hasSeenOnboardView 이녀석은 초기값이 false이다.
runStartupChecks에서의 if !hasSeenOnboardView
라는건 초기값이 false인지 아닌지를 확인을 하고 그 값에 따라서 어떤 작업을 할지를 결정하게 되는것.
Github: Dub-Dub-Grub Repository