// // ContentView.swift // textinput // // Created by Venin Alexander on 3/22/22. // import SwiftUI extension View { func hideKeyboard() { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } } struct InputViewRepresentable: UIViewRepresentable { @Binding var text: String @Binding var isEditing: Bool var keyboardType: UIKeyboardType = .asciiCapable var font: UIFont? func makeUIView(context: Context) -> UITextView { let textView = UITextView() textView.isSelectable = true textView.isUserInteractionEnabled = true textView.isEditable = true textView.delegate = context.coordinator textView.isScrollEnabled = false textView.backgroundColor = .clear textView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) textView.textContainer.lineFragmentPadding = 0 textView.autocorrectionType = .no textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) if let font = self.font { textView.font = font } return textView } func updateUIView(_ uiView: UITextView, context: Context) { uiView.text = text if let font = self.font { uiView.font = font } if uiView.text != self.text { uiView.text = self.text } if uiView.window != nil, isEditing { uiView.becomeFirstResponder() } } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITextViewDelegate { private let parent: InputViewRepresentable init(_ parent: InputViewRepresentable ) { self.parent = parent } func textViewDidChange(_ textView: UITextView) { DispatchQueue.main.async { [weak self] in self?.parent.text = textView.text } } func textViewDidBeginEditing(_ textView: UITextView) { DispatchQueue.main.async { [weak self] in self?.parent.isEditing = true } } func textViewDidEndEditing(_ textView: UITextView) { DispatchQueue.main.async { [weak self] in self?.parent.isEditing = false } } } } struct ContentView: View { @State var text: String = "" @State var isEditing: Bool = false @State var isValid = true var body: some View { VStack(spacing: 0) { Spacer() ScrollView(.vertical) { VStack(spacing: 40) { Spacer() validationErrorMessage InputViewRepresentable(text: $text, isEditing: $isEditing, keyboardType: .asciiCapable, font: UIFont.systemFont(ofSize: 20)) .border(Color.black) .padding(.top, 50) } .animation(.default, value: isValid) .animation(.default, value: isEditing) .padding(.horizontal, 20) }.frame(maxWidth: .infinity) Button("Validate") { hideKeyboard() isValid = false } } } @ViewBuilder private var validationErrorMessage: some View { if !isValid && !isEditing { Text("invalid input") .foregroundColor(Color.red) .transition(.opacity.combined(with: .move(edge: .top))) } } }