DEV_Larva

SwiftUI Tutorials - Landmarks(6) 본문

SwiftUI & UIKit/Tutorials

SwiftUI Tutorials - Landmarks(6)

NelchuPapa 2022. 11. 1. 23:14
반응형

이번 시간에는 랜드 마크의 카테고리 view에 수평으로 스크롤되는 랜드마크에 수직 스크롤 목록이 표시되게끔 해볼 것이다. 한마디로 수직으로 스크롤도 되는 페이지에 수평으로 스크롤이 되는 화면을 구성한다는 것이다. 바로 시작해보자. 

 


1.  Add a Category View

카테고리별로 랜드마크를 정렬하는 View를 만들고 해당 뷰 상단에서 주요 랜드마크를 강조 표시 해서 랜드마크를 탐색하는 다른 방법을 제공해보자. 

 

 


 

CategoryHome이라는 새로운 파일 하나를 만들고, 다양한 카테고리를 호스팅 하기 위해 NavigationView를 추가한다. 그리고. navigationTitle을 이용하여 내비게이션의 이름을 정해주자. 

 

import SwiftUI

struct CategoryHome: View {
    var body: some View {
        NavigationView {
            Text("Hello, World!")
                .navigationTitle("Featured")
        }
    }
}

struct CategoryHome_Previews: PreviewProvider {
    static var previews: some View {
        CategoryHome()
    }
}

 

 


 

2. Create a Category List

카테고리 뷰를 더 쉽게 탐색하기 위해 수직 열에 정렬된 별도의 행에 모든 카테고리를 표시해보자. 여기서는 수직, 수평 스택을 결합하고 리스트에 스크롤을 추가하게 된다. 


Landmark 파일에서 stuct부분에 카테고리에 대한 속성을 추가해주자.

    var category: Category
    enum Category: String, CaseIterable, Codable {
        case lakes = "Lakes"
        case rivers = "Rivers"
        case mountains = "Mountains"
    }

 

ModelData 파일에서는 카테고리 이름을 키로 사용하여 계산된 카테고리 이름을 추가하고 각 키에 연결된 랜드마크 배열을 추가한다. CategoryHome 파일에서 modelData 에 대한 @EnvironmenObject var(환경 객체)를 생성한다. 마지막으로 리스트를 사용해 랜드마크의 카테고리를 출력해준다. 

//ModelData

import Foundation
import Combine

final class ModelData: ObservableObject {
    @Published var landmarks: [Landmark] = load("landmarkData.json")
    var hikes: [Hike] = load("hikeData.json")

    var categories: [String: [Landmark]] {
        Dictionary(
            grouping: landmarks,
            by: { $0.category.rawValue }
        )
    }
}



//CategoryHome
    
import SwiftUI

struct CategoryHome: View {
    @EnvironmentObject var modelData: ModelData

    var body: some View {
        NavigationView {
            List {
                ForEach(modelData.categories.keys.sorted(), id: \.self) { key in
                    Text(key)
                }
            }
            .navigationTitle("Featured")
        }
    }
}

struct CategoryHome_Previews: PreviewProvider {
    static var previews: some View {
        CategoryHome()
            .environmentObject(ModelData())
    }
}

 

 


3. Create a Category Row

랜드마크는 가로로 스크롤되는 행에 각 카테고리를 표시한다. 행을 나타내는 새로운 View를 추가한 다음 새로운 View에서 해당 카테고리에 대한 모든 랜드마크를 표시하게된다. 


행의 내용을 저장하기 위한 새로운 사용자 정의 View인 CategoryRow 파일을 만들자. 다음으로 내부에 카테고리 이름 및 해당 카테고리의 항목 리스트에 대한 속성을 추가해주고, 카테고리의 항목을 HStack에 넣고 카테고리 이름으로 VStack에 그룹화를 해준다. 그리고 tallframe을 지정하고 패딩을 추가하여 스크롤 보기에서 HStack을 래핑 하여 콘텐츠에 공간을 만들어준다. 

 

//CategoryRow

import SwiftUI

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
                        Text(landmark.name)
                    }
                }
            }
            .frame(height: 185)
        }
    }
}

struct CategoryRow_Previews: PreviewProvider {
    static var landmarks = ModelData().landmarks

    static var previews: some View {
        CategoryRow(
            categoryName: landmarks[0].category.rawValue,
            items: Array(landmarks.prefix(4))
        )
    }
}

 

CategoryItem이라는 새로운 파일을 생성해주고 CategoryRow파일에서 랜드마크 이름을 담고 있는 Text를 새로운 CategoryItem뷰로 교체를 해줄것이다. (Text(landmark.name) --->> CategoryItem(landmark:landmark)로 바꿔주면 된다. 

 

//CategoryItem.swift

import SwiftUI

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)
    }
}

struct CategoryItem_Previews: PreviewProvider {
    static var previews: some View {
        CategoryItem(landmark: ModelData().landmarks[0])
    }
}

 

여기까지 진행한 결과 프리뷰는 다음과 같다.

 


4. Complete the Category View

카테고리 홈 페이지에 행과 추천 이미지를 추가 해준다.


 

 

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가 참으로 설정된 랜드마크만 보일 수 있도록 새로운 계산 기능 배열을 추가한다. 

 var features: [Landmark] {
        landmarks.filter { $0.isFeatured }
 }

CategoryHome에서 첫 번째 추천 랜드마크의 이미지를 목록 첫 번째에 추가한다. 그리고 콘텐츠가 화면의 가장자리까지 확장될 수 있게 두 종류의 랜드마크 프리뷰에서 EdgeInsets를 0으로 설정한다. 또한 행 유형의 인스턴스에 카테고리 정보를 전달해준다.

 

struct CategoryHome: View {
    @EnvironmentObject var modelData: ModelData

    var body: some View {
        NavigationView {
            List {
                modelData.features[0].image //첫번째 추천 랜드마크 이미지
                    .resizable()
                    .scaledToFill()
                    .frame(height: 200)
                    .clipped()
                    .listRowInsets(EdgeInsets())//0삽입

                ForEach(modelData.categories.keys.sorted(), id: \.self) { key in
                    CategoryRow(categoryName: key, items: modelData.categories[key]!)
                    //인스턴스에 카테고리 정보 전달
                }
                .listRowInsets(EdgeInsets())//0삽입
            }
            .navigationTitle("Featured")
        }
    }
}

 

 


5.  Add Navigation Between Sections

다르게 분류된 모든 랜드마크가 뷰에 표시되므로 사용자는 앱의 각 섹션에 도달할 수 있는 방법이 필요해진다. 여기서 내비게이션 및 프레젠테이션 API를 이용하여 탭 view에서 카테고리 홈, 상세 view 및 즐겨찾기 목록을 탐색할 수 있다. 

 


CategoryRow파일에서 기존 CategoryItem파일을 NavigationLink로 래핑 한다.

 HStack(alignment: .top, spacing: 0) {
               ForEach(items) { landmark in
                    NavigationLink {
                         LandmarkDetail(landmark: landmark)
                    } label: {
                            CategoryItem(landmark: landmark)
                    }

 

 

CategoryItem파일에서 renderingMode, foregroundColor 수정자를 적용해서 카테고리 항목의 탐색 모양을 변경 해주자.

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)
                .foregroundColor(.primary) //변경사항
                .font(.caption)
        }
        .padding(.leading, 15)
    }
}

 

다음으로 사용자가 방금 만든 카테고리 View와 기존 랜드마크 목록 중에서 선택할 수 있는 탭 View를 표시하여 앱의 기본 ContentView를 수정할 것이다. ContentView에서 표시할 탭의 케이스를 추가해준다. 그리고 LandmarkList와 새로운 CategoryHome을 래핑 하는 TabView를 하나 만들어준다. 마지막으로 탭에 라벨을 지정해주면 끝나게 된다. 

 

import SwiftUI

struct ContentView: View {
    @State private var selection: Tab = .featured // 탭 선택에 대한 상태 변수 추가 및 기본값 지정

    enum Tab {  //탭의 열거 추가
        case featured  
        case list
    }

    var body: some View {
        TabView(selection: $selection) {     //래핑하는 TabView 생성
            CategoryHome()
                .tabItem {
                    Label("Featured", systemImage: "star")
                }
                .tag(Tab.featured)

            LandmarkList()
                .tabItem {
                    Label("List", systemImage: "list.bullet")
                }
                .tag(Tab.list)
        }
    }
}

 

 


이번 섹션에서는 앱의 전체적인 디자인과 레이아웃에 많은 수정이 있었다. 특히 복잡한 인터페이스 구성으로 전보다 개발하는 과정은 많이 어려웠지만, 훨씬 고퀄리티의 앱이 만들어지고 있는 과정을 확인할 수 있었다. 나의 경우 기존에 있던 코드들에서 새롭게 추가된 코드나 파일 간의 상관관계를 찾는 과정에서 더욱 시간이 걸렸던 것 같다. 이제 정말 마무리가 되어가고 있다. 끝까지 마무리해보자.

반응형

'SwiftUI & UIKit > Tutorials' 카테고리의 다른 글

SwiftUI Tutorials - Landmarks(7)  (0) 2022.11.08
SwiftUI Tutorials - Landmarks(5)  (0) 2022.10.29
SwiftUI Tutorials - Landmarks(4)  (0) 2022.10.19
SwiftUI Tutorials - Landmarks(3)  (0) 2022.10.15
SwiftUI Tutorials - Landmarks(2)  (0) 2022.10.08