랜드마크 상세 뷰가 세팅되었으니, 랜드마크의 전체 리스트를 제공하는 방법을 제공해보자! 모든 랜드마크에 대한 정보를 표시할 수 있는 뷰를 만들고 사용자가 랜드마크에 대한 상세 뷰를 보기 위해 누를 수 있는 스크롤 목록을 동적으로 생성한 후, UI를 미세 조정하기 위해 Xcode의 canvas 를 사용하여 다양한 장치 크기에서 여러 preview 를 렌더링해보자.

Section 1. Create a Landmark Model

첫 스텝으로, 모든 뷰의 정보의 하드코딩을 진행하자. 이때 뷰에 전달할 수 있는 데이터 저장 모델을 생성한다.

스크린샷 2023-05-02 오후 5.51.34.png

Landmark.swift

struct Landmark: Hashable, Codable {
    var id: Int
    var name: String
    var park: String
    var state: String
    var description: String
    
    private var imageName: String // 사용자가 관심 없는 부분은 private 처리
    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
    }
}

landmarkData.json 데이터 파일의 일부 키와 일치하는 프로퍼티로 타입 정의

ModelData.swift

var landmarks: [Landmark] = load("landmarkData.json")

func load<T: Decodable>(_ filename: String) -> T {
    let data: Data
    
    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
    else {
        fatalError("Couldn't find \\(filename) in main bundle.")
    }
    
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \\(filename) from main bundle:\\n\\(error)")
    }
    
    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \\(filename) as \\(T.self):\\n\\(error)")
    }
}

파일의 랜드마크로 초기화 된 배열 생성

파일 그룹화

스크린샷 2023-05-02 오후 5.36.27.png

Section 2. Create the Row View

각 랜드마크에 대한 세부 정보를 보여주는 row 뷰를 만들어보자. row 뷰는 랜드마크에서 보여줘야 할 정보를 프로퍼티로 가지고 있기 때문에 어떤 랜드마크든 하나의 뷰로 표현할 수 있다. 이후 여러 row 뷰를 랜드마크 리스트로 결합시켜보자.

스크린샷 2023-05-02 오후 10.47.05.png

LandmarkRow.swift

struct LandmarkRow: View {
    var landmark: Landmark
    
    var body: some View {
        HStack {
            landmark.image
                .resizable()
                .frame(width: 50, height: 50)
            Text(landmark.name)
            
            Spacer()
        }
    }
}