[SwiftUI] @Binding, _, $

상 원·2022년 7월 31일
0

SwiftUI

목록 보기
2/4
post-thumbnail

이전 포스팅에서 썼던 @State와 관련된 프로퍼티 래퍼인 @Binding을 알아보자!

Bind. 당연히 묶는다는 의미임!
여기서는 데이터끼리 묶어서 뷰와 뷰끼리 데이터 공유가 가능하도록 만들어주는 프로퍼티 래퍼이다.

여기서는 ContentView의 isActivated 를 MyVStackView의 isActivated 에 묶어서 나타내려구 함.

일단 ContentView.swift에는

@State private var isActivated: Bool = false

가 선언돼 있는 상태!

이제 MyVStackView에다가 이 변수를 연동해서 사용할 바인딩 프로퍼티를 만들어 보자!

import SwiftUI

struct MyVStackView: View{
    
    // 데이터 연동시킬 binding, 다른곳에서 접근해야 하기에 private 안씀
    @Binding var isActivated: Bool
    
    init(isActivated: Binding<Bool> = .constant(true)){
       _isActivated = isActivated
    }
    // 여기까지 추가했음
    var body: some View{
        VStack{
            Text("1!")
                .fontWeight(.bold)
                .font(.system(size: 60))
            Text("2!")
                .fontWeight(.bold)
                .font(.system(size: 60))
            Text("3!")
                .fontWeight(.bold)
                .font(.system(size: 60))
        }
        .background(self.isActivated ? .green : .red)
        .padding(self.isActivated ? 10 : 0)
    }
}

우선 외부에서 접근해야 하기에 private 키워드는 쓰지 않았음.

@Binding var isActivated: Bool 이제 이렇게 만들어둔 isActivated에 ContentView의 @State로 선언해둔 isActivated를 받아오는 거임!

일단 그걸 하기 위해서 초기화를 해 줬는데, 저 이니셜라이저는 좀 생소하긴 함.
긴장하지말고 천천히 살펴보자..

일단 이 이니셜라이저는 MyVStackView를 초기화하는 이니셜라이저임. 초기화할 저장 프로퍼티가 하나밖에 없어서 인자가 하나인 거고, @Binding이라는 프로퍼티 래퍼로 감싸져 있는 저장 프로퍼티이기에 제네릭 타입으로 받을 값의 타입을 정해둔 것!

초기값을 지정해 두기 위해 .constant(true)라는 값을 넣어줬고, _isActivated = isActivated 라는 구문은 원래의 isActivated에 이니셜라이저의 인자의 값으로 받아오는 isActivated라는 새로운 값을 넣어주는 구문임!

_isActivated 의 뜻은 대체 무엇 ?

근데 대체 변수 앞에 _ 왜넣는거임? 함수 매개변수 이름 생략하는 건 알겠는디..
그래서 찾아봤따! 궁금증을 속시원하게 해결해 준 사람이 한명 있었음!!

Claude31 선생님 감사합니다..

일단 이건 옵젝씨에서 전해 내려오는 구문인데, 이름이 같은 두 변수를 구분해주기 위해서 사용됐다고 함.

언더스코어( _ )가 있는 변수가 진짜 로컬한 변수라는 것!
self.isActivated_isActivated 가 같은 거!!!!

근데 Swift에서는 self.isActivated를 사용하면 되니까 이제 안쓰는 문법인데, 가끔씩 쓰긴 한다구 함.
궁금증 해~!결~!


암튼 이제 @Binding 변수 선언이 끝났으니까 데이터를 연결해주려면!

ContentView.swift에서 MyVStackView를 호출했던 곳 있잖아? 거기다가 매개변수로 ContentView에서 사용하던 isActivated 값을 넣어주면 끝!

import SwiftUI

struct ContentView: View {
    @State private var isActivated: Bool = false
    
    var body: some View {
        NavigationView{
            VStack{
                HStack{
                // 여기를 바꿈!!
                    MyVStackView(isActivated: $isActivated)
                    MyVStackView(isActivated: $isActivated)
                    MyVStackView(isActivated: $isActivated)
                }
                .padding(isActivated ? 50 : 10)
                .background(isActivated ? .yellow : .black)
                .onTapGesture {
                    print("HSTack clicked")
                    withAnimation{
                        self.isActivated.toggle()
                    }
                } // HStack
                
                NavigationLink(destination: MyTextView()){
                    Text("navButton")
                        .font(.system(size: 40, weight: .bold))
                        .padding()
                        .background(.orange)
                        .foregroundColor(.white)
                        .cornerRadius(30)
                }.padding(.top, 50)
            }
        } // NavigationView
    }
}

MyVStackView(isActivated: $isActivated) 이렇게 호출해주면 두 데이터가 연동이 된다. 그래서 @State 로 선언해둔 isActivated가 바뀔 때마다 @Binding으로 선언해둔 것도 바껴서 MyVStackView에서도 바뀐 값을 사용할 수 있는 것!

근데 이걸 매개변수로 전달할 때 전달하는 값이 좀 이상한디? 왜 $를 붙이지??

State에 바인딩된 값을 projectedValue라고 하는데, 이 값에 접근하려면 $(dollar sign)을 붙여야 함!


이제 isActivated를 MyTextView에도 넣기 위해서 코드를 수정해주면

import SwiftUI

struct MyTextView: View{
    @State private var index: Int = 0
    @Binding var isActivated: Bool
    
    init(isActivated: Binding<Bool> = .constant(false))
    {
        _isActivated = isActivated
    }
    
    private let backgroundColors = [Color.red, Color.yellow, Color.blue, Color.green, Color.orange]
    
    var body: some View{
        VStack{
            Spacer()
            Text("Background item Index \(self.index + 1)")
                .font(.system(size: 30, weight: .bold))
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 100)
            Text("활성화 상태: \(String(isActivated))")
                .font(.system(size: 30, weight: .bold))
                .foregroundColor(self.isActivated ? .yellow : .gray)
                .background(.black)
            Spacer()
        }
        .background(backgroundColors[index])
        .edgesIgnoringSafeArea(.all)
        .onTapGesture {
            print("background item clicked")
            if(self.index == self.backgroundColors.count - 1){
                self.index = 0
            }
            else{
                self.index+=1
            }
        }
    }
}
이런식으로 됨!

한 가지 State를 공유하기 위해 Binding을 쓴다는 걸 명심하기!

이제 ContentView에서 MyTextView 호출문구를 수정해 주면 끝!

import SwiftUI

struct ContentView: View {
    @State private var isActivated: Bool = false
    
    var body: some View {
        NavigationView{
            VStack{
                HStack{
                    MyVStackView(isActivated: $isActivated)
                    MyVStackView(isActivated: $isActivated)
                    MyVStackView(isActivated: $isActivated)
                }
                .padding(isActivated ? 50 : 10)
                .background(isActivated ? .yellow : .black)
                .onTapGesture {
                    print("HSTack clicked")
                    withAnimation{
                        self.isActivated.toggle()
                    }
                } // HStack
                //여기 수정!
                NavigationLink(destination: MyTextView(isActivated: $isActivated)){
                    Text("navButton")
                        .font(.system(size: 40, weight: .bold))
                        .padding()
                        .background(.orange)
                        .foregroundColor(.white)
                        .cornerRadius(30)
                }.padding(.top, 50)
            }
        } // NavigationView
    }
}

이런식으로 고쳐주면 된다.

그러면 결과물은~


이렇게 된당.

profile
ios developer

0개의 댓글