Tab키로 다음 TextEditor로 이동하기 (feat. @FocusState)

SteadySlower·2022년 8월 6일
0
post-custom-banner

구현할 기능

보통 브라우저에서 키보드의 Tab키를 입력하면 다음 입력창으로 넘어가게 됩니다. 브라우저를 자주 사용하다보니 이게 습관이 되어서 Mac의 TextEditor에 입력을 할 때도 자연스럽게 Tab키를 통해서 다음 TextEditor로 커서를 옮기고 싶습니다.

우리가 구현하고자 하는 기능은 아래와 같습니다. 첫번째 TextEditor에서 입력을 끝내고 다음 TextEditor로 이동할 때 Tab키를 사용하는 것이죠.

구현

KeyboardShortcut를 사용할까?

처음에는 숨겨진 버튼을 만들고 keyboardShortcut을 통해서 Tab키를 누를 때 작동하도록 할 생각이었습니다. 그리고 moveCursor라는 메소드를 만들어서 first responder를 두번째 TextEditor로 바꿀 생각이었습니다.

Button("") {
    moveCursor()
}
.keyboardShortcut(.tab)
.hidden()

func moveCursor() {
	// first responder를 두번째 TextEditor로 바꾸기
}

하지만 우리의 의도와는 달리 TextEditor에 텍스트를 입력하고 있을 때 Tab키를 입력하면 4칸 띄어쓰기가 됩니다. 정확히 말하면 입력하고 있는 텍스트에 “\t”를 추가하는 것이죠.

@FocusState를 활용하기

공식문서에 있는 @FocusState의 정의는 다음과 같습니다.

우리말로 간단하게 말하면 scene 안에서 focus의 위치를 업데이트할 수 있는 값을 의미합니다.

View에 @FocusState로 변수를 추가합니다. 아래 변수는 첫번째 TextEditor에서 Tab키가 입력되었을 때 true가 될 것입니다.

@FocusState private var isFrontFinishEditing: Bool

다음은 해당 변수를 두 번째 TextEditor에 연결해서 true가 되었을 때 focus가 될 수 있도록 정의해줍시다.

TextEditor(text: $viewModel.backText)
  .font(.system(size: 30))
  .frame(height: Constants.Size.deviceHeight / 8)
  .padding(.horizontal)
  .focused($isFrontFinishEditing, equals: true)

마지막으로 첫 번째 TextEditor에서 Tab키를 입력했을 때 isFrontFinishEditing변수를 true로 만들어주는 코드입니다. 아까도 말했듯이 TextEditor에서 Tab키를 입력하면 “\t”가 입력됩니다. 따라서 onChange를 통해서 viewModel.frontText를 observe하고 있다고 “\t”가 입력되면 isFrontFinishEditing를 true로 바꾸어 줍니다.

.onChange(of: viewModel.frontText) { newValue in
    guard let last = newValue.last else { return }
    if last == "\t" {
        viewModel.frontText.removeLast() //👉 "\t" 입력은 취소
        isFrontFinishEditing = true
    }
}
profile
백과사전 보다 항해일지(혹은 표류일지)를 지향합니다.
post-custom-banner

0개의 댓글