SwiftUI自定义UITextView输入框行尾输入时文本消失问题求助
SwiftUI自定义消息输入框行尾输入文本消失问题修复
我在SwiftUI中实现自定义消息输入框时遇到问题:输入初期一切正常,按回车键换行也没问题,但输入到行尾时文本会突然消失。
问题现象
输入到行尾触发自动换行的瞬间,文本内容消失,输入框高度出现异常波动。
原问题代码
struct MessageInputField: UIViewRepresentable { var inputMessage: Binding<String> var currentFieldHeight: Binding<CGFloat> func makeUIView(context: Context) -> UITextView { let textView = UITextView(frame: .zero) textView.isScrollEnabled = false textView.font = UIFont.systemFont(ofSize: 15) textView.delegate = context.coordinator textView.textContainerInset = .zero textView.textContainer.lineFragmentPadding = 0 textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) textView.setContentHuggingPriority(.defaultHigh, for: .vertical) return textView } func updateUIView(_ uiView: UITextView, context: Context) { uiView.text = inputMessage.wrappedValue updateTextViewHeight(uiView) } private func updateTextViewHeight(_ textView: UITextView) { let size = textView.sizeThatFits(CGSize(width: textView.frame.width, height: .greatestFiniteMagnitude)) // 计算5行文本的最大高度 let maxHeight = textView.font!.lineHeight * 5 // 内容超过最大高度时限制高度并开启滚动 if size.height >= maxHeight { currentFieldHeight.wrappedValue = maxHeight textView.isScrollEnabled = true if textView.frame.size.height != maxHeight { textView.frame.size.height = maxHeight } } else { currentFieldHeight.wrappedValue = size.height textView.isScrollEnabled = false if textView.frame.size.height != size.height { textView.frame.size.height = size.height } } } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITextViewDelegate { var parent: MessageInputField init(_ parent: MessageInputField) { self.parent = parent } func textViewDidChange(_ textView: UITextView) { parent.inputMessage.wrappedValue = textView.text parent.updateTextViewHeight(textView) } } }
问题原因
updateUIView中每次直接覆盖uiView.text的值,在用户输入到行尾触发自动换行时,UITextView正在内部调整文本布局,此时强制赋值会打乱布局逻辑,导致文本消失。- 频繁直接修改
textView.frame.size.height,干扰了UIKit控件的内部布局流程。
修复后的代码
struct MessageInputField: UIViewRepresentable { var inputMessage: Binding<String> var currentFieldHeight: Binding<CGFloat> func makeUIView(context: Context) -> UITextView { let textView = UITextView(frame: .zero) textView.isScrollEnabled = false textView.font = UIFont.systemFont(ofSize: 15) textView.delegate = context.coordinator textView.textContainerInset = .zero textView.textContainer.lineFragmentPadding = 0 textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) textView.setContentHuggingPriority(.defaultHigh, for: .vertical) // 初始化时一次性设置文本 textView.text = inputMessage.wrappedValue return textView } func updateUIView(_ uiView: UITextView, context: Context) { // 仅当文本不一致时更新,避免干扰输入过程 if uiView.text != inputMessage.wrappedValue { uiView.text = inputMessage.wrappedValue } updateTextViewHeight(uiView) } private func updateTextViewHeight(_ textView: UITextView) { guard let font = textView.font else { return } let maxHeight = font.lineHeight * 5 let size = textView.sizeThatFits(CGSize(width: textView.frame.width, height: .greatestFiniteMagnitude)) let targetHeight = min(size.height, maxHeight) currentFieldHeight.wrappedValue = targetHeight // 仅高度变化时调整frame,减少不必要的布局触发 if textView.frame.size.height != targetHeight { textView.frame.size.height = targetHeight } textView.isScrollEnabled = size.height >= maxHeight } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITextViewDelegate { var parent: MessageInputField init(_ parent: MessageInputField) { self.parent = parent } func textViewDidChange(_ textView: UITextView) { parent.inputMessage.wrappedValue = textView.text parent.updateTextViewHeight(textView) } } }
关键修复点
- 避免不必要的文本覆盖:在
updateUIView中增加文本一致性判断,仅当UITextView当前文本与绑定值不同时才更新,防止干扰自动换行布局。 - 优化初始化逻辑:在
makeUIView中完成初始文本设置,减少updateUIView的重复操作。 - 简化高度计算:合并高度判断逻辑,减少冗余条件分支,降低布局冲突概率。
内容的提问来源于stack exchange,提问作者siflJebatkundy
相关产品推荐
相关产品推荐

