Created
June 6, 2020 08:11
-
-
Save dlgchg/3a9d89b82a844794a109d57f8b03d75c to your computer and use it in GitHub Desktop.
Revisions
-
dlgchg created this gist
Jun 6, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,140 @@ // // MultilineTextField.swift // GipsyToDo // // Created by liwei on 2020/6/4. // Copyright © 2020 liwei. All rights reserved. // import Foundation import SwiftUI import UIKit struct MultilineTextField: View { private var placeholder: String private var onCommit: (() -> Void)? @State private var viewHeight: CGFloat = 40 //start with one line @State private var shouldShowPlaceholder = false @Binding private var text: String @Binding private var showKeyBoard: Bool private var internalText: Binding<String> { Binding<String>(get: { self.text } ) { self.text = $0 self.shouldShowPlaceholder = $0.isEmpty } } var body: some View { UITextViewWrapper(text: self.internalText, showKeyBoard: $showKeyBoard, calculatedHeight: $viewHeight, onDone: onCommit) .frame(minHeight: viewHeight, maxHeight: viewHeight) .background(placeholderView, alignment: .topLeading) } var placeholderView: some View { Group { if shouldShowPlaceholder { Text(placeholder).foregroundColor(.gray) .padding(.leading, 4) .padding(.top, 8) } } } init (_ placeholder: String = "", text: Binding<String>, showKeyBoard: Binding<Bool>, onCommit: (() -> Void)? = nil) { self.placeholder = placeholder self.onCommit = onCommit self._text = text self._showKeyBoard = showKeyBoard self._shouldShowPlaceholder = State<Bool>(initialValue: self.text.isEmpty) } } private struct UITextViewWrapper: UIViewRepresentable { typealias UIViewType = UITextView @Binding var text: String @Binding var showKeyBoard: Bool @Binding var calculatedHeight: CGFloat var onDone: (() -> Void)? func makeUIView(context: UIViewRepresentableContext<UITextViewWrapper>) -> UITextView { let textField = UITextView() textField.delegate = context.coordinator textField.isEditable = true textField.font = UIFont.preferredFont(forTextStyle: .body) textField.isSelectable = true textField.isUserInteractionEnabled = true textField.isScrollEnabled = false textField.backgroundColor = UIColor.clear if nil != onDone { textField.returnKeyType = .done } textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) return textField } func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<UITextViewWrapper>) { if uiView.text != self.text { uiView.text = self.text } if self.showKeyBoard { uiView.becomeFirstResponder() } else { uiView.resignFirstResponder() } // if uiView.window != nil, !uiView.isFirstResponder { // uiView.becomeFirstResponder() // } UITextViewWrapper.recalculateHeight(view: uiView, result: $calculatedHeight) } private static func recalculateHeight(view: UIView, result: Binding<CGFloat>) { let newSize = view.sizeThatFits(CGSize(width: view.frame.size.width, height: CGFloat.greatestFiniteMagnitude)) if result.wrappedValue != newSize.height { DispatchQueue.main.async { result.wrappedValue = newSize.height // call in next render cycle. } } } func makeCoordinator() -> Coordinator { return Coordinator(text: $text, showKeyBoard: $showKeyBoard, height: $calculatedHeight, onDone: onDone) } final class Coordinator: NSObject, UITextViewDelegate { var text: Binding<String> var showKeyBoard: Binding<Bool> var calculatedHeight: Binding<CGFloat> var onDone: (() -> Void)? init(text: Binding<String>,showKeyBoard: Binding<Bool>, height: Binding<CGFloat>, onDone: (() -> Void)? = nil) { self.text = text self.showKeyBoard = showKeyBoard self.calculatedHeight = height self.onDone = onDone } func textViewDidChange(_ uiView: UITextView) { text.wrappedValue = uiView.text UITextViewWrapper.recalculateHeight(view: uiView, result: calculatedHeight) } func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { if let onDone = self.onDone, text == "\n" { textView.resignFirstResponder() onDone() return false } return true } } }