Managing user interface state, 원문
뷰를 재사용할 수 있도록 앱의 뷰 계층 구조 내에서 뷰 별 데이터를 캡슐화
뷰 전반에 걸쳐 공유되는 진실 소스를 확립하기 위해서 데이터가 필요한 뷰의 가장 공통 상위 뷰에 데이터를 State로 저장
Swift 속성(property)을 통해 데이터를 읽기 전용으로 제공하거나, 바인딩으로 `state`에 대한 양방향 연결을 만든다.
SwiftUI는 데이터의 변화를 감시하고, 필요에 따라 영향을 받는 뷰를 업데이트한다.
하지만, `state` 변수의 수명 주기가 뷰의 수명 주기를 반영하기 때문에 영구 저장에 `state` 속성을 사용하면 안 된다.
대신, 버튼의 하이라이트 상태, 필터 설정, 현재 선택한 목록 항목과 같이 사용자 인터페이스에 영향을 미치는 일시적인 `state`를 관리하는 데 사용할 수 있다.
앱의 데이터 모델을 변경할 준비가 되기 전에 프로토타입을 만드는 동안 이런 종류의 저장소가 편리할 수 있다.
- 여기서 말하는 저장소는 메모리 관점에서의 저장소를 말하는 듯
변경 가능한(mutable) 값을 `state`로 관리
뷰가 수정할 수 있는 데이터를 저장해야 하는 경우, `@State` 속성 래퍼로 변수를 선언한다.
예를 들어, 팟캐스트 플레이어 뷰에서 `isPlaying Boolean`을 만들어 팟 캐스트가 실행 중일 때를 추적할 수 있다.
struct PlayerView: View {
@State private var isPlaying: Bool = false
var body: some View {
// ...
}
}
속성을 `state`로 표시하면 프레임워크가 기본 저장소를 관리하도록 알려준다.
뷰는 속성 이름을 사용하여 `state`의 wrappedValue 속성에서 발견된 데이터를 읽고, 쓴다.
값을 변경하면, SwiftUI는 뷰의 영향을 받는 부분을 업데이트한다.
예를 들어, 탭할 때 저장된 값을 토글 하고, 저장된 값에 따라 다른 이미지를 표시하는 버튼을 `PlayerView`에 추가할 수 있다.
Button(action: {
self.isPlaying.toggle()
}) {
Image(systemName: isPlaying ? "pause.circle" : "play.circle")
}
`state` 변수를 private로 선언하여 범위를 제한한다.
이것은 변수가 선언되는 뷰 계층 구조에 캡슐화된 상태로 유지될 수 있게 만든다.
불변(Immutable)의 값을 저장하기 위한 Swift 속성 선언
뷰가 수정되지 않는 데이터로 뷰를 제공하려면, 표준 스위프트 속성을 선언한다.
예를 들어, 에피소드 제목과 쇼 이름에 대한 문자열을 포함하는 입력 구조를 갖도록 팟캐스트 플레이어를 확장(extension)할 수 있다.
struct PlayerView: View {
let episode: Episode // The queued episode.
@State private var isPlaying: Bool = false
var body: some View {
VStack { // Display information about the episode.
Text(episode.title)
Text(episode.showTitle)
Button(action: {
self.isPlaying.toggle()
}) {
Image(systemName: isPlaying ? "pause.circle" : "play.circle")
}
}
}
}
에피소드 속성의 값은 `PlayerView`의 상수(constant)이지만, 이 뷰의 상위 뷰에서 일정할 필요는 없다.
사용자가 부모에서 다른 에피소드를 선택하면, SwiftUI는 `state`의 변화를 감지하고 새로운 입력으로 `PlayerView`를 다시 만든다.
바인딩으로 `state`에 대한 액세스 공유
뷰가 하위 뷰와 `state` 제어를 공유해야 하는 경우, 바인딩 속성 래퍼로 하위 속성을 선언한다.
바인딩은 기존 저장소에 대한 참조를 나타내며, 기본 데이터에 대한 진실 소스를 보존한다.
예를 들어, 팟캐스트 PlayerView의 버튼을 `PlayButton`이라는 자식 뷰로 리팩터링 하면, `isPlaying` 속성에 바인딩할 수 있다.
struct PlayButton: View {
@Binding var isPlaying: Bool
var body: some View {
Button(action: {
self.isPlaying.toggle()
}) {
Image(systemName: isPlaying ? "pause.circle" : "play.circle")
}
}
}
위 예시처럼 `state`와 마찬가지로 속성을 직접 참조하여 바인딩의 래핑 된 값을 읽고, 쓴다.
하지만 `state` 값과 달리, 바인딩은 자체 저장소가 없다.
대신, 다른 곳에 저장된 `state` 속성을 참조하고, 그 저장소에 대한 양방향 연결을 제공한다.
`PlayButton`을 인스턴스화할 때, 달러 기호 `$`로 접두사를 붙여 부모 뷰에서 선언된 해당 `state` 변수에 대한 바인딩을 제공한다.
struct PlayerView: View {
var episode: Episode
@State private var isPlaying: Bool = false
var body: some View {
VStack {
Text(episode.title)
Text(episode.showTitle)
PlayButton(isPlaying: $isPlaying) // Pass a binding.
}
}
}
`$` 접두사는 projectedValue에 대해 래핑 된 속성을 요청하며,`state`는 기본 저장소에 대한 바인딩이다.
마찬가지로, `$` 접두사를 사용하여 바인딩에서 바인딩을 얻을 수 있으며, 임의의 수의 뷰 계층 구조를 통해 바인딩을 전달할 수 있다.
- 바인딩 체이닝
`state` 변수 내에서 범위 값에 대한 바인딩을 얻을 수도 있다.
예를 들어, Player의 부모 뷰에서 에피소드를 `state` 변수로 선언하고 에피소드 구조체에 토글로 제어하려는 `isFavorite` `Bool`이 포함되어 있는 경우, `$episode.isFavorite`를 참조하여 에피소드의 즐겨찾기 상태에 대한 바인딩을 얻을 수 있다.
struct Podcaster: View {
@State private var episode = Episode(title: "Some Episode", showTitle: "Great Show", isFavorite: false)
var body: some View {
VStack {
Toggle("Favorite", isOn: $episode.isFavorite) // Bind to the Boolean.
PlayerView(episode: episode)
}
}
}
'Swift' 카테고리의 다른 글
Swift: CLLocationManager (0) | 2024.09.09 |
---|---|
Swift: Core Location (0) | 2024.09.09 |
Swift: Model data (0) | 2024.08.22 |
Swift: Attributes (0) | 2024.08.22 |
Swift: Network 프레임워크 (0) | 2024.08.17 |