Use UIViewRepresentable to convert UIKit views to SwiftUI | Advanced Learning #13
UITextField
커스텀 컴포넌트를 구현한다.Coordinator
클래스 구현UITextField
를 초기화하는 함수를 구현한다. func makeUIView(context: Context) -> some UIView {
let textField = getTextField()
textField.delegate = context.coordinator
return textField
}
//From SwiftUI To UIKit
func updateUIView(_ uiView: UIViewType, context: Context) {
guard let textField = uiView as? UITextField else { return }
textField.text = text
}
class Coordinator: NSObject, UITextFieldDelegate {
@Binding var text: String
init(text: Binding<String>) {
self._text = text
}
func textFieldDidChangeSelection(_ textField: UITextField) {
text = textField.text ?? ""
}
}
UITextFiled
컴포넌트는 UIKit
이 제공하는 컴포넌트UIViewRepresentable
프로토콜을 통해 SwiftUI
와 UIKit
을 연결 가능makeUIView
함수를 통해 SwiftUI
에서 사용할 컴포넌트를 리턴updateUIView
함수를 통해 SwiftUI
의 데이터가 UIKit
컴포넌트의 데이터로 바인딩makeCoordinator
함수를 통해 UIkit
의 데이터가 SwiftUI
의 데이터로 바인딩 → Binding<>
타입으로 선언된 값을 통해 연결, 해당 커스텀 클래스 컴포넌트 이니셜라이즈 시 프로퍼티로 전달된 값import SwiftUI
struct TextFieldViewModifier: ViewModifier {
func body(content: Content) -> some View {
content
.frame(height: 55)
.background(Color.gray.opacity(0.3))
.cornerRadius(10)
.padding(.horizontal)
}
}
extension View {
func withDefaultUITextFieldModifier() -> some View {
modifier(TextFieldViewModifier())
}
}
struct UIViewRepresentableBootCamp: View {
@State private var text: String = ""
@State private var placeholder: String = "TYPE HERE..."
var body: some View {
VStack {
Text(text)
.font(.headline)
.fontWeight(.bold)
HStack {
Text("SwiftUI : ")
TextField("TYPE HERE...", text: $text)
.withDefaultUITextFieldModifier()
}
HStack {
Text("UIKit : ")
UITextFieldViewRepresentable(text: $text, placeholder: placeholder, placeholderColor: .gray)
.updatePlaceholder(placeholder)
.withDefaultUITextFieldModifier()
}
}
}
}
struct UITextFieldViewRepresentable: UIViewRepresentable {
@Binding var text: String
var placeholder: String
let placeholderColor: UIColor
init(text: Binding<String>, placeholder: String = "PLACEHOLDER", placeholderColor: UIColor = .gray) {
self._text = text
self.placeholder = placeholder
self.placeholderColor = placeholderColor
}
func makeUIView(context: Context) -> some UIView {
let textField = getTextField()
textField.delegate = context.coordinator
return textField
}
//From SwiftUI To UIKit
func updateUIView(_ uiView: UIViewType, context: Context) {
guard let textField = uiView as? UITextField else { return }
textField.text = text
}
//From UIKit to SwiftUI
func makeCoordinator() -> Coordinator {
// CUSTOM INSTANCE BETWEEN INTERFACES
return Coordinator(text: $text)
}
func updatePlaceholder(_ text: String) -> UITextFieldViewRepresentable {
print("UPDATE PLACEHOLDER")
var viewRepresentable = self
viewRepresentable.placeholder = text
return viewRepresentable
}
private func getTextField() -> UITextField {
let textField = UITextField(frame: .zero)
let placeholder = NSAttributedString(string: placeholder, attributes: [.foregroundColor: placeholderColor])
textField.attributedPlaceholder = placeholder
return textField
}
class Coordinator: NSObject, UITextFieldDelegate {
@Binding var text: String
init(text: Binding<String>) {
self._text = text
}
func textFieldDidChangeSelection(_ textField: UITextField) {
text = textField.text ?? ""
}
}
}
UIKit
의 컴포넌트의 데이터 바인딩을 사용하기 위한 델리게이트 패턴이 Coordinate
클래스를 통해 구현NSObject
를 따르고 있는 해당 클래스 내에서 활용할 기존 UIKit
컴포넌트의 메소드(textFieldDidChangeSelection
)를 사용 가능, UITextFieldViewRepresentable
클래스를 이니셜라이즈할 때 받은 바인딩 프로퍼티와 연결makeUIView
메소드를 통해 해당 컨텍스트의 Coordinate
가 곧 리턴하는 컴포넌트 오브젝트의 델리게이트로 연결SwiftUI
에서의 UIKit
연결, UIKit
에서의 SwiftUI
연결 가능UIKit이 제공하는 강력한 커스텀 기능을 SwiftUI에서 활용하기 위해 능숙해져야 할 기법 중 하나라고 생각한다!