Notice
Recent Posts
Recent Comments
Link
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

Jupyo's Daily Story

@FocusState 본문

SwiftUI

@FocusState

JangJupyo 2025. 6. 4. 14:44
728x90
반응형

@FocusState는 SwiftUI에서 특정 View의 포커스 상태(Focus State)를 프로그램적으로 관리할 수 있도록 해주는 속성 래퍼(property wrapper)입니다. 키보드 입력이나 다른 UI 상호작용을 통해 포커스를 특정 View로 이동시키거나, 현재 포커스된 View가 무엇인지 알아낼 때 주로 사용됩니다.

 

@FocusState는 주로 텍스트 필드(TextField, TextEditor)와 같이 사용자 입력을 받는 View에서 유용하게 사용됩니다.

 

왜 @FocusState가 필요한가?

기존에는 특정 텍스트 필드에 포커스를 주려면 becomeFirstResponder() 와 같은 UIKit 메서드를 직접 호출해야 했습니다. 이는 SwiftUI의 선언적인 특성과 잘 맞지 않았고, UIKit의 UIViewRepresentable이나 NSViewRepresentable을 통해 통합해야 하는 번거로움이 있었습니다.

 

@FocusState는 이러한 문제를 SwiftUI스러운 방식으로 해결해줍니다. View의 포커스 상태를 Bool 값 또는 열거형(enum)을 통해 선언적으로 바인딩하여 관리할 수 있게 합니다.

 

@FocusState의 기본 작동 방식

@FocusStateBool 타입 또는 Hashable을 준수하는 열거형 타입과 함께 사용될 수 있습니다.

  • Bool타입 사용(단일 View의 포커스 관리)
    가장 간단한 형태로, 하나의  View의 포커스 상태를 관리할 때 사용합니다.
struct FocusExampleView: View {
    @FocusState private var isInputActive: Bool // 포커스 상태를 나타내는 Bool 변수
    
    var body: some View {
    	VStack {
            TextField("Enter text here", text: .contant(""))
            	.focused($isInputActive) // isInputActive 변수와 TextField의 포커스 상태를 바인딩
                .textFieldStyle(.roundedBorder)
                .padding()
                
            Button("Toggle Focus") {
            	isInputActive.toggle() // 버튼을 누르면 포커스 상태를 토글
            }
            .padding()
            
            Button("Dismiss Keyboard") {
            	isInputActive = false // 키보드 내리기 (포커스 해제)
            }
            .padding()
        }
    }
}
  • isInputActive : isInputActive 라는 Bool 타입의 상태변수를 선언합니다. 이 변수가 true이면 해당 View에 포커스가 있고, false이면 포커스가 없습니다.
  • .focused($isInputActive) : 이 modifier는 TextField의 포커스 상태를 $isInputActive와 바인딩합니다. 즉, TextField에 포커스가 가면 isInputActivetrue가 되고, isInputActivetrue로 설정하면 TextField에 포커스가 가게 됩니다.
  • Hashable 열거형 타입 사용 (여러 View의 포커스 관리)
    여러 개의 텍스트 필드가 있고, 특정 순서대로 포커스를 이동시키거나, 어떤 필드가 현재 포커스되어 있는지 명확히 알고 싶을 때 유용합니다.
struct MultiFocusExampleView: View {
    enum Field: Hashable {
    	case username
        case password
        case email
    }
    
    @FocusState private var focusedField: Field? // 열거형 타입으로 포커스 상태 관리
    
    @State private var username: String = ""
    @State private var password: String = ""
    @State private var email: String = ""
    
    var body: some View {
    	VStack {
            TextField("Username", text: $username)
            	.focused($focusedField, equals: .username) // focusedField가 .username일 때 포커스
                .textFieldStyle(.roundedBorder)
                .padding()
                .onSubmit { focusedField = .password } // Enter 키 누르면 다음 필드로 이동

            SecureField("Password", text: $password)
            	.focused($focusedField, equals: .password)
                .textFieldStyle(.roundedBorder)
                .padding()
                .onSubmit { focusedField = .email }
                
            TextField("Email", text: $username)
            	.focused($focusedField, equals: .email)
                .textFieldStyle(.roundedBorder)
                .padding()
                .onSubmit { focusedField = nil } // 마지막 필드에서 Enter 누르면 포커스 해제
                
            Button("Set Focus to Password") {
            	focusedField = .password // 버튼으로 특정 필드에 포커스 설정
            }
            .padding()
            
            if let currentFocus = focusedField {
            	Text("Currently focused: \(String(describing: currentFocus))")
            } else {
            	Text("No field is focused.")
            }
        }
        .padding()
    }
}
  • enum Field: Hashable { ... } : 포커스 가능한 각 필드를 나타내는 열거형을 정의합니다. Hashable 프로토콜을 준수해야 합니다.
  • focusedField : 열거형 타입으로 focusedField를 선언합니다. Optional로 선언하는 이유는 아무 필드도 포커스되지 않은 상태를 나타내기 위함입니다.
  • .focused($focusedField, equals: .username) : 이 modifier는 TextField의 포커스 상태를 focusedField와 바인딩하되, focusedField가 .username 일 때 해당 TextField에 포커스가 있음을 나타냅니다.
  • .onSubmit { focusedField = .password } : TextField에서 Enter 또 Return 키를 눌렀을 때 다음 필드로 포커스를 이동시키는 일반적인 패턴입니다.

 

@FocusState의 주요 기능 및 사용처

  • 키보드 자동 올리기/내리기 : 앱이 시작될 때 특정 TextField에 자동으로 포커스를 주거나, 특정 이벤트 발생 시 키보드를 내리는 등의 작업을 수행할 수 있습니다.
// 앱 시작 시 자동 포커스
.onAppear {
    isInputActive = true
}

 

  • 포커스 이동 제어 : onSubmit modifier와 함께 사용하여 사용자가 Enter/Return 키를 눌렀을 때 다음 입력 필드로 포커스를 자동으로 이동시킬 수 있습니다.
  • 현재 포커스된 View 확인 : focusedField와 같은 @FocusState 변수의 값을 읽어서 현재 어떤 View가 포커스되어 있는지 확인할 수 있습니다.
  • 유효성 검사 : 특정 필드에 포커스가 있을 때만 특정 UI를 표시하거나, 포커스를 잃었을 때 입력 값의 유효성을 검사하는 등의 로직을 구현할 수 있습니다.
  • 탭 또는 버튼을 통한 포커스 이동 : 버튼을 클릭하여 원하는 텍스트 필드로 포커스를 직접 이동시킬 수 있습니다.

 

@FocusState 사용 시 주의사항

  • @FocusState는 View계층 구조 내에서 사용되어야 합니다. @FocusState 변수를 선언한 View와 .focused modifier를 적용한 View가 동일한 View계층 내에 있어야 정상적으로 작동합니다.
  • SwiftUI의 View Life Cycle 고려 : onAppear, onChange 등의 modifier 내에서 @FocusState 값을 변경하여 포커스를 제어할 때, View의 라이프사이클을 이해하고 적절한 시점을 호출해야 합니다.
  • 키보드 사라짐 제어 : focusedField = nil 또는 isInputActive = false 와 같이 @FocusState 값을 nil 또는 false로 설정하면 키보드를 내릴 수 있습니다.
  • 적절한 위치에 선언 : @FocusState 변수는 View의 @State나 @Environment와 유사하게, 해당 View의 상태로 선언되어야 합니다.

 

결론

@FocusState는 SwiftUI에서 사용자 입력 필드의 포커스 상태를 선언적이고 효율적으로 관리할 수 있게 해주는 핵심적인 도구입니다. 이를 통해 사용자 경험을 개선하고, 보다 동적이고 인터렉티브한 UI를 쉽게 구현할 수 있습니다. 특히 텍스트 입력이 많은 폼(form) 형태의 UI를 개발할 때 그 진가가 발휘됩니다.

반응형

'SwiftUI' 카테고리의 다른 글

@ViewBuilder  (0) 2025.06.04
GeometryReader  (2) 2024.09.27
@Environment  (0) 2024.09.27
@Published  (0) 2024.09.22
@ObservedObject  (0) 2024.09.21