Tunko Development Diary

SwiftUI) 실전형 TextField 사용하기 예제 [@FocusState, ScrollView movemunt] 본문

Development/SwiftUI

SwiftUI) 실전형 TextField 사용하기 예제 [@FocusState, ScrollView movemunt]

Tunko 2022. 9. 23. 01:48

실제 앱을 구현시 유저가 컨텐츠를 입력하는 부분에서 꼭 필요할것 같은 기능을 복합적으로 구현해보았습니다.

 

마음과 다르게 3개이상의 기능을 복합적으로 넣고 글을 작성하려고 하니 어려움이 많았습니다. 

최하단에 전체 코드를 먼저 보시는것을 추천드립니다. 🥹

 

구현 목표

  1. 화면 전환직후 TextField 활성화 키보드 나타남
  2. 화면 터치시 키보드 가리기
  3. TextField 터치시 입력하기 좋은위치로 스크롤 이동 (키보드에 UI가려짐 방지)

완성형태

 

설명과 무관한 코드는 …을 통해 생략했습니다.

1.  화면 전환직후 TextField 활성화 키보드 나타남

@FocusState var focusedField : FeildFocus?

...

TextField(...)
	.focused($focusedField, equals: autoFocusViewModel.fields[index].feildFocus)

...
.onAppear {         
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        self.focusedField = .feild1
    }
}

@FocusState 프로퍼티 레퍼로 선언한 focusedField를

TextField에 .focused에 적용해줍니다.

이렇게 하면 focusedField가 autoFocusViewModel.fields[index].feildFocus 이것과 같아지면 활성화 됩니다.

autoFocusViewModel.fields[index].feildFocus 는 단순한 Int 값입니다.

2. 화면 터치시 키보드 가리기

extension View {
    func hideKeyboard() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}
최상위뷰 
.onTapGesture {
    hideKeyboard()
}

최상위뷰를 터치할때 키보드가 가려지도록 해줍니다.

3. TextField 터치시 입력하기 좋은위치로 스크롤 이동 (키보드에 UI가려짐 방지)

...
ScrollViewReader { proxy in
	ScrollView{
    	VStack{
            TextField(...)
                .onTapGesture {
              		withAnimation {
                  		proxy.scrollTo(index, anchor: .top)
              		}
          		}
				Rectangle()
                    .id(index)
				}
    		}
		}
	}
...

해당 기능을 구현하는데 필요한 코드만 줄여서 남겼습니다.

TextField에 탭 제스쳐를 설정하고 해당 이벤트가 발생할 때 ScrollViewReader proxy를 통하여 원하는 위치에 있는 View로 이동합니다.

여기서 중요한건 index 부분입니다. index는 다른 뷰의 id로 활용되며 해당 id를 가진 뷰로 스크롤링하게 됩니다. anchor는 해당 뷰의 스크롤 위치 입니다.

위코드를 보면 Rectangle에 id를 설정했으며 Rectangle의 top부분에 스크롤이 됩니다.

전체 코드

import SwiftUI

struct feildModel : Identifiable {
    var id = UUID()
    var text : String
    var placeHolder : String
    var color : Color
    var fieldFocus : FieldFocus
}

enum FieldFocus : Int {
    case feild1 = 0
    case feild2 = 1
    case feild3 = 2
    case feild4 = 3
    case feild5 = 4
    case feild6 = 5
    case feild7 = 6
}

class AutoFocusViewModel : ObservableObject {
    @Published var fields = [feildModel]()
    func setData() {
        fields = [feildModel(text: "", placeHolder: "placeHolder1", color: Color.red, fieldFocus : .feild1),
                  feildModel(text: "", placeHolder: "placeHolder2", color: Color.orange, fieldFocus : .feild2),
                  feildModel(text: "", placeHolder: "placeHolder3", color: Color.yellow, fieldFocus : .feild3),
                  feildModel(text: "", placeHolder: "placeHolder4", color: Color.green, fieldFocus : .feild4),
                  feildModel(text: "", placeHolder: "placeHolder5", color: Color.cyan, fieldFocus : .feild5),
                  feildModel(text: "", placeHolder: "placeHolder6", color: Color.blue, fieldFocus : .feild6),
                  feildModel(text: "", placeHolder: "placeHolder7", color: Color.purple, fieldFocus : .feild7)]
    }
}

extension View {
    func hideKeyboard() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

struct AutoFocusTextFeild: View {
    
    @StateObject var autoFocusViewModel = AutoFocusViewModel()
    @FocusState var focusedField : FieldFocus?

    var body: some View {
        ScrollViewReader { proxy in
            ScrollView{
                VStack{
                    ForEach(0..<autoFocusViewModel.fields.count, id: \.self) { index in
                        TextField(autoFocusViewModel.fields[index].placeHolder, text: $autoFocusViewModel.fields[index].text)
                            .focused($focusedField, equals: autoFocusViewModel.fields[index].fieldFocus)
                            .padding()
                            .background {
                                RoundedRectangle(cornerRadius: 5, style: .continuous)
                                    .fill(Color.gray.opacity(0.1))
                            }
                            .border(.black)
                            .padding()
                            .onTapGesture {
                                withAnimation {
                                    proxy.scrollTo(index, anchor: .top)
                                }
                            }
                        Rectangle()
                            .fill(autoFocusViewModel.fields[index].color)
                            .frame(height: 300)
                            .id(index)
                    }
                }
            }
        }
        .onTapGesture {
            hideKeyboard()
        }
        .onAppear {
            autoFocusViewModel.setData()
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                self.focusedField = .feild1
            }
        }
    }
}

struct AutoFocusTextFeild_Previews: PreviewProvider {
    static var previews: some View {
        AutoFocusTextFeild()
    }
}

이상입니다 🙂

반응형
Comments