관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - SwiftUI] State, Binding 개념, 사용 방법 본문

iOS 기본 (SwiftUI)

[iOS - SwiftUI] State, Binding 개념, 사용 방법

jake-kim 2022. 10. 27. 22:48

목차) SwiftUI의 기본 - 목차 링크

State 란?

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

  • SwiftUI에 의해 관리되는 property wrapper 타입
struct ContentView: View {
  @State var age = 20 // <-
  
  var body: some View {
    Text("Hello, world!")
      .padding()
  }
}
  • 내부 코드
    • projectedValue에 나오는 Binding 개념은 아래에서 계속
@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 }
}
  • SwiftUI에 의해 관리된다는 것은?
    • @State로 선언한 프로퍼티는 값이 변경되면 뷰 계층 구조의 부분을 업데이트
    • @State를 자식 뷰에 전달하면 부모에서 값이 변경될 때마다 자식을 업데이트
    • 단, 자식 뷰에서 값을 수정하려면, 부모에서 자식으로 Binidng을 전달하여 자식 뷰에서 값을 수정이 가능

ex) 자식 뷰가 @State로 관리되는 경우 - 자식 뷰에서 값을 변경해도 부모 뷰의 값은 바뀌지 않음

  • 자식 뷰의 @State isPlaying 프로퍼티의 값이 변경된다 하더라도 부모 뷰의 isPlaying값은 바뀌지 않음
struct ContentView: View {
  @State private var isPlaying = false
  
  var body: some View {
    PlayButton(isPlaying: isPlaying)
      .onAppear {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
          print("isPaying = \(isPlaying)") // 자식 뷰에서 버튼을 눌러도 부모 isPlaying값은 언제나 false
        }
      }
  }
}

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

ex) Binding을 이용하여 자식 뷰에서 값이 변경되면 부모 뷰에도 값이 변경되도록 처리

자식 뷰에서 @State를 @Binidng으로 변경하고, 부모 뷰에서 자식 뷰로 넘겨줄때 Binding을 넘겨주는 방식

struct ContentView: View {
  @State private var isPlaying = false
  
  var body: some View {
    PlayButton(isPlaying: $isPlaying) // <-
      .onAppear {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
          print("isPaying = \(isPlaying)")
        }
      }
  }
}

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

State를 사용할때의 주의점

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

  • @State 프로퍼티는 항상 private으로 선언하고 가장 상위 뷰에서 @State를 관리할 것
    • 뷰를 초기화할때, @State 프로퍼티 값도 같이 초기화하게되면 SwiftUI에서 @State 프로퍼티를 관리하는 공간인 Storage에서 conflict가 나기 때문
  • 만약 상위 뷰에서의 State값을 하위 뷰에서도 접근할 수 있게하려면 Binding을 하위 뷰에 넘겨주거나 상위 뷰에서 read-only property로 설정하여 값을 공유

Binding 이란?

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

  • 신뢰적으로 값을 읽고 쓸수 있는 property wrapper 타입
    • "바인딩"의 의미: 자식 뷰의 특정 property 값이 변경되면 부모의 특정 property 값도 변경 (+양방향)
    • 위 State에서 알아본대로 바인딩을 통해 데이터를 표시하고 변경하는 뷰 간에 양방향 연결을 만드는 것
  • 내부 코드
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen @propertyWrapper @dynamicMemberLookup public struct Binding<Value> {
    public var transaction: Transaction
    
    public init(get: @escaping () -> Value, set: @escaping (Value) -> Void)
    public init(get: @escaping () -> Value, set: @escaping (Value, Transaction) -> Void)
    
    public static func constant(_ value: Value) -> Binding<Value>
    
    public var wrappedValue: Value { get nonmutating set }
    public var projectedValue: Binding<Value> { get }
    
    public init(projectedValue: Binding<Value>)
    
    public subscript<Subject>(dynamicMember keyPath: WritableKeyPath<Value, Subject>) -> Binding<Subject> { get }
}

ex) Binding property

  • @Binding 으로 선언
struct PlayButton: View {
  @Binding var isPlaying: Bool // <-
  
  var body: some View {
    Button(isPlaying ? "Pause" : "Play") {
      isPlaying.toggle()
    }
  }
}
  • State안의 projected value
struct ContentView: View {
  @State private var isPlaying = false
  
  var body: some View {
    PlayButton(isPlaying: $isPlaying) // <-
      .onAppear {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
          print("isPaying = \(isPlaying)")
        }
      }
  }
}

* 전체 코드: https://github.com/JK0369/ExState-SwiftUI

* 참고

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

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

Comments