Использование `UIViewRepresentable` для соединения` UITextView`, который увеличивается и сжимается, чтобы соответствовать его тексту в SwiftUI

Если я хочу без прокрутки UITextView который занимает всю ширину и увеличивается и сжимается по вертикали, чтобы соответствовать тексту, я могу сделать это в UIKit следующим образом:

import UIKit

class TestViewController: UIViewController {
    private lazy var textView = UITextView()

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let view = view else { return }

        view.backgroundColor = .white

        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
        textView.backgroundColor = .systemYellow
        textView.isEditable = true
        textView.isSelectable = true
        textView.isScrollEnabled = false // We want the view to resize to fit text instead of scrolling

        view.addSubview(textView)

        textView.translatesAutoresizingMaskIntoConstraints = false
        let constraints = [
            textView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            textView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
        ]

        NSLayoutConstraint.activate(constraints)
    }
}

А это выглядит так:

Я хочу связать это со SwiftUI, обернув UITextView с участием UIViewRepresentable. Я сделал это так, с текстовым представлением, настроенным точно так же, как в примере UIKit:

import SwiftUI
import UIKit

struct TextView: UIViewRepresentable {
    @Binding var text: String

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

        textView.backgroundColor = .clear
        textView.isEditable = true
        textView.isSelectable = true
        textView.isScrollEnabled = false // We want the view to resize to fit text instead of scrolling

        // Makes the text wrap rather than extend on one line outside the parent frame
        textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

        return textView
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(text: _text)
    }

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

extension TextView {
    class Coordinator: NSObject, UITextViewDelegate {
        @Binding var text: String

        init(text: Binding<String>) {
            self._text = text
        }

        func textViewDidChange(_ textView: UITextView) {
            self.text = textView.text
        }
    }
}

И использовал его в SwiftUI вот так:

import SwiftUI

struct ContentView: View {
    @State var text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

    var body: some View {
        VStack(alignment: .leading) {
            TextView(text: $text)
                .background(Color.yellow)
            Spacer()
        }
    }
}

Настройка textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) успешно заставляет текстовое представление переносить его текст на несколько строк, но высота заполняет весь экран:

Добавление textView.setContentHuggingPriority(.defaultHigh, for: .vertical)уменьшает высоту, но теперь перенос строк больше не работает; весь текст находится в одной строке, выходящей за пределы рамки:

Я не нашел слишком много в документации или в Интернете о том, как UIViewRepresentableсвязывает макеты от UIKit к SwiftUI. Есть ли способ добиться этого автоматического роста и сжатия в соответствии с поведением? Или мне придется заняться хакерством сsizeThatFits и устанавливать рамку вручную при изменении текста?

1 ответ

Попробуйте использовать.fixedSize(horizontal: false, vertical: true)

или установка приоритета содержимого UITextView на требуемый по вертикальной оси.

Протестировал это на iOS 16, и, похоже, это работает. Не уверен насчет предыдущих ОС

Другие вопросы по тегам