SwiftUI @FoucsState @FocusedValue @FocusedObject
SwiftUI @FoucsState @FocusedValue @FocusedObject
요약
@FocusState: 누가 포커스를 받았는지
@FocusedValue: 포커스 된 값이 뭔지 관찰
- 값이 존재하더라도, Focus가 풀린 경우에는 관찰하고 있는 부분에서는 nil로 된다.
extension FocusedValues {
@Entry var commentFocusedValue: String?
}
@FocusedObject: 여러 유형의 변경사항을 보다 효율적으로 관리함.
- @StateObject 차이점
- 값이 존재하더라도, Focus가 풀린 경우에는 관찰하고 있는 부분에서는 nil로 된다.
@FocusState
일반적으로 enum을 구성하여 사용.
Submit 버튼을 눌렀을 때 selectedField를 nil로 만듦.
nil이 아닌 다른 값을 줄 수도 있음.
`onChange`는 FocusState의 변화를 단순히 print 구문을 찍기 위함.
struct FocusStateDemoView: View {
enum Field: Hashable {
case name
case email
case comment
}
@State private var name: String = ""
@State private var email: String = ""
@State private var comment: String = ""
@FocusState private var selectedField: Field?
var body: some View {
VStack {
Text("👋Help us improve")
.font(.system(.largeTitle, design: .rounded, weight: .black))
TextField("Name", text: $name)
.padding()
.border(.gray, width: 1)
.focused($selectedField, equals: .name)
TextField("Email", text: $email)
.padding()
.border(.gray, width: 1)
.focused($selectedField, equals: .email)
TextField("Any comment?", text: $comment)
.padding()
.border(.gray, width: 1)
.focused($selectedField, equals: .comment)
Button("Submit") {
selectedField = nil
}
.controlSize(.extraLarge)
.buttonStyle(.borderedProminent)
}
.padding()
.onChange(of: selectedField) { oldValue, newValue in
print(newValue ?? "No field is selected")
}
}
}
@FocusValue
포커스 된 값을 관찰하기 위함.
extension FocusValue를 통해 필드를 선언한 후 CommentPreview에서 그 값을 받아서 사용.
단, @Binding과는 달리 포커스가 풀릴경우에는 CommentPreview는 nil로 변경됨.
아래는 샘플 코드
import SwiftUI
struct FocusStateDemoView: View {
enum Field: Hashable {
case name
case email
case comment
}
@State private var name: String = ""
@State private var email: String = ""
@State private var comment: String = ""
@FocusState private var selectedField: Field?
var body: some View {
VStack {
Text("👋Help us improve")
.font(.system(.largeTitle, design: .rounded, weight: .black))
TextField("Name", text: $name)
.padding()
.border(.gray, width: 1)
.focused($selectedField, equals: .name)
TextField("Email", text: $email)
.padding()
.border(.gray, width: 1)
.focused($selectedField, equals: .email)
TextField("Any comment?", text: $comment)
.padding()
.border(.gray, width: 1)
.focused($selectedField, equals: .comment)
TextField("Any comment?", text: $comment)
.padding()
.border(.gray, width: 1)
.focused($selectedField, equals: .comment)
.focusedValue(\.commentFocusedValue, comment)
Button("Submit") {
selectedField = nil
}
.controlSize(.extraLarge)
.buttonStyle(.borderedProminent)
Spacer()
CommentPreview()
}
.padding()
.onChange(of: selectedField) { oldValue, newValue in
print(newValue ?? "No field is selected")
}
}
}
extension FocusedValues {
@Entry var commentFocusedValue: String?
}
struct CommentPreview: View {
@FocusedValue(\.commentFocusedValue) var comment
var body: some View {
VStack {
Text(comment ?? "Not focused")
}
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 100)
.padding()
.background(.yellow)
}
}
#Preview {
FocusStateDemoView()
}
@FocusObject
@FocusValue의 변경을 모니터링 하는데 사용.
ObservableObject를 채택해서 사용하면 된다.
@StateObject 및 @ObservedObject와의 차이점은 @FocusObject는 @FocusState의 selectedField가 nil로 바뀌었을 때 해당 값을 참조하는 name, email, comment에 실제 값이 들어있더라도 commenetPreivew는 nil로 된다.
아래는 사용 예제
class FormViewModel: ObservableObject {
@Published var name: String = ""
@Published var email: String = ""
@Published var comment: String = ""
}
struct FocusStateDemoView: View {
enum Field: Hashable {
case name
case email
case comment
}
@FocusState private var selectedField: Field?
@StateObject private var viewModel: FormViewModel = FormViewModel()
var body: some View {
VStack {
Text("👋Help us improve")
.font(.system(.largeTitle, design: .rounded, weight: .black))
TextField("Name", text: $viewModel.name)
.padding()
.border(.gray, width: 1)
.focused($selectedField, equals: .name)
.focusedObject(viewModel)
TextField("Email", text: $viewModel.email)
.padding()
.border(.gray, width: 1)
.focused($selectedField, equals: .email)
.focusedObject(viewModel)
TextField("Any comment?", text: $viewModel.comment)
.padding()
.border(.gray, width: 1)
.focused($selectedField, equals: .comment)
.focusedObject(viewModel)
Button("Submit") {
selectedField = nil
}
.controlSize(.extraLarge)
.buttonStyle(.borderedProminent)
Spacer()
CommentPreview()
}
.padding()
.onChange(of: selectedField) { oldValue, newValue in
print(newValue ?? "No field is selected")
}
}
}
struct CommentPreview: View {
@FocusedObject var viewModel: FormViewModel?
var body: some View {
VStack {
Text(viewModel?.name ?? "Not focused")
Text(viewModel?.email ?? "Not focused")
Text(viewModel?.comment ?? "Not focused")
}
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 100)
.padding()
.background(.yellow)
}
}
(참고)
https://www.appcoda.com/focusstate/