안녕하세요! iOS와 macOS에서 사용자 인터페이스를 구축하기 위한 프레임워크 인 SwiftUI 에 대해 말씀드리고 싶습니다. 프로그래밍에 선언적 접근 방식을 사용하기 때문에 사용이 매우 편리합니다. SwiftUI를 사용하면 인터페이스의 기능과 모양을 설명할 수 있으며 나머지는 프레임워크가 처리합니다.
SwiftUI의 핵심 요소 중 하나는 속성 래퍼를 사용하는 것입니다. 이는 속성에 대한 추가 논리를 제공할 수 있는 기능적 요소입니다.
@상태
@제본
@ObservedObject
@StateObject
@EnvironmentObject
그들은 개발 과정에서 가장 친한 친구가 될 것입니다.
@State를 사용하면 변경할 수 있는 속성을 생성하고 필요한 경우 이러한 변경 사항에 따라 인터페이스를 업데이트할 수 있습니다. 예를 들어, 눌렀을 때 색상이 변경되는 버튼을 생성하려면 @State 변수를 생성하여 색상을 저장하고 버튼에 추가할 수 있습니다.
struct MyButton: View { @State var buttonColor = Color.blue var body: some View { Button("Press me!") { buttonColor = Color.red } .background(buttonColor) } }
@Binding을 사용하면 코드의 한 부분에 저장된 값을 코드의 다른 부분에서 사용할 수 있습니다. 이는 일반적으로 SwiftUI에서 한 뷰에서 다른 뷰로 값을 전달하여 서로 상호 작용할 수 있도록 하는 데 사용됩니다. 예를 들어 두 개의 보기(하나는 텍스트 필드가 있고 다른 하나는 버튼이 있음)가 있다고 가정해 보세요. 사용자가 버튼을 누를 때 텍스트 필드가 업데이트되기를 원합니다. 이를 위해 @Binding을 사용할 수 있습니다.
struct ContentView: View { @State private var text = "" var body: some View { VStack { TextField("Enter text", text: $text) Button("Update text") { text = "New text" } SecondView(text: $text) } } } struct SecondView: View { @Binding var text: String var body: some View { Text(text) } }
이 예제에서 @Binding은 $text ( ContentView 에 있음)의 값을 text ( SecondView 에 있음)로 전달하는 데 사용되므로 사용자가 버튼을 누르면 텍스트 필드가 업데이트되어 새 텍스트가 표시됩니다.
@ObservedObject는 관찰되고 외부 데이터 변경에 따라 변경될 수 있는 속성을 표시하는 데 사용됩니다 . 이 속성 래퍼는 ObservableObject 프로토콜을 준수하는 객체의 변경 사항을 구독하고 데이터가 변경된 경우 인터페이스의 관련 부분을 자동으로 업데이트합니다. @ObservedObject 사용에 대한 간단한 예는 다음과 같습니다.
class UserData: ObservableObject { @Published var name = "John" } struct ContentView: View { @ObservedObject var userData = UserData() var body: some View { VStack { Text("Hello, \(userData.name)!") TextField("Enter your name", text: $userData.name) } } }
이 예에서는 @Published 이름이 포함된 UserData라는 클래스를 만듭니다. ContentView 구조에서 @ObservedObject를 사용하여 UserData 유형의 userData라는 속성을 생성합니다. userData.name의 값을 텍스트 필드에 표시하고 이를 화면에 출력합니다.
사용자가 텍스트 필드 의 값을 변경하면 @Published를 사용하여 name 속성이 게시되고 관찰되므로 SwiftUI는 인터페이스의 해당 부분을 자동으로 업데이트합니다. 이는 인터페이스를 업데이트하기 위해 자체 코드가 필요하지 않으며 SwiftUI가 이를 수행하도록 허용한다는 의미입니다.
참고: 모르는 경우 @Published는 클래스 또는 구조 속성에 추가할 수 있는 Combine 프레임워크의 속성 래퍼입니다. 이 래퍼는 해당 속성 값에 대한 변경 사항에 대한 알림을 구독한 모든 사람에게 자동으로 보냅니다. . 즉, 변경 사항을 추적할 수 있는 속성에 대한 도우미 특성입니다.
@StateObject는 클래스 객체를 초기화하고 이를 SwiftUI의 뷰 상태에 저장하는 데 사용되는 속성 래퍼입니다. 이는 뷰가 존재하는 한 객체가 저장되고 뷰와 함께 삭제됨을 의미합니다. 일반적으로 @StateObject를 사용하는 것은 하나의 뷰가 아닌 여러 뷰에 필요한 클래스 객체에 더 실용적입니다 . 예를 들어:
class UserData: ObservableObject { @Published var name = "John" @Published var age = 30 } struct ContentView: View { @StateObject var userData = UserData() var body: some View { NavigationView { VStack { Text("Name: \(userData.name)") Text("Age: \(userData.age)") NavigationLink( destination: ProfileView(userData: userData), label: { Text("Edit Profile") }) } .navigationTitle("Home") } } } struct ProfileView: View { @ObservedObject var userData: UserData var body: some View { Form { TextField("Name", text: $userData.name) Stepper("Age: \(userData.age)", value: $userData.age) } .navigationTitle("Profile") } }
이 예제에서 UserData는 여러 보기에서 사용할 수 있는 여러 속성을 포함하는 클래스의 개체입니다. 클래스는 @StateObject 및 @ObservedObject 와 함께 사용할 수 있도록 ObservableObject 로 표시됩니다.
ContentView 에서는 @StateObject를 사용하여 새로운 UserData 객체를 생성하여 서로 다른 뷰 간 전환 사이의 상태를 저장합니다. 이 경우 ContentView는 사용자 데이터를 표시하고 시각화하며 사용자 데이터를 편집하는 데 사용할 수 있는 다른 보기 (ProfileView) 에 대한 링크를 포함합니다.
ProfileView 에서는 사용자 데이터를 수정하기 위해 @ObservedObject를 사용하여 동일한 UserData 개체에 액세스합니다. 사용자가 데이터를 변경하면 동일한 UserData 객체가 사용되므로 ContentView 에서 자동으로 업데이트됩니다.
참고: 하나의 뷰에서 클래스 객체의 변경 사항을 관찰해야 하는 경우 @ObservedObject를 사용하고, 여러 뷰의 표시에 영향을 미치는 클래스 객체의 상태를 저장해야 하는 경우 @StateObject를 사용하세요.
여러 뷰에 필요한 객체에 대해 @StateObject 대신 @ObservedObject를 사용하면 각 뷰는 객체의 자체 인스턴스를 가지게 되어 뷰 간 데이터 동기화에 문제가 발생할 수 있습니다. 따라서 이 경우에는 @StateObject를 사용하는 것이 좋습니다.
@EnvironmentObject는 SwiftUI 뷰 계층을 통해 데이터 객체를 전달하기 위한 속성 래퍼입니다. 환경 컨테이너(예: 장면, 보기, 앱 등)에 속하는 SwiftUI 계층 구조의 모든 보기에서 데이터 객체에 액세스할 수 있습니다. 예를 들어 작업 목록 관리 앱이 있다고 가정해 보겠습니다. 작업 목록과 새 작업을 생성하는 기능이 포함된 루트 ContentView를 가질 수 있습니다. 이를 위해 작업 목록과 새 작업을 추가하는 버튼을 표시하는 별도의 TaskListView 보기를 만듭니다. 새 작업을 추가한 후 사용자는 작업 추가 화면으로 리디렉션되어야 하므로 별도의 AddTaskView 뷰를 만듭니다.
UserManager 개체를 세 가지 보기 모두에 전달하려면 ContentView 에서 해당 인스턴스를 만든 다음 이를 TaskListView 및 AddTaskView 모두에 매개 변수로 전달할 수 있습니다. 그러나 많은 중간 뷰를 통해 UserManager를 전달해야 하므로 더 많은 중첩 뷰를 추가하기로 결정하면 문제가 될 수 있습니다.
대신에 @EnvironmentObject를 사용하여 뷰 계층 구조를 통해 UserManager를 전달할 수 있습니다. 이렇게 하면 UserManager 에 액세스해야 하는 모든 뷰는 간단히 @EnvironmentObject 로 선언하고 필요에 따라 사용할 수 있습니다.
struct TaskManagerApp: App { @StateObject var userManager = UserManager() var body: some Scene { WindowGroup { ContentView() .environmentObject(userManager) } } } struct ContentView: View { var body: some View { NavigationView { TaskListView() } } } struct TaskListView: View { @EnvironmentObject var userManager: UserManager var body: some View { List(userManager.tasks) { task in TaskRow(task: task) } .navigationBarTitle("Tasks") .navigationBarItems(trailing: Button(action: { // Navigate to AddTaskView }) { Image(systemName: "plus") } ) } } struct AddTaskView: View { @EnvironmentObject var userManager: UserManager var body: some View { // Add new task using userManager } }
따라서 이제 UserManager 개체는 @EnvironmentObject를 통해 TaskListView 및 AddTaskView 에 자동으로 전달됩니다. 한 보기에서 UserManager 의 상태를 수정할 수 있으며 변경 사항은 자동으로 다른 보기에 반영됩니다.
이 기사에서는 기본 SwiftUI 속성 래퍼인 @State, @Binding, @ObservedObject, @StateObject, @EnvironmentObject를 다루었습니다.
이러한 속성 래퍼는 SwiftUI에서 앱 상태 작업의 기초를 형성합니다. SwiftUI로 앱을 개발하는 데 필요한 기본 속성 래퍼를 손쉽게 사용할 수 있도록 이 문서를 치트 시트로 사용하세요. 이 지식을 적용하면 상태가 동적으로 변경되는 더욱 복잡한 사용자 인터페이스를 구축하고 모델의 데이터를 SwiftUI에 통합할 수 있습니다.