* advanced가 글 앞에 붙으면 이론 + 실전응용 내용을 담고 있습니다~
Environment와 keypath를 통해서 SwiftUI에서 미리 지정된 값을 사용할 수 있다.
@Environment(\.layoutDirection) var layoutDirection
@Environment(\.dismiss) var dismiss
각각 View의 방향, 뷰를 dismiss 하기 위한 값을 Environment 프로퍼티 래퍼를 통해서 얻을 수 있다. 당연히 직접 커스텀도 가능하다. Environment와 EnvironmentObject는 여러 View에 걸쳐서 사용할 값을 전달할 때 사용될 수 있는데 오늘은 그 중에서 Environment 알아보기!
Environment Values
Environment를 사용하면 SwiftUI에서 뷰 계층 전체에서 데이터를 공유할 수 있다. 이때 말하는 뷰 계층은 environemt를 주입한 시점이다. environment는 일종의 데이터 저장소로 보면 되는데 이 저장된 값을 주입하면 그 주입한 뷰의 계층은 이 environment 값을 사용할 수 있게 된다.
SwiftUI에서 직접 지정 제공하는 값은 뷰 전체 계층에서 Environment PropertyWrapper을 사용해서 접근할 수 있지만, 직접 지정한 Environemt 를 사용하기 위해서는 현재 뷰나 상위 뷰에서 Environment를 주입해주어야 한다.
@Environment(\.layoutDirection) var layoutDirection
SwiftUI의 뷰는 EnvironmentProperty를 사용해서 Environment로 부터 값을 사용할 수 있다. SwiftUI에서 직접 제공하는 Environment를 built in EnvironmentValues 라고 하고
https://developer.apple.com/documentation/swiftui/environmentvalues
애플이 이곳에 사용할 수 있는 빌트인 값을 정의해 놓았다. 캘린더, 다크모드, 화면 사이즈 등을 environment를 통해 쉽게 얻을 수 있다.
RToLView()
.environment(\.layoutDirection, layoutDirection)
environment modifier을 사용하면 해당 뷰 계층에 environment 값을 전달할 수 있다.
environment는 여러뷰가 동일한 데이터에 접근해야 하는 경우 사용하기에 적합하다. 이때 값은 뷰와 뷰 사이로 전달되는 것이 아닌 environment에 저장되고 이를 뷰 계층이 가져다 사용할 수 있게 된다.
EnvironmentValues 사용해 보기
아라빅권에서는 오른쪽에서 왼쪽으로 글을 읽기 때문에 레이아웃의 흐름도 한국과 달리 오른쪽 부터 시작해야 한다. 프로그래밍으로 직접 순서를 변경하는 것이 아닌 SwiftUI에서 미리 지정된 EnvironmentValues와 기본 View를 사용하면 쉽게 해결할 수 있다.
RToLView라고 하는 View에는 Text, Image, Text를 감싼 HStack이 정의되어 있다.
상위뷰에서 RToLView를 두 개 넣어주고 각각 environment를 설정하지 않도록, environment modifier를 사용해서 environment를 설정하도록 해준다.
프로그래밍적으로 view의 순서를 바꿀 필요 없이 뷰의 순서가 변경된 것을 확인 할 수 있다.
ThemeView에는 environment값을 주입하지 않은 RToLView를 두 개 넣어주었고 상위 뷰인 ThemeView에 environment를 주입하는 경우 하위 뷰 모두 environment값에 따라 뷰의 순서가 변경된 것을 확인할 수 있다.
즉, 상위 뷰에서 environment를 지정한 경우 하위 계층 구조도 그 값으로 지정된다.
View에서 Environment 값 변경하기
environmet로 주입할 값을 State로 선언해 준 뒤 environment modifier을 사용해서 값을 주입해 주면 된다. 위의 코드에서는 버튼을 누를 때마다 environment modifier로 주입할 값인 layoutDirection이 변경되는 것을 확인할 수 있다.
Custom Environment 추가하기
Environment와 EnvironmentObject의 가장 큰 차이점은 Environment은 키와 값을 가지고 있는 딕셔너리 구조이고, EnvironmentObject는 객체 그 자체라는 것이다. 만약 각각 값을 바꾼다고 할 때 Environment는 해당 키에 있는 '값'을 바꾸는 것이고, EnvironmentObject는 해당 객체 그 자체를 변경해 버리는 것이다.
사용자 정의 키값을 정의하고 Environment에서 사용할 수 있다. custom environment를 추가하기 위해서는 Key와 Value를 정의해 주어야 한다. 각각 EnvironmentKey, EnvironmentValues를 사용한다.
Environment의 키로 사용할 구조체를 하나 정의해 준다. 이때 EnvironmentKey 프로토콜을 채택해야 한다. 이를 채택하면 defaultValue를 꼭 구현해야 한다. 타입과 기본값을 정의해 주어야 한다. EnvironmentObject의 경우 사용할 EnvironmentObject가 뷰 계층에 주입되지 않으면 앱이 크래쉬를 일으키지만 Environment의 경우 주입되지 않은 environment를 사용할 경우 defaultValue값이 나타나게 된다. 따라서 environment를 사용하기 이전에 뷰 계층 구조에 environment가 주입되었는지 확인해야 하며 주입되지 않고 사용하는 경우 뷰 계층 전체에 걸쳐 사용되는 environment가 아닌 기본값일 수도 있음을 주의해야 한다.
ㅍ
EnvironmentValues의 extension에 프로퍼티를 정의해 준다. 이 프로퍼티가 키패스로 사용된다. EnvironmentValues의 CustomKey로 getter와 setter를 지정해 준다.
만들어준 custom environmet를 사용할 때는 environment modifier을 사용해서 값을 주입하면 된다.
environment에 접근해 값을 사용하고자 하는 View 구조 내부에 @Environment 프로퍼티 래퍼를 사용해서 값을 사용할 수 있다.
Custom Environment 값 뷰에서 변경하기
Environment의 값을 변경하면 상위뷰와 하위뷰에서 동일하게 값이 변경되는지 확인하기 위해 우선 하위 뷰에서 값을 변경해보면
State와 Binding과는 다르게 environment property는 view 내부에서 변경할 수 없는 get-only property로 지정되어 있다.
이 environment를 주입하는 부분에서 값을 변경해주는 방법을 사용할 수 있다. 즉 environment를 주입한 시작 지점, 해당 뷰 계층 구조의 가장 상위 지점에서 값을 변경해 주어야 한다.
View struct에서 값을 변경할 수 있는 State를 하나 정의해 준다.
State로 정의 되었기 때문에 값을 변경할 수 있다. 그리고 이 State를 environment로 주입해 준다.
상위뷰에서 버튼을 눌러 State 값을 변경하면 해당 environment를 사용하는 하위 뷰 또한 값이 변경되는 것을 확인할 수 있다.
Theme 만들기
어디에 응용될 수 있을까 고민해 봤는데 테마를 지정하기 위한 값으로 사용하면 괜찮겠다는 생각이 들었다. 테마를 지정하면 해당 테마에 맞게 즉시 텍스트나 뷰의 값이 변경되도록 할 수 있다.
Theme 프로토콜을 정의해 준다. 앱 내부에서 사용할 컬러값을 작성해 준다.
Asset에 테마별로 컬러를 지정해 준다. 이때 컬러는 Theme 프로토콜에서 작성한 색상 이름을 사용했다.
각 테마를 Theme 프로토콜을 사용해서 정의해 준다.
Theme 타입으로 custom environment를 생성해준다. View의 extension에 함수를 정의해서 사용하면 keypath 부분을 가릴 수 있어 더 깔끔하고 직관적인 이름으로 코드를 작성해 줄 수 있다.
State로 변경가능한 값인 theme을 설정하고 onTapGesture을 사용해 이 값을 변경한다. 그리고 이 값을 environment로 주입하면 하위 뷰에서 이 테마 값을 공유할 수 있다.
하위 뷰에서는 공유된 environment를 사용해서 각 컴포넌트의 색상을 지정할 수 있다.
상위 뷰에서 Environment 값을 변경하면 그 environment를 사용하는 하위뷰도 즉시 값이 변경되는 것을 확인할 수 있다.
Debugging
SwiftUI에서는 Environment를 디버깅 할 수 있는 modifier을 제공한다
environment를 주입하는 modifier인 environment 이전에 transformEnvironment를 사용해서 environment 값의 변화를 추적할 수있다.
Environment의 값이 변경되면 변경된 값을 콘솔에서 확인할 수 있다.
Observation
observation을 사용하면 keypath를 지정할 필요 없이 타입을 키패스처럼 사용해서 environment에 접근할 수 있다.
@Observable 매크로를 사용해서 클래스를 정의해준다.
상위뷰에서는 키패스 필요 없이 environment modifier을 사용해서 해당 객체를 뷰 계층으로 주입할 수 있다.
@Environment property wrapperr 또한 해당 객체의 타입으로 선언해 사용할 수 있다.
'iOS 🍎 > Property wrapper' 카테고리의 다른 글
[iOS/SwiftUI] @Published, ObservableObject, ObservedObject (0) | 2023.04.03 |
---|---|
[iOS/SwiftUI] @State with dollar sign (two-way binding) (0) | 2023.03.18 |
[iOS/SwiftUI] 왜 토글에서는 달러 사인을 쓰고 버튼에서는 안쓸까? (0) | 2023.03.15 |
[iOS/SwiftUI] @State (0) | 2023.03.15 |