我创建了这个 SwiftUI GrowingTextViewRepresentable,它代表一个 UITextView。即使焦点转移到另一个 TextField/TextView 之后,我也想保留 selectedTextRange 。为了实现这一点,我尝试使用绑定来保留它。我将其保存在 textViewDidEndEditing 中,期望当焦点更改到另一个文本字段时它保持选中状态。然后,当视图加载时,我在 updateUIView 中再次设置它。但是,它没有按预期工作。我做错了什么,或者我怎样才能实现这个目标?
仅供参考: GrowingTextView只是UITextView的子类,它在多行文本变化时增加高度。
GrowingTextView可表示代码:
import UIKit
import SwiftUI
import GrowingTextView
struct GrowingTextViewRepresentable: UIViewRepresentable {
let textView = GrowingTextView()
var placeHolder: String = ""
var customFont: UIFont = .systemFont(ofSize: 12)
var placeHolderColor: UIColor = .grey
var textColor: UIColor = .black
@Binding var text: String
@Binding var height: CGFloat
@Binding var selectedText: String
@Binding var selectedTextRange: UITextRange?
func makeUIView(context: Context) -> GrowingTextView {
textView.delegate = context.coordinator
textView.font = customFont
textView.placeholder = placeHolder
textView.placeholderColor = placeHolderColor
textView.textColor = textColor
textView.backgroundColor = .clear
textView.clipsToBounds = true
return textView
}
func updateUIView(_ uiView: GrowingTextView, context: Context) {
uiView.text = text
if selectedTextRange != nil && uiView.selectedTextRange != selectedTextRange {
uiView.selectedTextRange = selectedTextRange
}
}
func makeCoordinator() -> Coordinator {
Coordinator(text: $text, placeHolder: placeHolder, height: $height, selectedText: $selectedText, selectedTextRange: $selectedTextRange)
}
class Coordinator: NSObject, GrowingTextViewDelegate {
@Binding var text: String
var placeHolder: String
@Binding var height: CGFloat
@Binding var selectedText: String
@Binding var selectedTextRange: UITextRange?
private var tempSelectedBodyTextRange: UITextRange? = nil
init(text: Binding<String>, placeHolder: String, height: Binding<CGFloat>, selectedText: Binding<String>, selectedTextRange: Binding<UITextRange?>) {
self._text = text
self.placeHolder = placeHolder
self._height = height
self._selectedText = selectedText
self._selectedTextRange = selectedTextRange
}
func textViewDidChange(_ textView: UITextView) {
// UIKit -> SwiftUI
_text.wrappedValue = textView.text
}
func textViewDidChangeSelection(_ textView: UITextView) {
// Fires off every time the user changes the selection.
if let selectedText = textView.selectedText, let range = textView.selectedTextRange {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self._selectedText.wrappedValue = selectedText
self._selectedTextRange.wrappedValue = range
self.tempSelectedBodyTextRange = range
}
//print(selectedText)
}
}
func textViewDidChangeHeight(_ textView: GrowingTextView, height: CGFloat) {
DispatchQueue.main.async {
self.height = height
}
}
func textViewDidEndEditing(_ textView: UITextView) {
// Save the selected range when the text view ends editing
if let range = tempSelectedBodyTextRange {
DispatchQueue.main.async {
self._selectedTextRange.wrappedValue = range
textView.selectedTextRange = range
}
}
}
}
}
extension UITextView {
var selectedText: String? {
guard let selectedRange = selectedTextRange else { return nil }
return text(in: selectedRange)
}
}
我如何使用它:
import UIKit
struct MyView: View {
@State private var body:String = ""
@State private var selectedBodyText: String = ""
@State private var selectedBodyTextRange: UITextRange? = nil
@State private var heightOfBody: CGFloat = 0
var body: some View {
GrowingTextViewRepresentable(placeHolder: "write about...",
text: body, height: $heightOfBody, selectedText: selectedBodyText, selectedTextRange: selectedBodyTextRange)
.frame(height: heightOfBody)
.frame(minHeight: 20)
.onChange(of: viewModel.selectedBodyText, perform: { newValue in
print("selectedBodyText: \(newValue)")
})
}
}
我尝试保留 tempSelectedBodyTextRange 中的值,然后在 textViewDidEndEditing 中重置它,但它没有按预期工作。
makeUIView
需要初始化并返回您的GrowingTextView
,否则它将丢失,例如
func makeUIView(context: Context) -> GrowingTextView {
let view = GrowingTextView()