在 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 文本直接从 textViewDidBeginEditing
和 textViewDidEndEditing
更新而来。这种逻辑在创建完整的聊天系统时产生了很多问题。经过大量的尝试和错误来理解工作和调试,我找到了一个完美的直接解决方案。
解法:
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
}
}
}
}
我对 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 文本直接从 textViewDidBeginEditing
和 textViewDidEndEditing
更新而来。这种逻辑在创建完整的聊天系统时产生了很多问题。经过大量的尝试和错误来理解工作和调试,我找到了一个完美的直接解决方案。
解法:
Use a
isEditing
boolean to track changes and update UI only insideupdateUIView
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
}
}
}
}