본문 바로가기
Swift

Swift: Managing user interface state

by songmoro 2024. 8. 22.
728x90

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)
	    }
    }
}
728x90

'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