Использование `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, и, похоже, это работает. Не уверен насчет предыдущих ОС