아요 개발 일기

[SwiftUI] Property Wrapper - @State, @Binding 본문

UI/SwiftUI

[SwiftUI] Property Wrapper - @State, @Binding

소진이 2023. 1. 2. 11:09

안녕하세요~ 소진입니다 😄

오늘은 SwiftUI의 Property Wrapper에 대해 알아보겠습니다!

양이 많을 수 있지만, 차근차근 가봅시다! 🐢


 

@State

 

SwiftUI의 View는 struct이고, 이는 언제나 소멸되거나 재생성됩니다.

하지만 struct는 값 타입이기때문에 struct내의 값을 변경 할 수 없습니다

이때, SwiftUI는 @State를 제공해 struct내의 값을 변경할 수 있게 해줍니다.

(@State를 사용해 지속적으로 변형 가능한 변수를 만듬)

 

즉, @State를 앞에 추가하면 SwiftUI가 자동으로 변경사항을 Observe하고

해당 @State를 사용하는 view 부분을 업데이트합니다.

 

 

자, 이제 본격적으로

공식문서 부시러 가봅시다!

 

https://developer.apple.com/documentation/swiftui/state

 

" SwiftUI에서 관리하는 값을 읽고 쓸 수 있는 property wrapper type입니다."

라고 설명하고 있네요! 

위에서 잠깐 설명한 것을 짧게 간추린 버전같죠? ㅎㅎ

 

Overview가 길지만... 

다 알아야하기때문에! 읽어봅시당

 

- SwiftUI는 @State(상태)로 선언한 모든 property(속성)의 storage(저장소)를 관리

 

- 값이 변경될때, SwiftUI는 값에 의존하는 view 계층 구조 일부를 업데이트

 

- view 계층 구조에 저장된 value의 @State는 single source of truth를 사용

 

- @State는 String, Int, Bool과 같은 간단한 타입에만 사용되는 것이 좋음

 

- State 인스턴스는 value 자체가 아니며 읽고 쓰는 수단임

그래서 자식 view에 @State property를 전달하면 SwiftUI는 부모가 값이 변경될 때마다 자식을 update하지만,

자식은 값을 modify(수정)할 수 없음 이때 자식 view의 수정하기가 활성화되도록 하려면

저장된 값을 Binding instead을 전달해야 함

 

 

- property 이름에 dollar 표시인($) 접두사를 붙여 얻을 수 있는

projectedValue state에 접근하여 state value에대한 바인딩을 얻을 수 있음

 

- @State 변수는 private으로 선언하고, view 계층 구조의 최상위 view에 배치하여 다른 view와 공유되지 않음

왜냐하면 SwiftUI에서 제공하는 storage와 충돌할 수 있어서 view를 instance화하는 view 계층에서는 view state를 초기화하면 안 됨.

만약, 다른 view와 값을 공유하고 싶다면 @StateObject나 @ObservedObject를 사용

 

 

살짝 글만 읽는데 숨차네요,,

 

이럴때는

예시를 봅시다!

SwiftUI의 segmentedControl를 사용해볼꺼에요

struct SegementedControlView: View {
    var body: some View {
        VStack {
            Picker("", selection: .constant(0)) {
                Text("A").tag(0)
                Text("B").tag(1)
                Text("C").tag(2)
            }.pickerStyle(SegmentedPickerStyle())
        }
        .padding()
    }
}

 

selection 파라미터가 binding을 받는 곳 인데,

.constant(0)이 들어가있죠?

그 constant때문에 selection이 0(=A)에서 바뀌지 않습니다.

 

 

위처럼 아무리 눌러도 0(=A)에서 변하지 않습니다.

이때 @State property를 사용하면 됩니다!

 

struct SegementedControlView: View {
    @State private var selectedIndex = 0
    
    var body: some View {
        VStack {
            Picker("", selection: $selectedIndex) {
                Text("A").tag(0)
                Text("B").tag(1)
                Text("C").tag(2)
            }.pickerStyle(SegmentedPickerStyle())
        }
        .padding()
    }
}

 

위처럼 @State property를 선언해주고 

해당 @State property를 selection에 binding 해줍니다!

binding 값은 위에서 설명한 것처럼

dollar($)연산자를 이용하여 얻을 수 있습니다.

 

 

아주 잘 변경되네요!

뭔가 예시로 보니까 이해가 잘 되죠?ㅎㅎㅎㅎ


 

@Binding

 

 

https://developer.apple.com/documentation/swiftui/binding

 

"source of truth가 소유한 값을 읽고 쓸수 있는 property wrapper입니다"

무슨 소리야..

OverView를 봅시다!

 

- 데이터를 저장하는 state와 데이터를 표시하는 state간에 양방향 연결을 만듬

 

- 데이터를 직접 저장하는 대신 state를 다른 곳에 있는 source of truth stored(저장소)에 연결

 

 

무슨 소리지?

글로만 보는건 어려우니!

이번에도 예시 코드를 봅시당~.~

 

 

위처럼 버튼이 true일때는 Pause 

false일때는 Play로 나오도록 하겠습니다!

 

struct ButtonView: View {
    @State private var isPlaying: Bool = false
    
    var body: some View {
        VStack {
            PlayButton(isPlaying: $isPlaying)
                .buttonStyle(.bordered)
        } .foregroundColor(.green)
    }
}

 

위는 ButtonView를 구성한 부분입니다

@State로 isPlaying을 선언하고, 기본 Bool 값은 false로 설정하겠습니다.

body 내부에서

$기호를 사용하여 isPlaying의 값을 받아온 후,

그 값을 이용해 View를 출력합니다.

 

저희가  true일때는 Pause

false일때는 Play로 나오게하기로 했죠?

struct PlayButton: View {
    @Binding var isPlaying: Bool
    
    var body: some View {
        Button(isPlaying ? "Pause" : "Play") {
            isPlaying.toggle()
        }
    }
}

 

그 부분을 할 수 있도록 해주는 코드입니다!

isPlaying 값을 @Binding 해주고,

삼항 연산자를 이용해 Button에 출력되는 택스트를 선택해주고

.toggle을 이용하여 버튼의 상태를 전환해줍니다.

 

 

잘 전환되네용ㅎㅎ

 

오늘 포스팅은 글이 너무 길어질 것 같아서

이정도로만 하고!

다음은 다른 Property Wrapper 

@Published, @ObservedObject, @StateObject, @EnvironmentObjec을 공부하기 전에

ObservableObject에 대해 포스팅하도록하겠습니다!

다음 포스팅에서 만나용 🥰