버그 잡이

SwiftUI - @State, @Binding 개념 이해하기 본문

SwiftUI

SwiftUI - @State, @Binding 개념 이해하기

버그잡이 2023. 5. 13. 17:52

@State

SwiftUI에 의해 관리되는 값을 읽고 쓰는 Property Wrapper 타입

 

"Property Wrapper "

- 프로퍼티를 감싸 특별한 타입으로 만들어주는 친구. 

- 감싸는 행위를 통해서 코드 추가 없이 프로퍼티에 특정 기능을 추가할 수 있다.

 

"SwiftUI에 의해 관리된다"

- 변수에 변화가 생겼을때 해당 value의 appearance를 무효화 하고 다시 body 값을 계산

 

즉, 변수에 변화가 생겼을때, 이를 화면에 바로 반영할 수 있게 도와주는 Property Wrapper가 @State입니다.

 

 

@State 사용법

아래는 공식 문서에 있는 예제입니다.

isPlaying값에 따라 Button의 title이 달라지는 것을 볼 수 있습니다.

struct PlayButton: View {
    @State private var isPlaying: Bool = false // Create the state.

    var body: some View {
        Button(isPlaying ? "Pause" : "Play") { // Read the state.
            isPlaying.toggle() // Write the state.
        }
    }
}

 

State를 사용할떄 주의할 부분이 있는데요.

"Private으로 선언하고 가장 상위 뷰에서 @State를 관리하게 하라는 것입니다."

@State가 하위뷰에서 초기화 되면 SwiftUI storage 공간에서 conflict가 날 수 있다고 합니다.

그래서 하위 뷰에 공유하려면 read-only로 공유하거나 binding으로 공유하라고 합니다.

 

 

 

@Binding

Source of truth에 의해 소유되는 값을 읽고 쓰는 Property Wrapper 타입

 

"Source of truth에 의해 소유되는"

- 말이 좀 어려운데요...

- 여기서 말하는 Source of truth는 @State로 선언된 프로퍼티를 말합니다.

- 위에서 말했듯이 @Binding은 @State값을 하위 뷰에 공유하기 위해서 사용됩니다.

- 즉, @Binding은 @State에 의해서 소유되는 하나의 "연결용" property wrapper 입니다.

 

정리하자면

1. @Binding으로 감싸진 값은 @State에 의해서 소유되는 값이다

2. @Binding은 @State값을 하위뷰에서 read, write 할 수 있게 도와준다.

 

 

@Binding 사용법

struct PlayButton: View {
    @Binding var isPlaying: Bool

    var body: some View {
        Button(isPlaying ? "Pause" : "Play") {
            isPlaying.toggle()
        }
    }
}

struct PlayerView: View {
    var episode: Episode
    @State private var isPlaying: Bool = false

    var body: some View {
        VStack {
            Text(episode.title)
                .foregroundStyle(isPlaying ? .primary : .secondary)
            PlayButton(isPlaying: $isPlaying) // Pass a binding.
        }
    }
}

 

PlayButton에 isPlaying값을 넘겨줄때 '$'을 앞에 붙여줍니다.

$를 붙여준다는 것은 @State로 선언된 값의 projectedValue를 넘겨준다는 것을 의미합니다.

 

@State의 내부 코드를 보면 projectedValue가 Binding<Value>로 선언되어 있는 것으로 볼 수 있습니다.

하위뷰에 value를 직접 전달하는 것이 아니라 Binding을 전달해서 위에서 말한 conflict 이슈를 해결합니다.

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen @propertyWrapper public struct State<Value> : DynamicProperty {
    public init(wrappedValue value: Value)
    public init(initialValue value: Value)
    
    public var wrappedValue: Value { get nonmutating set }
    public var projectedValue: Binding<Value> { get }
}

 

 

 

 

*참고

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

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

 

 

 

 

 

반응형
Comments