Использование `UIViewRepresentable` для связывания` UITextView`, который растет и уменьшается, чтобы соответствовать его тексту, SwiftUI - PullRequest
0 голосов
/ 02 апреля 2020

Если я хочу не прокручиваемый 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)
    }
}

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

Proper layout

Я хочу связать это с 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) успешно заставляет текстовое представление переносить текст на несколько строк, но высота заполняет весь экран:

Wrapped but wrong height

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

No longer wrapping

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

...