SwiftUI - PropertyWrapper, State, Binding

Whale·2022년 6월 27일
0

SwiftUI 기초학습

목록 보기
4/7

뷰는 자신이 가진 데이터만으로 그려지지 않는다. 보통 상위 뷰에서 데이터를 전달받고, 전달받은 데이터를 가지고 놀다가 다시 상위뷰로 전달하는 등, 유기적으로 데이터를 이동해야한다.

Swift UI 에서는 이를위해 State 와 Binding 이라는 Property Wrapper 가 있다.

@frozen @propertyWrapper public struct State<Value> : DynamicProperty 
@frozen @propertyWrapper @dynamicMemberLookup public struct Binding<Value>

Property Wrapper...? 그게 뭔데?

State, Binding 의 help 를 타고들어가보면 propertyWrapper 란 녀석을 발견할 수 있다. 이게 무언지 알아야 개념적 이해가 생긴다.

Property Wrapper?

프로퍼티 래퍼란 특정 값을 특정 목적으로 변환하도록 한번 감싸는 역할을 한다고 볼 수 있다.
아래는 String 값을 받으면 대문자로 반환하는 프로퍼티 래퍼의 샘플이다.

@propertyWrapper
struct UppercaseMaker {
    private var _string: String = ""
    var wrappedValue: String {
        get { self._string.uppercased() }
        set { self._string = newValue }
    }
}

struct People {
    @UppercaseMaker var name: String
}

var p = People()
p.name = "j.r.r Tollkiehn"
print(p.name)

UppercaseMaker 는 받을땐 아무 스트링이나 받지만(set) 내보낼땐(get) 해당 스트링에 uppercased 가 실행되도록 작성되었고, People 의 name 이 선언될때 이 프로퍼티 래퍼를 사용하도록 지정했다.

해당 코드를 실행해보면 소문자가 포함된 j.r.r TollkiehnJ.R.R TOLLKIEHN 으로 프린트되는것을 볼 수 있다.

즉, 이를통해 보면 State, Binding 모두 특정 값을 받으면 어떠한 로직적 처리 가 진행된다고 이해할 수 있다.

여기서 어떠한 작업이란? 바로 뷰에대한 갱신이다.

State 와 binding

프로퍼티 래퍼가 뭔지 알았으니 이제 State와 Binding 을 알아보기위해 샘플을 작성했다.

  • State : 값이 셋팅되면 엮여있는 뷰에 새로운 값을 적용하는 Property Wrapper.
  • Binding : 값이 세팅되면 할당된 State 에 새로운값을 전달하는 Property Wrapper.

간단하게 뷰를 만들고 하위뷰로 버튼이 있는 뷰를 생성. 원하는 목적은 버튼이 눌렸을때 백그라운드 컬러를 바꾸는 기능이다.

일단 백그라운드 컬러를 State 프로퍼티래퍼를 단 변수로 선언하고, 기존에 ZStack 안에 있는 Color 을 대체한다.

struct StateBindingTestView: View {
    
    @State var backgroundColor: Color = .green
    
    var body: some View {
        ZStack {
            backgroundColor
                .edgesIgnoringSafeArea(.all)
            ButtonView()
        }
    }
}

이제 State 로 선언된 백그라운드 컬러값이 바뀌면 그에 엮여있는 배경색이 스스로 전환된다.

여기에 백그라운드를 바꾸는 역할은 버튼뷰에서 처리되도록 버튼뷰에 Binding 프로퍼티 래퍼를 추가.

약간의 잔재주를 부려서 버튼액션이 발생할때 isSelected 를 토글하도록 하고, 토글될때마다 그 값을 본 후 부모의 배경색을 변경하도록 한다.

struct ButtonView: View {
    
    @State var isSelected: Bool = false
    @Binding var parentBackgroundColor: Color
    
    var body: some View {
        Button {
            print("action!")
            self.isSelected = !self.isSelected
            
            if self.isSelected {
                parentBackgroundColor = .red
            } else {
                parentBackgroundColor = .green
            }
            
        } label: {
            Text("Press Button!")
                .foregroundColor(Color.white)
        }
        .padding()
        .background(
            Capsule().foregroundColor(Color.blue)
        )

    }
}

버튼뷰가 Binding 프로퍼티 래퍼를 가지게되면 부모뷰에 있는 생성자가 에러가 나게 되는데, 여기에 머니사인($)을 달아서 backgroundColor 을 지정하게되면 State -> Binding 에 할당된다.

ButtonView(parentBackgroundColor: $backgroundColor)

이제 실행해보면 버튼이 눌릴때마다 Background Color 이 변경되면서 상위뷰의 컬러가 변경되는것을 볼 수 있다.

마무리

복잡하게 적긴 했지만, 결국 심플하게 보면 뷰에 State를 이용해서 변경되어야 하는 뷰를 만들어두고, Binding 으로 엮어서 원하는 시점에 값을 변경하면 된다.

나중에 ViewModel 에서 값을 제어하려면 다른 프로퍼티 래퍼를 써야하는데.. 그건 다음포스트에서.

profile
그저 오래된 iOS 개발자.

0개의 댓글