You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
        }
    }
}

问题原因

  1. updateUIView中每次直接覆盖uiView.text的值,在用户输入到行尾触发自动换行时,UITextView正在内部调整文本布局,此时强制赋值会打乱布局逻辑,导致文本消失。
  2. 频繁直接修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 02:46:20