티스토리 뷰

안녕하세요.

이전 글에서 @ObservedObject, @StateObject에 대해 알아봤고,

오늘은 마지막 @Environment에 대해 알아보겠습니다.

@Environment

A property wrapper that reads a value from a view’s environment.
출처: 공식문서

공식 문서에는.

@Environment Property Wrapper는 View 환경에 저장된 EnvironmentValues 을 읽어오기 위한 Property Wrapper이고,

key path를 통해 EnvironmentValues을 지정하고, 읽어올 수 있다고, 나와있습니다.

위 같은 글을 읽었을 땐 바로 이해가 되질 못했습니다.

때문에 직접 사용해 보면서 정리해 보겠습니다.

예제를 통해 사용해 볼 EnvironmentValuedismiss입니다.

swift가 기본으로 제공하는 다양한 EnvironmentValues 중 하나입니다

struct SubView: View {
    @Environment(\.dismiss) var dismiss
    var body: some View {
        VStack {
            Button("dismiss") {
                dismiss()
            }
        }
    }
}

위와 같이 dismiss@Environment 사용하여 선언해 주고,

struct ContentView: View {
    var body: some View {
        NavigationStack {
            VStack {
                NavigationLink {
                    SubView()
                } label: {
                    Text("SubView로 이동")
                }

            }
            .padding()
        }
    }
}

상위 View에서 push하면, 

위와 같이 정상적으로 dismiss 되는 것을 보실 수 있습니다.

만약, swift가 제공하는 EnvironmentValues 중 필요한 것이 없다고 한다면,

직접 만들어 사용할 수도 있습니다.

private struct SubViewBackgroundColorKey: EnvironmentKey {
    static let defaultValue = Color.red
}

extension EnvironmentValues {
    var subViewBackgroundColor: Color {
        get { self[SubViewBackgroundColorKey.self] }
        set { self[SubViewBackgroundColorKey.self] = newValue }
    }
}

위와 같이 구조체를 통해 EnvironmentKey protocol을 채택한 Key 타입을 만들어주고, 기본값을 설정합니다,

그다음, EnvironmentValues를 확장하여 원하는 Value를 생성해 줍니다.

struct SubView: View {
    @Environment(\.subViewBackgroundColor) var subViewBackgroundColor
    var body: some View {
        subViewBackgroundColor
    }
}

그리고 위와 같이 사용해주면,

끝!!

만약, 기본값이 아닌 다른 값으로 변경해 사용하고 싶다면,

상위 View에서 .environment를 사용하여,

struct ContentView: View {
    @State private var isPresentSubView2: Bool = false
    var body: some View {
        NavigationStack {
            VStack {
                NavigationLink {
                    SubView()
                        .environment(\.subViewBackgroundColor, .yellow) // 값 변경
                } label: {
                    Text("SubView push")
                }

            }
            .padding()
        }
    }
}

위와 같이 .yellow 값을 넣으면,

위와 같이 SubView에선 변경된 값을 사용할 수 있습니다.

그럼 이번엔 @Environment와 비슷하지만 좀 다른 @EnvironmentObject에 대해 알아보겠습니다.

@EnvironmentObject

A property wrapper type for an observable object that a parent or ancestor view supplies.
출처: 공식문서

@EnvironmentObject 같은 경우 @Environment과 사용법은 비슷하지만, 사용되는 상황은 많이 다릅니다.

@Environment과 다르게 @EnvironmentObject는 상위 View가 하위 View에서 observable한 객체를 넘겨줄 때 사용할 수 있습니다.

예제 코드를 통해 이해해 보겠습니다.

class DataModel: ObservableObject {
    @Published private(set) var name = "Some Name"
    
    func changeName(name: String) {
        self.name = name
    }
}

먼저, 위와 같이 observable이 가능한 객체를 만들어 주고,

struct ContentView: View {
    @State private var code: Int = 0
    @StateObject private var dataModel = DataModel()
    var body: some View {
        VStack {
            SubView(dataModel: dataModel)
        }
        .padding()
    }
}

struct SubView: View {
    @ObservedObject private var dataModel: DataModel
    
    init(dataModel: DataModel) {
        self.dataModel = dataModel
    }
    
    var body: some View {
        VStack {
            Text("이름: \(dataModel.name)")
            Button("랜덤 이름") {
                dataModel.changeName(name: "ABC\(Int.random(in: 1...1000))")
            }
        }
    }
}

위와 같이 코드를 작성해 보았습니다.

위 코드를 @EnvironmentObject를 사용해서 수정해 보도록 하겠습니다.

struct ContentView: View {
    @State private var code: Int = 0
    @StateObject private var dataModel = DataModel()
    var body: some View {
        VStack {
            SubView()
                .environmentObject(dataModel) // init -> environmentObject
        }
        .padding()
    }
}

struct SubView: View {
    @EnvironmentObject var dataModel: DataModel // @ObservedObject -> @EnvironmentObject
    
    var body: some View {
        VStack {
            Text("이름: \(dataModel.name)")
            Button("랜덤 이름") {
                dataModel.changeName(name: "ABC\(Int.random(in: 1...1000))")
            }
        }
    }
}

위와 같이 수정해 보았습니다.

좀 더 깔끔해 보이는 것 같네요. 

둘 중 정답이 없기 때문에 자신만의 스타일로 코드를 작성하면 될 것 같습니다.

코드가 잘 작동하는지 봐볼까요?

정상 작동하는 것을 보실 수 있습니다.

여기까지 3편에 걸친 Property Wrapper에 대해 알아보기 마무리하겠습니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함