apple/SwiftUI & Combine

SwiftUI @FoucsState @FocusedValue @FocusedObject

lgvv 2024. 8. 8. 00:52

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")
        }
    }
}

 

FocusState 예제

 

 

 

@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()
}

 

 

FocusValue

 

 

@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)
    }
}

FocusObject



 

 

(참고)
https://www.appcoda.com/focusstate/

 

Understanding @FocusState, @FocusedValue and @FocusedObject

In any user interface, focus plays a crucial role in determining which element receives the next input. SwiftUI provides a powerful set of tools and view modifiers that allow you to control and manage focus in your apps. By using these modifiers, you can i

www.appcoda.com