Issue #843

This can be used in message bar input or some popover form. We use sizeThatFits to calculate the height so that it grow under certain height limit

import SwiftUI
import UIKit

struct MessageTextField: View {
    let placeholder: String
    @Binding var text: String
    @Binding var isEditing: Bool

    @State private var height: CGFloat = 100

    var body: some View {
        UITextViewWrapper(
            text: $text,
            isEditing: $isEditing,
            height: $height
        )
        .frame(height: height)
        .background(placeholderView, alignment: .topLeading)
    }

    var placeholderView: some View {
        Group {
            if text.isEmpty {
                Text(placeholder)
                    .foregroundColor(Asset.textSecondary.color)
                    .font(.system(size: 16))
                    .padding(.leading)
            }
        }
    }
}

private struct UITextViewWrapper: UIViewRepresentable {
    typealias UIViewType = UITextView

    @Binding var text: String
    @Binding var isEditing: Bool
    @Binding var height: CGFloat

    private static let maxHeight: CGFloat = 100

    func makeUIView(context: Context) -> UITextView {
        let textField = UITextView()
        textField.delegate = context.coordinator

        textField.isEditable = true
        textField.font = UIFont.systemFont(ofSize: 16)
        textField.isSelectable = true
        textField.isUserInteractionEnabled = true
        textField.isScrollEnabled = true
        textField.backgroundColor = UIColor.clear

        textField.textColor = .textPrimary
        textField.tintColor = .textPrimary

        textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        return textField
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        if uiView.text != text {
            uiView.text = text
        }

        calculateHeight(view: uiView)
    }

    private func calculateHeight(
        view: UIView
    ) {
        let size = view.sizeThatFits(
            CGSize(
                width: view.frame.size.width,
                height: CGFloat.greatestFiniteMagnitude
            )
        )

        guard height != size.height else { return }
        DispatchQueue.main.async {
            height = min(size.height, Self.maxHeight)
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }

    final class Coordinator: NSObject, UITextViewDelegate {
        let parent: UITextViewWrapper

        init(parent: UITextViewWrapper) {
            self.parent = parent
        }

        func textViewDidChange(_ uiView: UITextView) {
            parent.text = uiView.text
        }

        func textViewDidBeginEditing(_: UITextView) {
            parent.isEditing = true
        }

        func textViewDidEndEditing(_: UITextView) {
            parent.isEditing = false
        }
    }
}