본문 바로가기

Apple/SwiftUI

SwiftUI 05 - @Binding 사용법

반응형

SwiftUI에서 하나의 뷰 내부에서 값의 변동을 공유하기 위해 @State 프로퍼티 래퍼를 사용한다. 하지만 뷰 사이에 같은 값을 사용하는 @State를 사용하고 그 변수의 값이 변경되는 경우 하나의 뷰에서는 값이 변경되지 않는다. 말이 요상한데 아래와 같은 예시를 살펴보면 이해가 더 빠르다.

아래 코드는 두가지 뷰로 이루어져있고, 하나는 메인뷰, 하나는 커스텀 버튼 뷰이다. 원하는 기능은 버튼을 클릭했을 때 버튼 내부의 텍스트가 Dribbling에서 Shooting으로 바뀌고, 버튼의 배경색에 blue에서 orange로 바뀌는 것이고, 메인뷰의 텍스트 메시지가 shooting에서 sonny is on fire 로 바뀌는 것이다. 뷰 내부의 값들의 변경을 캐치하기 위해 두 가지 부울 변수를 @State 프로퍼티 래퍼로 선언했다. 

import SwiftUI

// 커스텀 버튼 뷰
struct ShootingButtonView: View {
    @State var isShoot: Bool
    @State var isGoal: Bool
    
    var body: some View {
        Button {
        	// 버튼 클릭하면 isShoot 토글
            isShooting.toggle()
        	// 버튼 클릭하면 isGoal 토글
            isGoal.toggle()
        } label: {
        	// 버튼 클릭하면 isShooting 의 결과에 따라 버튼의 텍스트 변경
            Text(isShoot ? "Shooting" : "Dribbling")
                .padding(.vertical, 20)
                .padding(.horizontal, 40)
                .background(isGoal ? Color.orange : Color.blue)
                .foregroundColor(.white)
                .cornerRadius(30)
        }
    }
}

// 메인 뷰
struct ContentView: View {
    @State private var isShoot = false
    @State private var isGoal = false
    
    var body: some View {
        VStack {
            ShootingButtonView(isShoot: isShoot, isGoal: isGoal)
            // isGoal의 값에 따라 버튼 아래의 텍스트 메시지 변경
            Text(isGoal ? "sonny is on FIRE!!🔥" : "shooting")
                .padding(.top, 40)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

하지만 이렇게 하면 버튼의 배경색상과 버튼의 텍스트는 바뀌지만 메인뷰의 텍스트는 변경되지 않는다.  또 다른 문제는 버튼을 한 번 눌렀을 때 버튼 뷰의 isGoal 의 변수에는  toggle로 인해 isGoal의 값이 true로 변경되지만 메인뷰의 isGoal  변수는 그대로 false를 유지하게 되는 한 뷰에 두 가지의 값이 공존하는 이상한 형태가 된다. 

이를 해결하기 위해 @Binding 프로퍼티 래퍼를 사용한다. 커스텀 버튼 뷰의 isGoal 변수를 @Binding으로 선언하고 값 변경을 변수에 다시 반영하기 위해 메인뷰에서 isGoal을 $isGoal로 바꿔준다 바뀐 코드는 아래와 같다.

import SwiftUI

struct PushButton: View {
    @State var isShoot: Bool
    @Binding var isGoal: Bool // @State를 @Binding으로
    
    var body: some View {
        Button {
            isShoot.toggle()
            isGoal.toggle()
        } label: {
            Text(isShoot ? "Shooting" : "Dribbling")
                .padding(.vertical, 20)
                .padding(.horizontal, 40)
                .background(isGoal ? Color.orange : Color.blue)
                .foregroundColor(.white)
                .cornerRadius(30)
        }
    }
}

struct ContentView: View {
    @State private var isShoot = false
    @State private var isGoal = false
    
    var body: some View {
        VStack {
            PushButton(isShoot: isShoot, isGoal: $isGoal) // isGoal을 $isGoal로
            Text(isGoal ? "sonny is on FIRE!!🔥" : "shooting")
                .padding(.top, 40)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

이렇게 되면 커스텀버튼뷰에서 바뀐 isGoal의 값이 메인뷰의 isGoal 변수에 반영되고 그 결과 메인뷰의 텍스트 메시지가 변경된다.

반응형

'Apple > SwiftUI' 카테고리의 다른 글

SwiftUI 04 - @State, @StateObject  (0) 2022.03.15
SwiftUI03 - Stepper  (0) 2022.03.14
SwiftUI02 - Button  (0) 2022.03.14
SwiftUI01 - Stack(HStack, VStack, ZStack)  (0) 2022.03.14