suvera-dev ๐Ÿฅฆ

SwiftUI) SwiftUI Essentials - 2 Building Lists and Navigation ๋ณธ๋ฌธ

iOS/SwiftUI

SwiftUI) SwiftUI Essentials - 2 Building Lists and Navigation

suvera 2023. 3. 29. 20:57

SwiftUI ํŠœํ† ๋ฆฌ์–ผ Swift UI Essentials ์ฑ•ํ„ฐ์˜ 2๋ฒˆ์งธ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.


Building Lists and Navigation

- ๊ธฐ๋ณธ ๋žœ๋“œ๋งˆํฌ ์ƒ์„ธ ๋ทฐ๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ ์ „์ฒด ๋ชฉ๋ก๊ณผ ๊ทธ ๊ฐ๊ฐ์˜ ํ•ญ๋ชฉ์— ๋Œ€ํ•œ ์ƒ์„ธ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์ค„๊ฒŒ์š” !
- ๋ชจ๋“  ๋žœ๋“œ ๋งˆํฌ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋Š” ๋ทฐ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ํƒญ ํ•˜์—ฌ ๋žœ๋“œ ๋งˆํฌ์— ๋Œ€ํ•œ ์„ธ๋ถ€ ์‚ฌํ•ญ ๋ทฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์Šคํฌ๋กค ๋ชฉ๋ก์„ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค !

- UIKit์—์„œ TableView๋‚˜ CollectionView๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •์ด๋ž‘ ์œ ์‚ฌํ•œ ๊ฒƒ ๊ฐ™์•„์š” ! 

 

1. ๋žœ๋“œ๋งˆํฌ ๋ชจ๋ธ ๋งŒ๋“ค๊ธฐ !

- ์ด์ „ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ๋ชจ๋“  ์ปค์Šคํ…€ ๋ทฐ์˜ ์ •๋ณด๋ฅผ ํ•˜๋“œ ์ฝ”๋”ฉํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

- ์ด๋ฒˆ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ปค์Šคํ…€๋ทฐ๋กœ ์ „๋‹ฌํ•˜์—ฌ ํ‘œ์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
- ์œ„์— ์ฒจ๋ถ€๋˜์–ด์žˆ๋Š” ์Šคํƒ€ํ„ฐ ํ”„๋กœ์ ํŠธ๋ฅผ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์œผ์‹œ๊ณ  ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๋ฒˆ ๋‘˜๋Ÿฌ๋ณด์„ธ์š”.

 

landmarkData.json
0.00MB

์šฐ์„  ๋ชจ๋ธ์„ ๋งŒ๋“ค์–ด ๋ณผ๊ฒŒ์š” !

1. ์œ„์˜ JSON ํŒŒ์ผ์„ ๋‹ค์šด๋ฐ›์•„์„œ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

2. Landmarks.Swift ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ JSON์˜ ํ‚ค ์ด๋ฆ„๊ณผ ์ผ์น˜ํ•˜๋Š” ๋ช‡ ๊ฐœ์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ •์˜ํ•ด์ฃผ์„ธ์š” !

{
        "name": "Turtle Rock",
        "category": "Featured",
        "city": "Twentynine Palms",
        "state": "California",
        "id": 1001,
        "park": "Joshua Tree National Park",
        "coordinates": {
            "longitude": -116.166868,
            "latitude": 34.011286
        },
        "imageName": "turtlerock"
    }

์š”๊ฒŒ json ๋ฐ์ดํ„ฐ ์ด๊ตฌ์š”.

struct Landmark: Hashable, Codable {
    var id: Int
    var name: String
    var park: String
    var state: String
    var description: String
}

 

๋žœ๋“œ๋งˆํฌ ๋ชจ๋ธ์€ id, name, park, state, description์œผ๋กœ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŒŒ์ผ์—์„œ Data๋ฅผ ์ฝ๊ธฐ์œ„ํ•ด์„  Decodable ํ”„๋กœํ† ์ฝœ์˜ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์—,

Codable ํ”„๋กœํ† ์ฝœ์„ conform ํ•ด์คฌ์Šต๋‹ˆ๋‹ค.

 

Resources.zip
2.51MB

- ๊ทธ๋ฆฌ๊ณ  ์œ„์˜ Resource ํŒŒ์ผ์—์„œ ๋žœ๋“œ๋งˆํฌ ์ด๋ฏธ์ง€๋“ค์„ ์ „๋ถ€ ๋„ฃ์–ด์คฌ์Šต๋‹ˆ๋‹ค.

- ๊ทธ๋‹ค์Œ์— Landmark ๋ชจ๋ธ์— ์ด๋ฏธ์ง€ ๋กœ๋“œํ•˜๋Š” ์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

 

- ์‚ฌ์šฉ์ž๋Š” ์˜ค์ง ์ด๋ฏธ์ง€์—๋งŒ ๊ด€์‹ฌ์ด ์žˆ์œผ๋ฏ€๋กœ, imageName์€ private๋กœ ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค. ( ์ด๋Ÿฐ ๊ฒƒ๊นŒ์ง€ ์„ค๋ช…ํ•ด์ฃผ๋„ค์š” '.')

- ๊ทธ ๋‹ค์Œ์€ ์œ„์น˜ ์ •๋ณด ์ถ”๊ฐ€ ! ๋‚ด๋ถ€์— Coordinates๋ผ๋Š” struct๋ฅผ ํ•˜๋‚˜ ๋” ๋งŒ๋“ค์–ด์ฃผ๊ณ  latitude, longtitude ํ”„๋กœํผํ‹ฐ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

- ์ด๋Š” JSON ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์™€ ์ผ์น˜ํ•˜๋„๋ก ์ค‘์ฒฉ๋œ Coordinates ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด์คฌ์Šต๋‹ˆ๋‹ค. 

- ๊ทธ ๋‹ค์Œ์—๋Š” Mapkit ํ”„๋ ˆ์ž„์›Œํฌ์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š”๋ฐ ์œ ์šฉํ•œ locationCoordinate ๋ผ๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค. ( import CoreLocation ์„ ํ•ด์ค˜์•ผํ•ฉ๋‹ˆ๋‹ค. )

var locationCoordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(
            latitude: coordinates.latitude,
            longitude: coordinates.longitude)
    }

- Mapkit ์—์„œ ์œ„์น˜์ •๋ณด๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” struct์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹น !


[ ModelData.Swift ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ ]

 

- ๋‹ค์Œ์œผ๋กœ, ModelData.Swift ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์ฃผ๊ณ , load(_: ) ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์„ฑํ•ด์คฌ์Šต๋‹ˆ๋‹ค.

- ์ด ๋ฉ”์„œ๋“œ๋Š” main ๋ฒˆ๋“ค์—์„œ JSON ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค !

- load ๋ฉ”์„œ๋“œ๋Š” ๋ฐ˜ํ™˜ ํƒ€์ž…์ด Decodable ํ”„๋กœํ† ์ฝœ์„ conformํ•˜๋Š” ๊ฒƒ์— ์˜์กดํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. 

- ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด, Array ํƒ€์ž…์œผ๋กœ ๋žœ๋“œ๋งˆํฌ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. 

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

- ๋งˆ์ง€๋ง‰์œผ๋กœ ํด๋”๋ง๊นŒ์ง€ ์™„๋ฃŒ ~

 

 

2. ํ–‰์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ทฐ ์ƒ์„ฑ

- ์ฒซ ๋ฒˆ์งธ ๋ทฐ๋Š” ๊ฐ๊ฐ์˜ ๋žœ๋“œ๋งˆํฌ์— ๋Œ€ํ•œ ์ƒ์„ธ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•œ ํ–‰(row)์ž…๋‹ˆ๋‹ค.

- ์ด ํ–‰์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ทฐ๋Š” ๋žœ๋“œ๋งˆํฌ์˜ ํ”„๋กœํผํ‹ฐ ์ •๋ณด๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. 

- ๋‚˜์ค‘์— ์—ฌ๋Ÿฌ ํ–‰์„ ๊ฒฐํ•ฉํ•˜์—ฌ ๋žœ๋“œ๋งˆํฌ ๋ชฉ๋ก์„ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

1) LandmarkRow๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ SwiftUI ๋ทฐ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

2) landmark๋ผ๋Š” ์ €์žฅ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

-> LandmarkRow๋ฅผ ์ดˆ๊ธฐํ™” ์ค‘์— landmark๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ landmark ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ๋ฉˆ์ถฅ๋‹ˆ๋‹ค.

- preview๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด preview provider๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

- LandmarkRow_Previews์˜ static ํ”„๋กœํผํ‹ฐ์—์„œ landmarkData ๋ฐฐ์—ด์˜ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ์ง€์ •ํ•˜์—ฌ Landmark ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ LandmarkRow ์ดˆ๊ธฐํ™” ํ”„๋กœ๊ทธ๋žจ์— ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค.

 

struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkRow(landmark: landmarks[0])
    }
}

 

- ๋‹ค์Œ์œผ๋กœ๋Š” ๋ฐ”๋””์— HStack Embed ํ•ด์ฃผ๊ณ , ํ…์ŠคํŠธ๋Š” landmark.name,  ์œ„์—๋Š” ์ด๋ฏธ์ง€๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

- ์‚ฌ์ด์ฆˆ๋Š” 50 * 50 ์œผ๋กœ ํ•ด์ค๋‹ˆ๋‹ค.

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

            Spacer()
        }
    }

 

3. ํ–‰ ํ”„๋ฆฌ๋ทฐ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•˜๊ธฐ

- Xcode์˜ ์บ”๋ฒ„์Šค๋Š” ํ˜„์žฌ ํŽธ์ง‘๊ธฐ์—์„œ PreviewProvideํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋Š” ํƒ€์ž…์— ๋Œ€ํ•ด์„œ ์ž๋™์œผ๋กœ ์ธ์‹ํ•˜๊ณ  ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

- ๋ฏธ๋ฆฌ ๋ณด๊ธฐ ์ œ๊ณต์ž(preview provide)๋Š” ํฌ๊ธฐ์™€ ์žฅ์น˜๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์˜ต์…˜๊ณผ ํ•จ๊ป˜ ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
- ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ œ๊ณต์ž(preview provide)์—์„œ ๋ฐ˜ํ™˜๋œ ์ฝ˜ํ…์ธ ๋ฅผ ์‚ฌ์šฉ์ž ์ •์˜ํ•˜์—ฌ ๊ฐ€์žฅ ์œ ์šฉํ•œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            LandmarkRow(landmark: landmarks[0])
            LandmarkRow(landmark: landmarks[1])
        }
        .previewLayout(.fixed(width: 300, height: 70))
    }
}

- previewLayout(_:) ์ˆ˜์ •์ž(modifier)๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ชฉ๋ก ์•ˆ์˜ ํ–‰์˜ ๊ทผ์ ‘ํ•œ ํฌ๊ธฐ๋กœ ์„ค์ • ๊ฐ€๋Šฅ

- ๊ทธ๋ฃน์€ ๋ทฐ ์ฝ˜ํ…์ธ ๋ฅผ ๊ทธ๋ฃนํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ปจํ…Œ์ด๋„ˆ์ž…๋‹ˆ๋‹ค.

- Xcode๋Š” ๊ทธ๋ฃน์˜ ํ•˜์œ„ ๋ทฐ๋“ค์„ ์บ”๋ฒ„์Šค์—์„œ ๊ฐ€๊ฐ€ ๋ถ„๋ฆฌ๋œ ํ˜•ํƒœ์˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋กœ ๋ Œ๋”๋ง ํ•ฉ๋‹ˆ๋‹ค.

- ์ฝ”๋“œ๋ฅผ ์ข€ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด previewLayout(_:)์˜ ํ˜ธ์ถœํ•˜๋Š” ์œ„์น˜๋ฅผ ๊ทธ๋ฃน ์™ธ๋ถ€๋กœ ์˜ฎ๊น๋‹ˆ๋‹ค.

- ๊ทธ๋Ÿฌ๋ฉด ์ด๋Ÿฐ์‹์œผ๋กœ ํ”„๋ฆฌ๋ทฐ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์ด ๋ฉ๋‹ˆ๋‹น.

 

 

4. ๋žœ๋“œ๋งˆํฌ ๋ฆฌ์ŠคํŠธ ๋งŒ๋“ค๊ธฐ.

- SwiftUI์˜ ๋ฆฌ์ŠคํŠธ ํƒ€์ž…์„ ์‚ฌ์šฉํ•  ๋•Œ ๋ทฐ์˜ ํ”Œ๋žซํผ๋ณ„ ๋ชฉ๋ก์„ ํ‘œ์‹œํ• ์ˆ˜์žˆ์Šต๋‹ˆ๋‹ค.
- ๋ชฉ๋ก์˜ ์š”์†Œ๋Š” ์ง€๊ธˆ๊นŒ์ง€ ์ƒ์„ฑ ํ•œ ์Šคํƒ์˜ ์ž์‹ ๋ทฐ์™€ ๊ฐ™์ด ์ •์ ์ด๊ฑฐ๋‚˜ ๋™์ ์œผ๋กœ ์ƒ์„ฑ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

- ์ •์  ๋ทฐ์™€ ๋™์ ์œผ๋กœ ์ƒ์„ฑ ๋œ ๋ทฐ๋ฅผ ํ˜ผํ•ฉ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

 

1) LandmarkList๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ SwiftUI ๋ทฐ๋ฅผ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

2) ๊ธฐ๋ณธ ํ…์ŠคํŠธ๋ฅผ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฐ”๊พธ๊ณ  LandmarkRow ์ธ์Šคํ„ด์Šค์— ๋ฆฌ์ŠคํŠธ์˜ ํ•ญ๋ชฉ์œผ๋กœ ์ฒ˜์Œ ๋‘ ๊ฐœ์˜ ๋žœ๋“œ ๋งˆํฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

struct LandmarkList: View {
    var body: some View {
        List {
            LandmarkRow(landmark: landmarks[0])
            LandmarkRow(landmark: landmarks[1])
        }
    }
}

 

 

5. ๋™์ ์œผ๋กœ ๋ฆฌ์ŠคํŠธ ๋งŒ๋“ค๊ธฐ.

- ๋ชฉ๋ก์˜ ์š”์†Œ๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ง€์ •ํ•˜๋Š” ๋Œ€์‹  ์ปฌ๋ ‰์…˜์—์„œ ์ง์ ‘ ํ–‰์„ ์ƒ์„ฑ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
- ๋ฐ์ดํ„ฐ์˜ ์ปฌ๋ ‰์…˜๊ณผ ์ปฌ๋ ‰์…˜์˜ ๊ฐ ์š”์†Œ์— ๋Œ€ํ•œ ๋ทฐ๋ฅผ ์ œ๊ณตํ•˜๋Š” ํด๋กœ์ €๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์ปฌ๋ ‰์…˜์˜ ์š”์†Œ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ชฉ๋ก์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

- ์ œ๊ณต๋œ ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชฉ๋ก์˜ ๊ฐ ์š”์†Œ๋ฅผ ์ž์‹๋ทฐ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

1) ์•ž์—์„œ ๋งŒ๋“  2๊ฐœ์˜ ์ •์  ๋žœ๋“œ๋งˆํฌRow๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ๋Œ€์‹  LandmarkData๋ฅผ List ์ดˆ๊ธฐํ™” ํ”„๋กœ๊ทธ๋žจ์— ์ „๋‹ฌํ•˜์„ธ์š”.

2) ์‹๋ณ„ ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ž‘์—…์„ ๋‚˜์—ดํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์‹๋ณ„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

-> ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ ๊ฐ ์š”์†Œ๋ฅผ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•˜๋Š” ์†์„ฑ์˜ ์ฃผ์š” ๊ฒฝ๋กœ๋ฅผ ์ „๋‹ฌํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ ์œ ํ˜•์ด Identifiable ํ”„๋กœํ† ์ฝœ์„ ์ค€์ˆ˜ํ•˜๊ฒŒํ•ฉ๋‹ˆ๋‹ค.

3) ํด๋กœ์ €์—์„œ LandmarkRow๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๋™์ ์œผ๋กœ ์ƒ์„ฑ ๋œ ๋ชฉ๋ก์„ ์™„์„ฑํ•˜์‹ญ์‹œ์˜ค.
-> ์ด๊ฒƒ์€ landmarkData ๋ฐฐ์—ด์˜ ๊ฐ ์š”์†Œ์— ๋Œ€ํ•ด ํ•˜๋‚˜์˜ LandmarkRow๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

struct LandmarkList: View {
    var body: some View {
        List(landmarkData, id: \.id) { landmark in
            LandmarkRow(landmark: landmark)
        }
    }
}

4) Landmark ๋ชจ๋ธ์— Identifiable ํ”„๋กœํ† ์ฝœ ์ฑ„ํƒ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”. + Landmark ํƒ€์ž…์—๋Š” ์ด๋ฏธ Identifiable ํ”„๋กœํ† ์ฝœ์— ํ•„์š”ํ•œ id ์†์„ฑ์ด ์žˆ์Œ !

5) ์ด์ œ Landmark List์—์„œ id ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”.

struct LandmarkList: View {
    var body: some View {
        List(landmarks) { landmark in
            LandmarkRow(landmark: landmark)
        }
    }
}

- ์š”๋ ‡๊ฒŒ ์™„์„ฑ.

 

6. ๋„ค๋น„๊ฒŒ์ด์…˜ ์„ค์ • ๋ฐ ์ƒ์„ธ ํŽ˜์ด์ง€

- ๋ชฉ๋ก์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ Œ๋”๋ง๋˜์ง€๋งŒ ๊ฐœ๋ณ„ ๋žœ๋“œ ๋งˆํฌ๋ฅผ ํƒญํ•˜์—ฌ ํ•ด๋‹น ๋žœ๋“œ ๋งˆํฌ์˜ ์„ธ๋ถ€ ์‚ฌํ•ญ ํŽ˜์ด์ง€๋ฅผ ์•„์ง ๋ณผ ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.
- NavigationView์— ํƒ์ƒ‰ ๊ธฐ๋Šฅ์„ ํฌํ•จ์‹œํ‚จ ๋‹ค์Œ NavigationLink์— ๊ฐ ํ–‰์„ ์ค‘์ฒฉํ•˜์—ฌ ํƒ์ƒ‰ ๊ธฐ๋Šฅ์„ ๋ชฉ๋ก์— ์ถ”๊ฐ€ํ•˜์—ฌ ๋Œ€์ƒ ๋ทฐ๋กœ์˜ ์ „ํ™˜์„ ์„ค์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

1) ์ƒ์„ธ๋ณด๊ธฐ ๋ทฐ๋ฅผ ๋งŒ๋“ค๊ฑฐ์—์š”. LandmarkDetail.Swift ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

2) ContentView ๋‚ด์šฉ์„ copyํ•ด์„œ body์— ๋„ฃ์–ด์ฃผ์„ธ์š”.

3) ๊ทธ๋ฆฌ๊ณ  ContentView์˜ body์—๋Š” LandmarkList()๋ฅผ ๋„ฃ์–ด์ฃผ์„ธ์š”.

4) LandmarkList์—์„œ ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ ๋ฆฌ์ŠคํŠธ๋ฅผ NavigationView๋กœ ๊ฐ์‹ธ์ฃผ์„ธ์š” !

5) Navigation ํƒ€์ดํ‹€๋„ ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค

6) ๋ฆฌ์ŠคํŠธ ํด๋กœ์ €์•ˆ์—์„œ LandmarkDetail ๋ทฐ๋ฅผ ๋ชฉ์ ์ง€๋กœ ํ•˜์—ฌ ๋ฐ˜ํ™˜ ๋œ ํ–‰์„ NavigationLink์— ๋ž˜ํ•‘ํ•ฉ๋‹ˆ๋‹ค.

struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarks) { landmark in
                NavigationLink {
                    LandmarkDetail()
                } label: {
                    LandmarkRow(landmark: landmark)
                }
            }
        }
        .navigationTitle("Landmarks")
    }
}

+ ํ”„๋ฆฌ๋ทฐ๋ฅผ ๋ผ์ด๋ธŒ ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•˜๋ฉด ๋„ค๋น„๊ฒŒ์ด์…˜์„ ํด๋ฆญํ•ด์„œ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. -> ๋ฐ”๋กœ ๋””ํ…Œ์ผ ํŽ˜์ด์ง€๋กœ ์—ฐ๊ฒฐ์ด ๋ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€ ์™„์„ฑ ~

 

7. ์ž์‹ ๋ทฐ์—๊ฒŒ ๋ฐ์ดํ„ฐ ์ „๋‹ฌํ•˜๊ธฐ

- LandmarkDetail๋ทฐ๋Š” ์—ฌ์ „ํžˆ ํ•˜๋“œ ์ฝ”๋”ฉ๋œ ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋žœ๋“œ๋งˆํฌ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

- LandmarkRow์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ LandmarkDetail ํƒ€์ž…์œผ๋กœ ๊ตฌ์„ฑ๋œ ๋ทฐ๋Š” ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ landmark ํ”„๋กœํผํ‹ฐ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
- ์ž์‹๋ทฐ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์—ฌ CircleImage, MapView, LandmarkDetail์„ ๋ณ€ํ™˜ํ•˜์—ฌ ๊ฐ ํ–‰์„ ํ•˜๋“œ ์ฝ”๋”ฉํ•˜๋Š” ๋Œ€์‹  ์ „๋‹ฌ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

 

1) ์•ž์—์„œ ๋งŒ๋“ค์—ˆ๋˜ CircleImage.swift์—์„œ ์ €์žฅ ์ด๋ฏธ์ง€(image) ํ”„๋กœํผํ‹ฐ๋ฅผ CircleImage์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. 

2) Turtle Rock์˜ ์ด๋ฏธ์ง€๋ฅผ ์ „๋‹ฌํ•˜๋„๋ก Prview provider๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์„ธ์š”.

struct CircleImage: View {
    var image: Image

    var body: some View {
        image
            .clipShape(Circle())
            .overlay {
                Circle().stroke(.white, lineWidth: 4)
            }
            .shadow(radius: 7)
    }
}

struct CircleImage_Previews: PreviewProvider {
    static var previews: some View {
        CircleImage(image: Image("turtlerock"))
    }
}

3) MapView.swift์—์„œ ์ขŒํ‘œ ์†์„ฑ์„ MapView์— ์ถ”๊ฐ€ํ•˜๊ณ  Prview provider์— ๊ณ ์ •์ขŒํ‘œ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜์„ธ์š”.

4) ์ขŒํ‘œ๊ฐ’์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ง€์—ญ์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€. - setRegion

5) onAppear๋ผ๋Š” ํ˜„์žฌ ์ขŒํ‘œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์œ„์น˜ ๊ณ„์‚ฐ์„ ํŠธ๋ฆฌ๊ฑฐ ํ•˜๋Š” view modifier๋ฅผ ์ถ”๊ฐ€.

 

struct MapView: View {
    var coordinate: CLLocationCoordinate2D

    @State private var region = MKCoordinateRegion()

    var body: some View {
        Map(coordinateRegion: $region)
            .onAppear {
                setRegion(coordinate)
            }
    }

    private func setRegion(_ coordinate: CLLocationCoordinate2D) {
        region = MKCoordinateRegion(
            center: coordinate,
            span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
        )
    }
}

struct MapView_Previews: PreviewProvider {
    static var previews: some View {
        MapView(coordinate: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868))
    }
}

 

6) LandmarkDetail๋กœ ๊ฐ€์„œ, Landmark property๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

7) ๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์„ ์œ„ํ•ด์„œ LandmarkList ์™€ ์—ฐ๊ฒฐ๋œ ๋ถ€๋ถ„์— ํ˜„์žฌ landmark๋ฅผ ๋ชฉ์ ์ง€๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค !

struct LandmarkDetail: View {
    var landmark: Landmark
    //...
}

struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarks) { landmark in
                NavigationLink {
                    LandmarkDetail(landmark: landmark)
                } label: {
                    LandmarkRow(landmark: landmark)
                }
            }
            .navigationTitle("Landmarks")
        }
    }
}

 

8) ์ด์ œ LandmarkDetail์—์„œ ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฐ’๋“ค์„ ์ „๋‹ฌํ•ด์ค์‹œ๋‹ค !

9) ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์„ค๋ช…์„ ์Šคํฌ๋กค ํ•  ์ˆ˜ ์žˆ๋„๋ก VStack์„ ScrollView๋กœ ๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค. + ํ•„์š”์—†๋Š” Spacer ์‚ญ์ œ

10 ) ๋งˆ์ง€๋ง‰์œผ๋กœ, navigationTitle์„ ๋žœ๋“œ๋งˆํฌ ์ด๋ฆ„์œผ๋กœ ์„ค์ •ํ•ด์ฃผ๊ณ ,

navigationBarTitleDisplayMode๋ฅผ inline์œผ๋กœ ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค.

 

struct LandmarkDetail: View {
    var landmark: Landmark

    var body: some View {
        ScrollView {
            MapView(coordinate: landmark.locationCoordinate)
                .ignoresSafeArea(edges: .top)
                .frame(height: 300)

            CircleImage(image: landmark.image)
                .offset(y: -130)
                .padding(.bottom, -130)

            VStack(alignment: .leading) {
                Text(landmark.name)
                    .font(.title)
                HStack {
                    Text(landmark.park)
                        .font(.subheadline)
                    Spacer()
                    Text(landmark.state)
                        .font(.subheadline)
                }
                .font(.subheadline)
                .foregroundColor(.secondary)

                Divider()

                Text("About \(landmark.name)")
                    .font(.title2)
                Text(landmark.description)
            }
            .padding()
        }
        .navigationTitle(landmark.name)
        .navigationBarTitleDisplayMode(.inline)
    }
}

 

8. Previews ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๊ธฐ.

- ๋‹ค์Œ์œผ๋กœ LandmarkList_Previews preview provider์— ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋‹ค์–‘ํ•œ ์žฅ์น˜ ํฌ๊ธฐ์—์„œ ๋ชฉ๋ก๋ณด๊ธฐ์˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

- ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” active scheme ์—์„œ ์žฅ์น˜์˜ ํฌ๊ธฐ๋กœ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.

- previewDevice (_ :) modifier ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์žฅ์น˜๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

1) ํ˜„์žฌ ๋ชฉ๋ก ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ iPhone SE ํฌ๊ธฐ๋กœ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. Xcode์˜ scheme ๋ฉ”๋‰ด์— ํ‘œ์‹œ๋˜๋Š” ๋ชจ๋“  ์žฅ์น˜์˜ ์ด๋ฆ„์„ ์ œ๊ณต ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
            .previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
    }
}

 

 

2) ForEach๋กœ ์—ฌ๋Ÿฌ ๊ธฐ๊ธฐ๋ฅผ ํ•œ๊บผ๋ฒˆ์— ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

- ๋ชฉ๋ก ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋‚ด์—์„œ ์žฅ์น˜ ์ด๋ฆ„ ๋ฐฐ์—ด์„ ๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์šฉํ•˜์—ฌ LandmarkList๋ฅผ ForEach ์ธ์Šคํ„ด์Šค์— ํฌํ•จ์‹œํ‚ต๋‹ˆ๋‹ค. 
ForEach๋Š” ๋ฆฌ์ŠคํŠธ์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ปฌ๋ ‰์…˜์—์„œ ์ž‘๋™ํ•˜๋ฏ€๋กœ ์Šคํƒ, ๋ชฉ๋ก, ๊ทธ๋ฃน ๋“ฑ์˜ ํ•˜์œ„ ๋ทฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜์žˆ๋Š” ๋ชจ๋“  ์œ„์น˜์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ์š”์†Œ๊ฐ€ ์—ฌ๊ธฐ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฌธ์ž์—ด๊ณผ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ ๊ฐ’ ์œ ํ˜• ์ธ ๊ฒฝ์šฐ \ .self๋ฅผ ์‹๋ณ„์ž์˜ ํ‚ค ๊ฒฝ๋กœ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

3) previewDisplayName (_ :) ์ˆ˜์ •์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์žฅ์น˜ ์ด๋ฆ„์„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์˜ ๋ ˆ์ด๋ธ”๋กœ ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค. 

4) ์บ”๋ฒ„์Šค์—์„œ ๋ชจ๋“  ์žฅ์น˜๋“ค์„ ๋น„๊ตํ•˜๊ณ  ๋ Œ๋”๋ง์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹น !

 

struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        ForEach(["iPhone SE (3rd generation)", "iPhone 14"], id: \.self) { deviceName in
            LandmarkList()
                .previewDevice(PreviewDevice(rawValue: deviceName))
                .previewDisplayName(deviceName)
        }
    }
}

 

 

์—ฌ๊ธฐ๊นŒ์ง€ ~ ์ด๋ฒˆ ์‹œ๊ฐ„์—๋Š” ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์› ๋Š”๋ฐ์š”.

์ƒ๊ฐ๋ณด๋‹ค ์žฌ๋ฐŒ๋„ค์š” ์Šค์œ  ใ…‹ใ…Žใ…‹ใ…Ž

'iOS > SwiftUI' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

SwiftUI) Drawing and Animation 1 - Drawing Paths and Shapes  (0) 2023.04.02
SwiftUI) SwiftUI Essentials 3 - Handling User Input  (4) 2023.03.31
SwiftUI) SwiftUI Essentials  (8) 2023.03.29
Comments