在 SwiftUI 聊天应用程序中单击发送按钮时如何清除自定义 UITextView 中的文本?

How to clear text in custom UITextView when a send button is clicked in a SwiftUI Chat app?

我对 UIKit 的了解有限。所以我遵循了创建聊天室的教程。按照代码。唯一的问题是我无法在发送消息后重置聊天文本。短信正在重置。但它没有反映在名为 MessageField 的自定义 UITextView 中。我正在使用 Xcode 12.3.

如何在执行发送按钮操作时清除 UITextView 的文本?

MessageField.swift


import SwiftUI

struct MessageField: UIViewRepresentable {
    
    @Binding var text: String
    @Binding var height: CGFloat
    var placeholder = "Message"
    
    func makeCoordinator() -> Coordiator {
        return MessageField.Coordiator(parent: self, placeholder: placeholder)
    }
    
    func makeUIView(context: Context) -> UITextView {
        let view = UITextView()
        view.isEditable = true
        view.isScrollEnabled = true
        view.text = placeholder
        view.font = .systemFont(ofSize: 18)
        view.textColor = .lightGray
        view.backgroundColor = .clear
        view.delegate = context.coordinator
        return view
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        DispatchQueue.main.async {
            self.height = uiView.contentSize.height
        }
    }
    
    class Coordiator: NSObject, UITextViewDelegate {
        var parent: MessageField
        var placeholder: String
        
        init(parent: MessageField, placeholder: String) {
            self.parent = parent
            self.placeholder = placeholder
        }
        
        func textViewDidBeginEditing(_ textView: UITextView) {
            if self.parent.text == "" {
                textView.text = ""
                textView.textColor = .label
            }
        }
        
        func textViewDidEndEditing(_ textView: UITextView) {
            if self.parent.text == "" {
                textView.text = placeholder
                textView.textColor = .lightGray
            }
        }
        
        func textViewDidChange(_ textView: UITextView) {
            DispatchQueue.main.async {
                self.parent.height = textView.contentSize.height
                self.parent.text = textView.text
            }
        }
    }
}

ChatField.swift


import SwiftUI

struct ChatField: View {
    
    @Binding var text: String
    @State var height: CGFloat = 0
    var sendText: () -> ()
    
    var body: some View {
        HStack(alignment: .bottom) {
            MessageField(text: self.$text, height: self.$height)
                .frame(height: self.height < 150 ? self.height : 150)
                .padding(.horizontal)
                .background(Color(.systemBackground))
                .cornerRadius(10)
            Button(action: {
                self.sendText()
                self.text = ""
                print("Message sent")
            }, label: {
                Image(systemName: "paperplane.fill")
                    .resizable()
                    .scaledToFit()
                    .padding(12)
                    .frame(height: 38, alignment: .center)
                    .foregroundColor(.foregroundLabel)
            })
            .background(Color(.systemBackground))
            .clipShape(RoundedRectangle(cornerRadius: 10))
        }
        .padding(.horizontal)
        .padding(.vertical, 5)
    }
}

ChatRoom.swift


import SwiftUI

struct ChatRoom: View {
    
    @State var text = ""
    @State var height: CGFloat = 0
    
    var body: some View {
        VStack(spacing: 0) {
            HStack {
                Text("Chat Room")
                    .font(.title)
                    .fontWeight(.bold)
                
                Spacer()
            }
            .padding()
            .background(Color(.systemBackground))
            
            ScrollView(.vertical, showsIndicators: false) {
                VStack(alignment: .leading) {
                    ForEach(0 ..< 60) { item in
                        Text("Hello \(item+1)")
                    }
                }
                .padding(.horizontal)
                .frame(maxWidth: .infinity, alignment: .leading)
            }
            
            ChatField(text: $text, height: height) {
                print("Sending following text:")
                print(self.text)
            }
            .background(Color(.systemBackground).edgesIgnoringSafeArea(.bottom))
        }
        .background(Color(.systemGroupedBackground))
        .onTapGesture {
            UIApplication.shared.windows.first?.rootViewController?.view.endEditing(true)
        }
    }
}

修改绑定时调用UIViewRepresentable.updateUIView,因此您需要在此函数中应用外部修改的数据(在您的情况下为text),例如

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

    DispatchQueue.main.async {
        self.height = uiView.contentSize.height
    }
}

教程中使用的逻辑很好 UI。但实际上不能用。 UI 文本直接从 textViewDidBeginEditingtextViewDidEndEditing 更新而来。这种逻辑在创建完整的聊天系统时产生了很多问题。经过大量的尝试和错误来理解工作和调试,我找到了一个完美的直接解决方案。

解法:

Use a isEditing boolean to track changes and update UI only inside updateUIView method.

这解决了所有问题。以下是固定代码。

MessageField.swift

import SwiftUI

struct MessageField: UIViewRepresentable {
    
    @Binding var text: String
    @Binding var height: CGFloat
    var placeholder: String
    @State var isEditing: Bool = false
    
    func makeCoordinator() -> Coordiator {
        return MessageField.Coordiator(parent: self)
    }
    
    func makeUIView(context: Context) -> UITextView {
        let view = UITextView()
        view.isEditable = true
        view.isScrollEnabled = true
        view.text = placeholder
        view.font = .systemFont(ofSize: 18)
        view.textColor = .lightGray
        view.backgroundColor = .clear
        view.delegate = context.coordinator
        return view
    }
    
    func updateUIView(_ textView: UITextView, context: Context) {
        if self.text == "" {
            textView.text = self.isEditing ? "" : self.placeholder
            textView.textColor = self.isEditing ? .label : .lightGray
        }
        DispatchQueue.main.async {
            self.height = textView.contentSize.height
        }
    }
    
    class Coordiator: NSObject, UITextViewDelegate {
        var parent: MessageField
        
        init(parent: MessageField) {
            self.parent = parent
        }
        
        func textViewDidBeginEditing(_ textView: UITextView) {
            DispatchQueue.main.async {
                self.parent.isEditing = true
            }
        }
        
        func textViewDidEndEditing(_ textView: UITextView) {
            DispatchQueue.main.async {
                self.parent.isEditing = false
            }
        }
        
        func textViewDidChange(_ textView: UITextView) {
            DispatchQueue.main.async {
                self.parent.height = textView.contentSize.height
                self.parent.text = textView.text
            }
        }
        
    }
}