랜드마크 앱에서 유저들은 프로필을 만들 수 있습니다. 유저들에게 프로필 편집 기능을 제공하기 위해 편집 모드와 환경설정 화면을 추가해줍니다.

프로젝트의 Model 그룹에 Profile.swift 라는 스위프트 파일을 만들고 다음과 같이 작성해서 새로운 프로필 모델을 추가해줍니다.
**// Profile.swift
import Foundation
struct Profile {
var username: String
var prefersNotifications = true
var seasonalPhoto = Season.winter
var goalDate = Date()
static let `default` = Profile(username: "g_kumar")
enum Season: String, CaseIterable, Identifiable {
case spring = "🌷"
case summer = "🌞"
case autumn = "🍂"
case winter = "☃️"
var id: String { rawValue }
}
}**
코드 설명

프로젝트의 Views 그룹 안에 Profiles 라는 그룹을 새로 만들고 그 안에 ProfileHost.swift 라는 이름의 SwiftUI 뷰를 만들어줍니다. 이 뷰는 정적인 프로필 내용을 보여주는 summary 뷰와 편집 모드를 제공해줍니다.
**// ProfileHost.swift**
**import SwiftUI
struct ProfileHost: View {
@State private var draftProfile = Profile.default
var body: some View {
Text("Profile for: \(draftProfile.username)")
}
}
struct ProfileHost_Previews: PreviewProvider {
static var previews: some View {
ProfileHost()
}
}**
ProfileSummary.swift 라는 이름의 SwiftUI view를 만들어줍니다. 이 뷰에서는 Profile 인스턴스를 불러와서 유저의 기본 정보를 보여줍니다.
**// ProfileSummary.swift
import SwiftUI
struct ProfileSummary: View {
var profile: Profile
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 10) {
Text(profile.username)
.bold()
.font(.title)
Text("Notifications: \(profile.prefersNotifications ? "On": "Off")")
Text("Seasonal Photos: \(profile.seasonalPhoto.rawValue)")
Text("Goal Date: ") + Text(profile.goalDate, style: .date)
}
}
}
}
struct ProfileSummary_Previews: PreviewProvider {
static var previews: some View {
ProfileSummary(profile: Profile.default)
}
}**
summary 뷰를 불러오도록 ProfileHost.swift를 다음과 같이 수정해줍니다.
**// ProfileHost.swift**
import SwiftUI
struct ProfileHost: View {
@State private var draftProfile = Profile.default
var body: some View {
**VStack(alignment: .leading, spacing: 20) {
ProfileSummary(profile: draftProfile)
}
.padding()**
}
}
struct ProfileHost_Previews: PreviewProvider {
static var previews: some View {
ProfileHost()
}
}

Hikes 그룹에 HikeBadge.swift라는 SwiftUI view를 만들어서 다음과 같은 배지 뷰를 구성해줍니다.
**// HikeBadge.swift
import SwiftUI
struct HikeBadge: View {
var name: String
var body: some View {
VStack(alignment: .center) {
// 배지는 렌더링되는 프레임의 크기에 따라 달라지기 때문에 300x300 크기의 프레임을 기준으로 렌더링되도록 고정?해줍니다.
Badge()
.frame(width: 300, height: 300)
.scaleEffect(1.0 / 3.0)
.frame(width: 100, height: 100)
Text(name)
.font(.caption)
.accessibilityLabel("Badge for \(name).")
}
}
}
struct HikeBadge_Previews: PreviewProvider {
static var previews: some View {
HikeBadge(name: "Preview Testing")
}
}**

ProfileSummary.swift에 다음과 같이 배지 내용과 하이킹 기록을 추가해줍니다.
**// ProfileSummary.swift**
import SwiftUI
struct ProfileSummary: View {
@EnvironmentObject var modelData: ModelData
var profile: Profile
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 10) {
Text(profile.username)
.bold()
.font(.title)
Text("Notifications: \(profile.prefersNotifications ? "On": "Off")")
Text("Seasonal Photos: \(profile.seasonalPhoto.rawValue)")
Text("Goal Date: ") + Text(profile.goalDate, style: .date)
**Divider()
VStack(alignment: .leading) {
Text("Completed Badges")
.font(.headline)
ScrollView(.horizontal) {
HStack {
HikeBadge(name: "First Hike")
HikeBadge(name: "Earth Day")
.hueRotation(Angle(degrees: 90))
HikeBadge(name: "Tenth Hike")
.grayscale(0.5)
.hueRotation(Angle(degrees: 45))
}
.padding(.bottom)
}
}**
**Divider()
VStack(alignment: .leading) {
Text("Recent Hikes")
.font(.headline)
HikeView(hike: modelData.hikes[0])
}**
}
}
}
}
struct ProfileSummary_Previews: PreviewProvider {
static var previews: some View {
ProfileSummary(profile: Profile.default)
**.environmentObject(ModelData())**
}
}

CategoryHome.swift의 내비게이션 바에 유저 프로필 화면을 볼 수 있는 버튼을 추가해줍니다.
.toolbar 모디파이어를 사용합니다.
**// CategoryHome.swift**
import SwiftUI
struct CategoryHome: View {
@EnvironmentObject var modelData: ModelData // ModelData의 environment객체를 만들어준다.
**@State private var showingProfile = false**
var body: some View {
NavigationView {
List {
// 추천 장소
modelData.features[0].image
.resizable()
.scaledToFill()
.frame(height: 200)
.clipped()
.listRowInsets(EdgeInsets()) // 가장자리 모양에 맞춤
// 각 카테고리 별 장소들
ForEach(modelData.categories.keys.sorted(), id: \.self) { key in
CategoryRow(categoryName: key, items: modelData.categories[key]!)
}
.listRowInsets(EdgeInsets())
}
**.listStyle(.inset) // 리스트 스타일 수정**
.navigationTitle("Featured")
**.toolbar {
Button {
showingProfile.toggle()
} label: {
Label("User Profile", systemImage: "person.crop.circle")
}
}
.sheet(isPresented: $showingProfile) {
ProfileHost()
.environmentObject(modelData)
}**
}
}
}
struct CategoryHome_Previews: PreviewProvider {
static var previews: some View {
CategoryHome()
.environmentObject(ModelData())
}
}

유저는 프로필의 세부사항을 보기 화면과 편집 화면으로 전환할 수 있어야 합니다. 이번 섹션에서는 ProfileHost.swift에 EditButton을 추가하여 각각의 값을 편집할 수 있는 뷰를 만들어줍니다.
