suvera-dev ๐Ÿฅฆ

SwiftUI) SwiftUI Essentials 3 - Handling User Input ๋ณธ๋ฌธ

iOS/SwiftUI

SwiftUI) SwiftUI Essentials 3 - Handling User Input

suvera 2023. 3. 31. 15:13

 

 

Handling User Input | Apple Developer Documentation

In the Landmarks app, a user can flag their favorite places, and filter the list to show just their favorites. To create this feature, you’ll start by adding a switch to the list so users can focus on just their favorites, and then you’ll add a star-sh

developer.apple.com

์Šค์œ  ํŠœํ† ๋ฆฌ์–ผ 3๋ฒˆ์งธ ์ž…๋‹ˆ๋‹ค !

 

Landmarks ์•ฑ์—์„œ ์‚ฌ์šฉ์ž๋Š” ์ฆ๊ฒจ์ฐพ๋Š” ์žฅ์†Œ๋ฅผ ํ‘œ์‹œํ•˜๊ณ , ๊ทธ ๋ชฉ๋ก์„ ํ•„ํ„ฐ๋งํ•˜์—ฌ ์ฆ๊ฒจ์ฐพ๊ธฐ๋งŒ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๋จผ์ € ์‚ฌ์šฉ์ž๊ฐ€ ์ฆ๊ฒจ์ฐพ๊ธฐ์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋Š” ์Šค์œ„์น˜๋ฅผ ๋ชฉ๋ก์— ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ,

์‚ฌ์šฉ์ž๊ฐ€ ์ฆ๊ฒจ์ฐพ๋Š” landmark๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด ๋ณ„ ๋ชจ์–‘์˜ ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 


 

1. ์‚ฌ์šฉ์ž์˜ ์ฆ๊ฒจ์ฐพ๊ธฐ ํ‘œ์‹œํ•˜๊ธฐ 

์‚ฌ์šฉ์ž๊ฐ€ ํ•œ๋ˆˆ์— ์ž์‹ ์˜ ์ฆ๊ฒจ์ฐพ๋Š” ์žฅ์†Œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๋ชฉ๋ก์„ ๊ฐœ์„ ํ•˜๋Š” ๊ฒƒ์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

Landmark ๊ตฌ์กฐ์ฒด์— ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋žœ๋“œ๋งˆํฌ์˜ ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์ฆ๊ฒจ์ฐพ๋Š” ์žฅ์†Œ๋กœ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ณ ,

์ฆ๊ฒจ์ฐพ๋Š” landmark๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฐ LandmarkRow์— ๋ณ„ํ‘œ๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

 


 

1. isFavorite ๋ผ๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ Landmark ๋ชจ๋ธ์— ์ถ”๊ฐ€ํ•ด์ฃผ๊ธฐ.

( landmarkData.json ํŒŒ์ผ ์•ˆ์— ๊ฐ™์€ ์ด๋ฆ„์˜ ํ‚ค๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ, Codable ํ”„๋กœํ† ์ฝœ ์ฑ„ํƒ์„ ํ†ตํ•ด์„œ ํ•ด๋‹น ํ‚ค์™€ ์—ฐ๊ด€๋œ ๊ฐ’์„ ์–ป์„ ์ˆ˜ ์žˆ์–ด์š”.)

2. LandmarkRow์— isFavorite ๊ฐ’์ด true ์ผ๋•Œ ๊ฝ‰์ฐฌ ๋ณ„ ๋ชจ์–‘ ์ด๋ฏธ์ง€๋ฅผ ์ถ”๊ฐ€. ์ƒ‰์ƒ์€ yellow

 

// Landmark ๋ชจ๋ธ์— ์•„๋ž˜ ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€
var isFavorite: Bool

// SwiftUI ๋ธ”๋ก์—์„œ๋Š” if ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋ทฐ๋ฅผ ์กฐ๊ฑด๋ถ€๋กœ ํฌํ•จ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
if landmark.isFavorite {
   Image(systemName: "star.fill")
        .foregroundColor(.yellow)
}

 

2. ๋ฆฌ์ŠคํŠธ ๋ทฐ ํ•„ํ„ฐ๋งํ•˜๊ธฐ

๋ชจ๋“  landmark ๋˜๋Š” ์‚ฌ์šฉ์ž์˜ ์ฆ๊ฒจ์ฐพ๊ธฐ๋งŒ ํ‘œ์‹œํ•˜๋„๋ก ๋ชฉ๋ก ๋ณด๊ธฐ๋ฅผ ์‚ฌ์šฉ์ž ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด LandmarkList ํƒ€์ž…์— ์•ฝ๊ฐ„์˜ state๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

State๋Š” ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋Š” ๊ฐ’ ๋˜๋Š” ๊ฐ’ ์ง‘ํ•ฉ์ด๋ฉฐ, ๋ทฐ์˜ ๋™์ž‘, ์ฝ˜ํ…์ธ  ๋˜๋Š” ๋ ˆ์ด์•„์›ƒ์— ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.

๋ทฐ์— state๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด @State ์†์„ฑ์„ ๊ฐ€์ง„ ์†์„ฑ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 


 

1. ๋žœ๋“œ๋งˆํฌ ๋ฆฌ์ŠคํŠธ ๋ทฐ์— false๋กœ ์ดˆ๊ธฐ๊ฐ’์ด ์„ค์ •๋œ showFavoritesOnly๋ผ๋Š” @State ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

2. ๋žœ๋“œ๋งˆํฌ ๋ฆฌ์ŠคํŠธ์˜ showFavoritesOnly ํ”„๋กœํผํ‹ฐ ๋ฐ ๊ฐ landmark.isFavorite ๊ฐ’์„ ํ™•์ธํ•˜์—ฌ ํ•„ํ„ฐ๋ง๋œ ๋ฒ„์ „์˜ landmarks ๋ชฉ๋ก์„ ๊ณ„์‚ฐํ•˜์„ธ์š”.

    @State private var showFavoritesOnly = false

    var filteredLandMarks: [Landmark] {
        landmarks.filter { landmark in
            (!showFavoritesOnly || landmark.isFavorite)
        }
    }

- ์ด๋ ‡๊ฒŒ showFavoritesOnly ์†์„ฑ๊ณผ ๊ฐ landmark์˜ isFavorite ๊ฐ’์„ ํ™•์ธํ•˜์—ฌ ํ•„ํ„ฐ๋ง๋œ ๋ฒ„์ „์˜ landmarks ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š” !

 

3. ๊ทธ๋ฆฌ๊ณ  ๋ฆฌ์ŠคํŠธ ๋ทฐ์— ๋“ค์–ด๊ฐ€๋Š” landmarks๋ฅผ filteredLandMarks ๋กœ ๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค !

List(filteredLandmarks) { landmark in

 

3. ํ† ๊ธ€ ๋ฉ”๋‰ด ๋งŒ๋“ค๊ธฐ 

์‚ฌ์šฉ์ž๊ฐ€ ๋ชฉ๋ก ํ•„ํ„ฐ๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋„๋ก showFavoritesOnly์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ์ปจํŠธ๋กค์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

( ํ•„ํ„ฐ๋ง ๋œ ๊ฒƒ๋งŒ ๋ณผ์ง€, ์ „์ฒด ๋ชฉ๋ก์„ ๋‹ค ๋ณผ์ง€ ํ† ๊ธ€๋กœ ์ œ์–ด.)

์ด๋ฅผ ์œ„ํ•ด toggle control์— binding์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

 

binding์€ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ์— ๋Œ€ํ•œ ์ฐธ์กฐ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ํ† ๊ธ€์„ ๋„๊ณ  ์ผœ๊ณ  ๋‹ค์‹œ ๋Œ ๋•Œ, ์ปจํŠธ๋กค์€ ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ทฐ์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.


 

1. ๋žœ๋“œ๋งˆํฌ๋ฅผ ํ–‰์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด ์ค‘์ฒฉ๋œ ForEach ๊ทธ๋ฃน์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

- ๋ฆฌ์ŠคํŠธ์—์„œ ์ •์  ๋ฐ ๋™์  ๋ทฐ๋ฅผ ๊ฒฐํ•ฉํ•˜๊ฑฐ๋‚˜ ๋‘ ๊ฐœ ์ด์ƒ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ๋™์  ๋ทฐ ๊ทธ๋ฃน์„ ๊ฒฐํ•ฉํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ,

๋ฐ์ดํ„ฐ ์ปฌ๋ ‰์…˜์„ List์— ์ „๋‹ฌํ•˜๋Š” ๋Œ€์‹  ForEach ํƒ€์ž…์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

2. List ๋ทฐ์˜ ์ฒซ ๋ฒˆ์งธ ์ž์‹์œผ๋กœ showFavoritesOnly์— ๋Œ€ํ•œ ๋ฐ”์ธ๋”ฉ์„ ์ „๋‹ฌํ•˜๋Š” ํ† ๊ธ€ ๋ทฐ(Toggle)๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

- ์ƒํƒœ ๋ณ€์ˆ˜๋‚˜ ๊ทธ ์†์„ฑ ์ค‘ ํ•˜๋‚˜์— ๋Œ€ํ•œ ๋ฐ”์ธ๋”ฉ์„ ์•ก์„ธ์Šคํ•˜๊ธฐ ์œ„ํ•ด $ ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

var body: some View {
        NavigationView {
            List {
                Toggle(isOn: $showFavoritesOnly) {
                    Text("Favorites only")
                }
                
                ForEach(filteredLandMarks) { landmark in
                    NavigationLink {
                        LandmarkDetail(landmark: landmark)
                    } label: {
                        LandmarkRow(landmark: landmark)
                    }
                }
            }
            .navigationTitle("Landmarks")
        }
    }

 

 

 

4. ์ €์žฅ์„ ์œ„ํ•ด Observable ๊ฐ์ฒด ์‚ฌ์šฉํ•˜๊ธฐ

์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • landmark๋ฅผ ์ฆ๊ฒจ์ฐพ๊ธฐ๋กœ ์„ ํƒํ•˜๋„๋ก ์ค€๋น„ํ•˜๊ธฐ ์œ„ํ•ด landmark ๋ฐ์ดํ„ฐ๋ฅผ observable object์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

Observable object๋Š” SwiftUI์˜ ํ™˜๊ฒฝ์—์„œ ์ €์žฅ๋œ ๋ทฐ์— ๋ฐ”์ธ๋”ฉ ๋  ์ˆ˜ ์žˆ๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ๋ฐ์ดํ„ฐ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

SwiftUI๋Š” ๋ทฐ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋Š” observable object์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ๋ณ€๊ฒฝ ํ›„ ์˜ฌ๋ฐ”๋ฅธ ๋ฒ„์ „์˜ ๋ทฐ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.


 

1. ModelData ํŒŒ์ผ์—์„œ Combine ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ importํ•˜๊ณ , ObservableObject ํ”„๋กœํ† ์ฝœ์„ ์ค€์ˆ˜ํ•˜๋Š” ์ƒˆ๋กœ์šด ๋ชจ๋ธ ํƒ€์ž…์„ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

- SwiftUI๋Š” ObservableObject๋ฅผ ๊ตฌ๋…ํ•˜๊ณ , ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์ƒˆ๋กœ ๊ณ ์ณ์•ผ ํ•˜๋Š” ๋ชจ๋“  ๋ทฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

2. ๋žœ๋“œ๋งˆํฌ ๋ฐฐ์—ด์„ Observable Object ์•ˆ์œผ๋กœ ์ด๋™.

3. @Published ์†์„ฑ์„ ๋žœ๋“œ๋งˆํฌ ๋ฐฐ์—ด์— ์ถ”๊ฐ€

- ObservableObject๋Š” ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐœํ–‰(publish)ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ด๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ๋‹ค๋ฅธ ๊ฐ์ฒด๋“ค์ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

import Combine

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

 

5. ๋ทฐ์—์„œ ๋ชจ๋ธ ๊ฐ์ฒด ์ฑ„ํƒํ•˜๊ธฐ

์ด์ œ ModelData ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋ฏ€๋กœ, ์•ฑ์˜ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๋กœ ์ฑ„ํƒํ•˜๋„๋ก ๋ทฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 


 

1. LandmarkList.swift ํŒŒ์ผ์—์„œ, view์— @EnvironmentObject ํ”„๋กœํผํ‹ฐ ์„ ์–ธ์„ ์ถ”๊ฐ€ํ•˜๊ณ , preview์— environmentObject(_:) modifier๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

- modelData ํ”„๋กœํผํ‹ฐ๋Š” ๋ถ€๋ชจ ๋ทฐ์— environmentObject(_:) ์ˆ˜์ •์ž๊ฐ€ ์ ์šฉ๋˜์–ด ์žˆ์œผ๋ฉด ์ž๋™์œผ๋กœ ๊ฐ’์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

 

2. ๋žœ๋“œ๋งˆํฌ๋ฅผ ํ•„ํ„ฐ๋งํ•  ๋•Œ ๋ฐ์ดํ„ฐ๋กœ modelData.landmarks๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

struct LandmarkList: View {
    @EnvironmentObject var modelData: ModelData
    @State private var showFavoritesOnly = false

    var filteredLandmarks: [Landmark] {
        modelData.landmarks.filter { landmark in
            (!showFavoritesOnly || landmark.isFavorite)
        }
    }
...

struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
            .environmentObject(ModelData())
    }
}

 

3. LandmarkDetail ๋ทฐ๋ฅผ ํ™˜๊ฒฝ(Environment)์— ์žˆ๋Š” ModelData ๊ฐ์ฒด์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋„๋ก ์—…๋ฐ์ดํŠธํ•˜์„ธ์š”.

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

4. LandmarkRow ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ModelData ๊ฐ์ฒด์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋„๋ก ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

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

5. ContentView์˜ preview๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ํ™˜๊ฒฝ(Environment)์— ์ถ”๊ฐ€ํ•˜์—ฌ ๋ชจ๋“  ํ•˜์œ„ ๋ทฐ์—์„œ ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

- ๋งŒ์•ฝ ๋ฏธ๋ฆฌ๋ณด๊ธฐํ•˜๋Š” ๋ทฐ์—์„œ ํ•„์š”ํ•œ ํ•˜์œ„ ๋ทฐ๊ฐ€ ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ํ™˜๊ฒฝ(environment)์œผ๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์€ ๊ฒฝ์šฐ, ๋ทฐ๊ฐ€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ, ํ•ด๋‹น ํ•˜์œ„ ๋ทฐ์— environmentObject(_:) ์ˆ˜์ •์ž๋ฅผ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(ModelData())
    }
}

 

๋‹ค์Œ์œผ๋กœ, ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ๋‚˜ ๊ธฐ๊ธฐ์—์„œ ์•ฑ์„ ์‹คํ–‰ํ•  ๋•Œ ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ํ™˜๊ฒฝ(Environment)์— ๋„ฃ์–ด์ฃผ๊ธฐ ์œ„ํ•ด ์•ฑ ์ธ์Šคํ„ด์Šค๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

6. LandmarksApp์„ ์—…๋ฐ์ดํŠธํ•˜์—ฌ model ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ  environmentObject(_:) modifier๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ContentView์— ๊ณต๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.

- @StateObject ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ์•ฑ ๋ผ์ดํ”„ ์‚ฌ์ดํด ๋™์•ˆ ์ฃผ์–ด์ง„ ์†์„ฑ์— ๋Œ€ํ•ด ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ํ•œ ๋ฒˆ๋งŒ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๋ณด์—ฌ์ฃผ๋Š” ๋Œ€๋กœ ์•ฑ ์ธ์Šคํ„ด์Šค์—์„œ ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ทฐ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค.

 

@main
struct LandmarksApp: App {
    @StateObject private var modelData = ModelData()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(modelData)
        }
    }
}

 

6. ๋žœ๋“œ๋งˆํฌ์— ์ฆ๊ฒจ์ฐพ๊ธฐ ๋ฒ„ํŠผ ์ƒ์„ฑํ•˜๊ธฐ

Landmark ์•ฑ์€ ์ด์ œ landmark๋“ค์˜ ํ•„ํ„ฐ๋ง๋œ ๋ทฐ์™€ ํ•„ํ„ฐ๋ง๋˜์ง€ ์•Š์€ ๋ทฐ ๊ฐ„์— ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ฆ๊ฒจ์ฐพ๊ธฐ landmark ๋ชฉ๋ก์€ ์—ฌ์ „ํžˆ ํ•˜๋“œ ์ฝ”๋”ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ์ฆ๊ฒจ์ฐพ๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋„๋ก Landmark detail view์— ์ฆ๊ฒจ์ฐพ๊ธฐ ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


1. ์žฌ์‚ฌ์šฉํ•  Favorite Button ํŒŒ์ผ ๋งŒ๋“ค์–ด์ฃผ๊ธฐ

2. isSet์— ๋Œ€ํ•œ ๋ฐ”์ธ๋”ฉ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฒ„ํŠผ์˜ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๊ณ , ํ”„๋ฆฌ๋ทฐ์— ๋Œ€ํ•œ ์ƒ์ˆ˜ ๊ฐ’์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
- Binding์„ ์‚ฌ์šฉํ•˜๋ฉด, ์ด ๋ทฐ์—์„œ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

struct FavoriteButton: View {
    @Binding var isSet: Bool

    var body: some View {
        Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
    }
}

struct FavoriteButton_Previews: PreviewProvider {
    static var previews: some View {
        FavoriteButton(isSet: .constant(true))
    }
}

3. isSet ์ƒํƒœ๋ฅผ ํ† ๊ธ€ํ•˜๋Š” ์•ก์…˜์„ ๊ฐ€์ง€๋Š” Button์„ ๋งŒ๋“ค๊ณ , ์ƒํƒœ์— ๋”ฐ๋ผ ๋ชจ์–‘์ด ๋ณ€๊ฒฝ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
- ๋ฒ„ํŠผ์˜ ๋ผ๋ฒจ๋กœ ์ œ๊ณตํ•˜๋Š” ๋ฌธ์ž์—ด์€ iconOnly ๋ผ๋ฒจ ์Šคํƒ€์ผ์„ ์‚ฌ์šฉํ•  ๋•Œ UI์— ๋‚˜ํƒ€๋‚˜์ง€ ์•Š์ง€๋งŒ, VoiceOver๋Š” ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ ‘๊ทผ์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

 

( VoiceOver ๊ฐ€ ๋ญ”์ง€ ๋ชฐ๋ผ์„œ GPTํ•œํ…Œ ๋ฌผ์–ด๋ดค์–ด์š”.)

 

VoiceOver๋Š” ์‹œ๊ฐ์ ์œผ๋กœ ๋ณด์ด์ง€ ์•Š๋Š” UI ์š”์†Œ๋“ค์„ ์Œ์„ฑ์œผ๋กœ ์„ค๋ช…ํ•ด์ฃผ๋Š” ๋„๊ตฌ์ด๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ์‹œ๊ฐ์ ์ธ ์žฅ์• ๋ฅผ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๋„ ์•ฑ์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ผ๋ฒจ ๋ฌธ์ž์—ด์„ ์ œ๊ณตํ•˜์—ฌ VoiceOver๋ฅผ ์ด์šฉํ•˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ ๊ธฐ๋Šฅ์„ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

    var body: some View {
        Button {
            isSet.toggle()
        } label: {
            Label("Toggle Favorite", systemImage: isSet ? "star.fil" : "star")
                .labelStyle(.iconOnly)
                .foregroundColor(isSet ? .yellow : .gray)
        }
    }

4. ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์ง€๋ฉด ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ€๊ธฐ ์ „์— ๋ช‡ ๊ฐœ์˜ ๊ทธ๋ฃน์„ ๋” ๋งŒ๋“œ์„ธ์š”. 

- ํด๋”๋ง์„ ํ•ด์คฌ์Šต๋‹ˆ๋‹ค !

 

5. ๋‹ค์Œ์œผ๋กœ, ์ฆ๊ฒจ์ฐพ๊ธฐ ๋ฒ„ํŠผ์„ ์ƒ์„ธ ๋ณด๊ธฐ ๋ทฐ์— ์ถ”๊ฐ€ํ•˜๊ณ , ๋ฒ„ํŠผ์˜ isSet ์†์„ฑ์„ ํŠน์ • landmark์˜ isFavorite ์†์„ฑ์— ๋ฐ”์ธ๋”ฉํ•ฉ๋‹ˆ๋‹ค.
- ์ž…๋ ฅ๋œ landmark์™€ model data๋ฅผ ๋น„๊ตํ•˜์—ฌ ํ•ด๋‹น landmark์˜ ์ธ๋ฑ์Šค๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

- ์ด๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด, ํ™˜๊ฒฝ์˜ model data์— ๋Œ€ํ•œ ์•ก์„ธ์Šค ๊ถŒํ•œ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

 

    @EnvironmentObject var modelData: ModelData
    var landmark: Landmark

    var landmarkIndex: Int {
        modelData.landmarks.firstIndex(where: { $0.id == landmark.id })!
    }

6. ์ƒˆ๋กœ์šด FavoriteButton๊ณผ ํ•จ๊ป˜ landmark์˜ ์ด๋ฆ„์„ HStack์œผ๋กœ ๋ฌถ์–ด์„œ ์‚ฌ์šฉํ•˜๊ณ , isFavorite ์†์„ฑ์— $ ๊ธฐํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ”์ธ๋”ฉ์„ ์ œ๊ณตํ•˜์„ธ์š”.

- landmarkIndex์™€ modelData ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฒ„ํŠผ์ด ๋ชจ๋ธ ๊ฐ์ฒด์— ์ €์žฅ๋œ landmark์˜ isFavorite ์†์„ฑ์„ ์—…๋ฐ์ดํŠธํ•˜๋„๋กํ•ฉ๋‹ˆ๋‹ค.

 

HStack {
	Text(landmark.name)
    	.font(.title)
        FavoriteButton(isSet: $modelData.landmarks[landmarkIndex].isFavorite)
 }

๋ฆฌ์ŠคํŠธ ๋ทฐ๋กœ ๋Œ์•„๊ฐ€์„œ ๋ผ์ด๋ธŒ ํ”„๋ฆฌ๋ทฐ๋กœ ํ™•์ธํ•ด๋ณด์„ธ์š” !

 

๋ฆฌ์ŠคํŠธ์—์„œ ๋””ํ…Œ์ผ๋กœ ์ด๋™ํ•˜๊ณ  ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„์™€๋„ ํ•ด๋‹น ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

๋‘ ๋ทฐ๊ฐ€ ํ™˜๊ฒฝ(environment)์—์„œ ๋™์ผํ•œ ๋ชจ๋ธ ๊ฐ์ฒด(model object)์— ์•ก์„ธ์Šคํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ๋ทฐ๊ฐ€ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

 


 

 

๊ฐœ์ธ์ ์œผ๋กœ ๊ถ๊ธˆํ–ˆ๋˜ ๋ถ€๋ถ„ - @State ๋ณ€์ˆ˜๋ฅผ ๊ณ„์† private๋กœ ์„ ์–ธํ•˜๋Š” ์ด์œ ?

 

@State ๋ณ€์ˆ˜๋ฅผ private์œผ๋กœ ์„ ์–ธํ•˜๋Š” ๊ฒƒ์€ ๊ถŒ์žฅ๋˜๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

์ด์œ ๋Š” @State ๋ณ€์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค SwiftUI๊ฐ€ ํ•ด๋‹น View๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ๋•Œ๋ฌธ์—,

๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก View์˜ ์ตœ์†Œํ•œ์˜ ๋ถ€๋ถ„๋งŒ ๋‹ค์‹œ ๊ทธ๋ ค์ง€๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์„ฑ๋Šฅ์ ์œผ๋กœ ์ข‹๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ, @State ๋ณ€์ˆ˜๊ฐ€ private์œผ๋กœ ์„ ์–ธ๋˜์–ด์•ผ๋งŒ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๋‹ค๋ฅธ View์—์„œ ํ•ด๋‹น ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š”, @State ๋ณ€์ˆ˜๋ฅผ ํ•ด๋‹น View์˜ ์ƒ์œ„ View์—์„œ ์„ ์–ธํ•˜๊ณ ,

binding์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•˜์œ„ View๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ, @State ๋ณ€์ˆ˜๋ฅผ private์œผ๋กœ ์„ ์–ธํ• ์ง€๋Š” ๊ฐœ๋ฐœ์ž์˜ ํŒ๋‹จ์— ๋”ฐ๋ผ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

SwiftUI) Drawing and Animation 1 - Drawing Paths and Shapes  (0) 2023.04.02
SwiftUI) SwiftUI Essentials - 2 Building Lists and Navigation  (0) 2023.03.29
SwiftUI) SwiftUI Essentials  (8) 2023.03.29
Comments