Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to make multiline message text view in SwiftUI #843

Open
onmyway133 opened this issue Aug 21, 2021 · 0 comments
Open

How to make multiline message text view in SwiftUI #843

onmyway133 opened this issue Aug 21, 2021 · 0 comments

Comments

@onmyway133
Copy link
Owner

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
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant