앞서 SwiftUI에서 UIKit를 사용하기 위해 UIViewRepresentable 프로토콜을 사용하는 방법을 간단히 알아보았다. 이제 여기서 궁금한 것은 SwiftUI와 UIKit 간에 데이터를 주고받고 변화를 감지할 수 있는가이다. 우리는 @Binding property와 Coordinator를 사용하면 적절한 상황에서 SwiftUI와 UIKit 간에 데이터를 주고받을 수 있다.
SwiftUI에서 UIKit 사용하기 UIViewRepresentable (1) | TextField에서 clear button 사용하고 싶다면
SwiftUI에서 UIKit 사용하기 UIViewRepresentable(2) | @Binding 이란? Coordinator 란?
# 🥨 @Binding property
@Binding property를 사용하면 SwiftUI -> UIKit 방향으로 데이터를 전달할 수 있다. UIViewRepresentable 프로토콜을 채택한 구조체에서 SwiftUI로부터 @Binding property를 넘겨받았다면, 해당 프로퍼티 값이 바뀔때마다 구조체에서 정의한 updateUIView 메서드가 호출되는 것을 확인할 수 있다.
실제로 아래 코드는 "toggle"을 누를때마다 구조체의 updateUIView 메서드가 호출된다.
struct UIVIewRepresentablePracticeView: View {
@State var binding : Bool = true
@State var comment : String = "글자수를 넘기지 않았습니다."
var body: some View {
VStack {
Text(comment)
UIKitTextField(binding : self.$binding, comment : self.$comment)
.frame(height : 40)
Text("toggle")
.onTapGesture {
binding.toggle()
}
}
}
}
struct UIKitTextField : UIViewRepresentable {
private let textField = UITextField()
@Binding var binding : Bool
@Binding var comment : String
func makeUIView(context: UIViewRepresentableContext<UIKitTextField>) -> UITextField{
//textField의 style
textField.backgroundColor = .gray
textField.layer.cornerRadius = 3
return textField
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<UIKitTextField>){
print("updateUIView : ", binding)
}
}
그렇다면 SwiftUI로부터받은 데이터를 바꾸고 이 바뀐 내용을 SwiftUI 쪽에 전달하려면 어떻게 해야 할까?
@Binding property로 받은 binding이라는 변수의 값이 바뀔 때마다 updateUIView 메서드가 호출된다고 했는데, 그럼 이 함수가 호출될 때마다 @Binding property로 받은 comment라는 변수에도 변화를 줄 수 있을까? 답은 아니오이다. 아마 아래와 같이 코드를 작성해 주면 Modifying state during view update, this will cause undefined behavior 에러가 날것이다.
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<UIKitTextField>){
comment = "----"
print("updateUIView : ", binding)
}
구조체에서 @Binding property로 받은 변수에 변화를 주고 싶을 때 필요한 것이 coordinator이다.
# 🥨 Coordinator
위에서 만든 뷰에서 TextField에서 적은 글자의 수가 10자를 넘겼을 때 comment 변수를 변화시켜 SwiftUI에서 해당 변수의 변동을 감지할 수 있도록 해보자.
우선 Coordinator를 사용하기 위해서는 아래의 세 가지를 완료해야 한다.
1) makeCoordinator 함수 생성
2) TextFieldDelegate 프로토콜을 준수하는 Coordinator 클래스 생성
3) delegate 연결해 주기
struct UIKitTextField : UIViewRepresentable {
private let textField = UITextField()
@Binding var binding : Bool
@Binding var comment : String
func makeUIView(context: UIViewRepresentableContext<UIKitTextField>) -> UITextField{
//3) delegate 연결해주기
//textField의 delegate
textField.delegate = context.coordinator
//textField의 style
textField.backgroundColor = .gray
textField.layer.cornerRadius = 3
return textField
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<UIKitTextField>){
print("updateUIView : ", binding)
}
//1) makeCoordinator 함수 생성
func makeCoordinator() -> UIKitTextField.Coordinator {
print("makeCoordinator")
return UIKitTextField.Coordinator(self)
}
//2) TextFieldDelegate 프로토콜을 준수하는 Coordinator 클래스 생성
class Coordinator: NSObject, UITextFieldDelegate {
var parent : UIKitTextField
init(_ textField: UIKitTextField) {
self.parent = textField
}
}
}
다음으로는 UIKit에서 TextField 구현 시 많이 사용하는 UITextFieldDelegate관련 메서드를 사용하여 글자를 10자 이상 썼을 때 comment 변수를 변화시켜 보자. 위의 구조체에서 Coordinator 클래스 부분만 수정해 주면 된다. UITextFieldDelegate 프로토콜을 채택한 클래스는 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool 함수를 사용할 수 있으며, 이를 이용해서 comment 변수를 를 바꾸어줄 수 있다.
class Coordinator: NSObject, UITextFieldDelegate {
var parent : UIKitTextField
init(_ textField: UIKitTextField) {
self.parent = textField
}
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
// return (textField.text! + string).count <= 10
if (textField.text! + string).count > 10 {
parent.comment = "글자수가 초과되었습니다."
} else {
parent.comment = "글자수를 넘기지 않았습니다."
}
return true
}
}
실행시켜 보면 구조체에서 바꾸어준 변수가 SwiftUI에 잘 전달되어 뷰에서 적용되는 것을 볼 수 있다.
'SwiftUI' 카테고리의 다른 글
[SwiftUI] UIViewControllerRepresentable프로토콜로 갤러리에서 이미지 선택하기 (0) | 2023.06.20 |
---|---|
[SwiftUI] Custom Picker로 textField 입력값 받기 (0) | 2023.06.12 |
SwiftUI에서 UIKit 사용하기 UIViewRepresentable (1) | TextField에서 clear button 사용하고 싶다면 (0) | 2023.06.08 |
SwiftUI 다양한 토글 형태를 만들어 주기 위한 ToggleStyle 프로토콜 (0) | 2023.06.06 |
SwiftUI 재사용가능한 뷰를 만들기 위해 필요한 @ViewBuilder (0) | 2023.06.05 |