Starbucks Caramel Frappuccino
본문 바로가기
  • 그래 그렇게 조금씩
SwiftUI/SwiftUI(Basic)

[60] @FocusState / TextField

by Toughie 2023. 5. 14.

 ⭐️@FocusState / TextField⭐️

온보딩 페이지나 로그인하는 단계에서, 키보드가 자동으로 올라오고
입력을 마치면 다음 텍스트필드로 이동하며, 빈칸이 있으면 빈칸으로 커서가 이동하는 것은

매끄러운 사용자 경험에 큰 도움이 된다. 이를 구현하기 위한 @FocusState에 대해 알아보자.

 

@FocusState는 *포커스 상태(focus state)를 관리하기 위해 사용되는 프로퍼티 래퍼이다.

*포커스 상태 - 사용자가 특정 뷰나 컨트롤에 '입력 포커스를 주었는지' 나타내는 Bool값이다.

@FocusState를 활용하면 포커스가 되는 시점에 특정한 동작을 수행하도록 구현할 수 있다.

 

아래 예시를 통해 살펴보자.

1. 화면에 진입하면 잠시 뒤에 키보드가 자동으로 올라오며 아이디 입력창에 입력 포커스

2. 아이디 입력을 완료하고 로그인 버튼을 누르면 비밀번호 입력창으로 포커스 이동

3. 비밀번호만 입력된 경우 로그인 버튼을 누르면 아이디 입력창으로 포커스 이동

4. 아이디와 비밀번호 입력이 완료된 경우 키보드 내리기

//  Created by Toughie on 2023/05/13.
//

import SwiftUI

//value는 Hashable 프로토콜 채택 필요.
//열거형을 통해 효율적인 코드 작성
enum OnboardingFields: Hashable {
    case userId
    case userPassword
}

struct FocustStatePrac: View {
// 열거형 활용 없이 개별 포커스 관리하는 경우
//    @FocusState private var userIdInFocus: Bool
//    @FocusState private var userPasswordInFocus: Bool

//텍스트필드 바인딩용
    @State private var userId: String = ""
    @State private var userPassword: String = ""
    
    //옵셔널로 선언, 여기서는 onAppear 시점에 초기화
    @FocusState private var fieldInFocus: OnboardingFields?
    
    var body: some View {
        VStack {
            TextField("아이디를 입력해 주세요.", text: $userId)
                .focused($fieldInFocus, equals: .userId)
//                .focused($userIdInFocus)

                .padding(.leading)
                .frame(height: 60)
                .frame(maxWidth: .infinity)
                .background(Color.gray.brightness(0.2))
                .cornerRadius(10)
                .shadow(color: .gray, radius: 10, y: 5)
                .padding()
            // 입력값 마스킹 필드
            SecureField("비밀번호를 입력해 주세요.", text: $userPassword)
//                .focused($userPasswordInFocus)
                .focused($fieldInFocus, equals: .userPassword)
                
                .padding(.leading)
                .frame(height: 60)
                .frame(maxWidth: .infinity)
                .background(Color.gray.brightness(0.2))
                .cornerRadius(10)
                .shadow(color: .gray, radius: 10, y: 5)
                .padding()
            
            Button {
                let idIsValid = !userId.isEmpty
                let passwordIsValid = !userPassword.isEmpty
                
                if idIsValid && passwordIsValid {
                //키보드 내리기, 콘솔창으로 확인
                    fieldInFocus = nil
                    print("로그인 성공")
                    //아이디 입력만 마친 경우 비밀번호 입력창으로 커서 이동
                } else if idIsValid {
                    fieldInFocus = .userPassword
//                    userIdInFocus = false
//                    userPasswordInFocus = true
					//비밀번호 입력만 마친 경우 아이디 입력창으로 커서 이동
                } else {
                    fieldInFocus = .userId
//                    userIdInFocus = true
//                    userPasswordInFocus = false
                }
            } label: {
                Text("로그인")
                    .font(.title2)
                    .bold()
                    .foregroundColor(.white)
                    .padding()
                    .background(Color.gray)
                    .cornerRadius(20)
                    .shadow(radius: 10)
            }
        }
        //화면 진입 후 1.2초 후 아이디 입력창에 커서, 키보드 팝업
        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) {
                self.fieldInFocus = .userId
            }
        }
    }
}

UIKit에서는 이런 구현이 예전부터 가능했는데 SwiftUI에서는 뒤늦게 생긴 것으로 보인다.

로그인 구현이 필요한 경우 꼭 적용해봐야겠다.

'SwiftUI > SwiftUI(Basic)' 카테고리의 다른 글

[61] .submitLabel / .onSubmit  (0) 2023.05.14
[59] Badge 뱃지  (0) 2023.05.13
[58] List Swipe Actions  (0) 2023.05.12
[57] .buttonStyle / .controlSize / .buttonBorderShape  (0) 2023.05.12
[56] TextSelection / Window, Scene  (0) 2023.05.12