1. Add a category view
import SwiftUI
struct CategoryHome: View {
var body: some View {
NavigationSplitView {
Text("Hello, World!")
.navigationTitle("Featured")
} detail: {
Text("Select a Landmark")
}
}
}
2. Create a category list
Landmark 구조체에 Category enum과 프로퍼티를 추가한다.
struct Landmark: Hashable, Codable, Identifiable {
var id: Int
var name: String
var park: String
var state: String
var description: String
var isFavorite: Bool
var category: Category
enum Category: String, CaseIterable, Codable {
case lakes = "Lakes"
case rivers = "Rivers"
case mountains = "Mountains"
}
private var imageName: String
var image: Image {
Image (imageName)
}
private var coordinates: Coordinates
var locationCoordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: coordinates.latitude,
longitude: coordinates.longitude)
}
struct Coordinates: Hashable, Codable {
var latitude: Double
var longitude: Double
}
}
ModelData에도 catetories 프로퍼티를 추가
@Observable
class ModelData {
var landmarks: [Landmark] = load("landmarkData.json")
var hikes: [Hike] = load("hikeData.json")
var categories: [String: [Landmark]] {
Dictionary(
grouping: landmarks,
by: { $0.category.rawValue }
)
}
}
CategoryHome 에 @Environment 으로 ModelData에 접근, List 로 뿌린다.
struct CategoryHome: View {
@Environment(ModelData.self) var modelData
var body: some View {
NavigationSplitView {
List {
ForEach(modelData.categories.keys.sorted(), id: \.self) { key in
Text(key)
}
}
.navigationTitle("Featured")
} detail: {
Text("Select a Landmark")
}
}
}
3. Create a category row
CategoryItem
struct CategoryItem: View {
var landmark: Landmark
var body: some View {
VStack(alignment: .leading) {
landmark.image
.resizable()
.frame(width: 155, height: 155)
.cornerRadius(5)
Text(landmark.name)
.font(.caption)
}
.padding(.leading, 15)
}
}
CategoryRow
struct CategoryRow: View {
var categoryName: String
var items: [Landmark]
var body: some View {
VStack(alignment: .leading) {
Text(categoryName)
.font(.headline)
.padding(.leading, 15)
.padding(.top, 5)
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top, spacing: 0) {
ForEach(items) { landmark in
CategoryItem(landmark: landmark)
}
}
}
.frame(height: 185)
}
}
}
4. Complete the category view
CategoryHome에서 CategoryRow를 선언
struct CategoryHome: View {
@Environment(ModelData.self) var modelData
var body: some View {
NavigationSplitView {
List {
ForEach(modelData.categories.keys.sorted(), id: \.self) { key in
CategoryRow(categoryName: key, items: modelData.categories[key]!)
}
}
.navigationTitle("Featured")
} detail: {
Text("Select a Landmark")
}
}
}
Landmark 구조체에 isFeatured 프로퍼티를 추가
struct Landmark: Hashable, Codable, Identifiable {
var id: Int
var name: String
var park: String
var state: String
var description: String
var isFavorite: Bool
var isFeatured: Bool
ModelData 에 isFeatured 가 true인 Landmark를 담는 computed property, features추가
@Observable
class ModelData {
var landmarks: [Landmark] = load("landmarkData.json")
var hikes: [Hike] = load("hikeData.json")
var features: [Landmark] {
landmarks.filter { $0.isFeatured }
}
var categories: [String: [Landmark]] {
Dictionary(
grouping: landmarks,
by: { $0.category.rawValue }
)
}
}
CategoryHome 에서 ModelData 의 features 첫번째 Landmark 의 이미지 표시
struct CategoryHome: View {
@Environment(ModelData.self) var modelData
var body: some View {
NavigationSplitView {
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())
}
.navigationTitle("Featured")
} detail: {
Text("Select a Landmark")
}
}
}
5. Add naviation between sections
CategoryItem 에 링크를 추가한다.
struct CategoryRow: View {
var categoryName: String
var items: [Landmark]
var body: some View {
VStack(alignment: .leading) {
Text(categoryName)
.font(.headline)
.padding(.leading, 15)
.padding(.top, 5)
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top, spacing: 0) {
ForEach(items) { landmark in
NavigationLink {
LandmarkDetail(landmark: landmark)
} label: {
CategoryItem(landmark: landmark)
}
}
}
}
.frame(height: 185)
}
}
}
CategoryItem 뷰에 내에 Image, Text에 renderingMode, foregroundStyle modifier를 추가한다.
struct CategoryItem: View {
var landmark: Landmark
var body: some View {
VStack(alignment: .leading) {
landmark.image
.renderingMode(.original)
.resizable()
.frame(width: 155, height: 155)
.cornerRadius(5)
Text(landmark.name)
.foregroundStyle(.primary)
.font(.caption)
}
.padding(.leading, 15)
}
}
ContentView 에 Tabview를 선언하고 CategoryHome과 LandmarkList를 포함시킨다
struct ContentView: View {
@State private var selection: Tab = .featured
enum Tab {
case featured
case list
}
var body: some View {
TabView(selection: $selection) {
CategoryHome()
.tabItem {
Label("Featured", systemImage: "star")
}
.tag(Tab.featured)
LandmarkList()
.tabItem {
Label("List", systemImage: "list.bullet")
}
.tag(Tab.list)
}
}
}
'iOS > SwiftUI Tutorial' 카테고리의 다른 글
SwiftUI Tutorial 8 - Interfacing with UIKit (0) | 2024.07.10 |
---|---|
SwiftUI Tutorial 6 - Animating views and transitions (1) | 2024.07.08 |
SwiftUI Tutorial 5- Drawing paths and shapes (0) | 2024.07.07 |
SwiftUI Tutorial 4 - Handling user input (0) | 2024.07.06 |
SwiftUI Tutorial 3 - Building lists and navigation (0) | 2024.07.05 |