SwiftUI에서 데이터 바인딩이 필요한 이유

SwiftUI에서 View는 struct(구조체)형태이다.
따라서 구조체이기에 내부에서 프로퍼티를 변경하는 것이 불가능하다.


struct BlogUpload: View {
    var str = "A"
    
    var body: some View {
        VStack{
            Text(str)
            Button("Change") {
                str = "B"
            }
        }
    }
}

사진


다음 코드와 같이 A로 정의된 값을 내부에서 B로 변경하려고 할 때,
‘Cannot assign to property: ‘self’is immutable과 같은 오류가 발생한다.

따라서 이와 같은 문제를 해결하기 위해 @State를 사용한 데이터 바인딩이 필요하다.


💡 @State

A property wrapper type that can read and write a value managed by SwiftUI



사용법

변수 앞에 @State 붙이기만 하면 됨

@State var str = "A"


  • Property Wrapper : 변화가 생기면 해당 변수의 값을 읽거나 새로 쓸 수 있다.
  • State value 값이 변경되면 뷰는 해당 value의 apperance를 무효화 하고 새롭게 body 값을 계산
  • State 변수값이 변경되면 view를 다시 랜더링하기 때문에 항상 최신 값을 가짐
  • State 인스턴스는 그 자체로 값이 아닌, 값을 읽고 쓰는 것을 의미
  • @State 속성으로 어떤 프로퍼티의 초기값을 지정했다면, 다른 값으로 재할당 불가능 -> **@Binding** 변수를 통해서만 가능
  • @State는 private이고 다른 View와 공유되지 않음
  • SwiftUI는 state로 선언된 property들의 저장소를 관리 -> Property Wrapper로 감싼 변수는 View가 소유하고 있지 않는다.


struct BlogUpload: View {
    @State var str = "A"
    
    var body: some View {
        VStack{
            Text(str)
            Button("Change") {
                str = "B"
            }
        }
    }
}

앞선 코드를 @State를 사용해 정의해주게 되면 더이상 오류가 발생 하지 않는다.

사진



📝 Docs

공식문서


 @State private var isPlaying: Bool = false // Create the state. 

App, Scene 또는 View에서 상태 값을 생성하려면, 속성 선언에 @State 속성(attribute)을 적용하고 초기값을 제공해야 한다.
private 선언을 통해 저장 관리(storage management)와 충돌 방지



공식문서


SwfitUI에서는 속성들의 저장을 자동으로 관리
값이 변경되면 이에 영향 받는 뷰 계층의 일부분을 업데이트

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.
        }
    }
}

isPlaying 상태 속성의 래핑된 값을 직접 참조하여 읽고 쓰기 가능


공식문서


상태(state)를 사용하는 가장 높은 뷰 계층(highest view in the view hierarchy) 중에서 상태 값을 필요로 하는 뷰에서 해당 상태를 private으로 선언
하위 뷰들은 이를 직접적으로 읽기 전용으로 접근하거나, 읽기 및 쓰기 권한을 위해 바인딩(binding)으로 공유 가능
이러한 상태(state) 속성은 어떤 스레드(thread)에서든 안전하게 변경 가능



⭐️ 정리

  • SwiftUI에서는 상태(state)를 사용하여 값을 저장하고 관리할 수 있으며, 상태를 private으로 선언하여 안전하게 관리하는 것이 중요
  • @State 속성을 사용하여 초기값을 제공하여 SwiftUI에서 해당 상태 값을 적절하게 처리할 수 있도록 해야 함
  • SwiftUI에서는 State 속성의 저장과 업데이트를 자동으로 처리하며, 래핑된 값을 직접 참조하여 액세스 가능
  • SwiftUI에서는 상태(state)를 최상위 뷰에서 선언하고, @Binding으로 하위 뷰들에게 전달하여 상태 값을 공유하고, 이를 안전하게 변경 가능


💡 @StateObject

pic

값 타입을 저장할 경우 @State를 사용하지만, 참조 타입 (Reference Type)인 클래스(Class) 인스턴스와 같은 값을 저장할 경우, @StateObject를 사용
클래스(Class)는 객체(Object)를 생성해 참조 타입으로 사용

class Person {
    var name: String
    var phoneNumber: String
    var email: String

    init(name: String, phoneNumber: String, email: String) {
        self.name = name
        self.phoneNumber = phoneNumber
        self.email = email
    }
}

struct PersonView: View {
    @StateObject var person = Person(name: "John Doe", phoneNumber: "010-1234-5678", email: "johndoe@example.com")

    var body: some View {
        VStack {
            Text(person.name)
            Text(person.phoneNumber)
            Text(person.email)
        }
    }
}


💡 @Binding

A property wrapper type that can read and write a value owned by a source of truth

@State로 선언된 속성을 다른 뷰에서 사용하려 할 때 @Binding 사용


사용법

변수 앞에 $(달러)을 붙여 바인딩 변수임을 나타냄

struct BlogUpload: View {
    @State var toggle = false
    
    var body: some View {
        VStack{
            Toggle("토글버튼", isOn: $toggle)
            
        }
    }
}

  • 외부에서 접근해야 하기 때문에 private X


📝 Docs

pic


바인딩은 데이터를 직접 저장하지 않고 대신 다른 곳에 저장된 속성을 연결
즉, 바인딩은 뷰 간에 데이터를 공유하고 연결하는 방법 중 하나
양방향 연결을 통해 뷰에서 변경된 데이터를 속성에 즉시 반영 가능, 속성에서 변경된 데이터를 뷰에 반영 가능
데이터의 일관성과 신뢰성을 유지하며 뷰 업데이트 가능



👀 예시

import SwiftUI

struct BlogUpload: View {
    @State var num : Int = 160
    @State var isShowingModal: Bool = false
    
    var body: some View {
        VStack{
            Text("Now, the number is \(num)")
                .font(.system(size: 30))
                .bold()
                .padding(.bottom, 40)
            
            Button(action:
                    {self.num += 1},
                   label: {Text("Add number")
                    .frame(width: 200, height: 50)
                    .background(Color.black)
                    .cornerRadius(15)
                    .foregroundColor(.white)
                    .font(.system(size: 20))
            })
            .padding(.bottom, 40)
            
            Button(action:
                    {isShowingModal = true},
                   label: {Text("Show Modal")
                    .frame(width: 200, height: 50)
                    .background()
                    .shadow(radius: 10)
                    .foregroundColor(.black)
            }) .sheet(isPresented: $isShowingModal){
                ZStack{
                    Color.black.ignoresSafeArea()
                    Text("Check the number : \(num)")
                        .font(.system(size: 30))
                        .foregroundColor(.white)
                    
                }
            }
            
        }
    }
}


사진 사진