如何在 UITextView UIViewRepresentable 中维护 selectedTextRange,即使焦点转移到另一个 TextField 后它也应该保持选中状态

问题描述 投票:0回答:1

我创建了这个 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 中重置它,但它没有按预期工作。

ios swift swiftui uikit uitextview
1个回答
0
投票

makeUIView
需要初始化并返回您的
GrowingTextView
,否则它将丢失,例如

func makeUIView(context: Context) -> GrowingTextView {
    let view = GrowingTextView()
© www.soinside.com 2019 - 2024. All rights reserved.