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

iOS14下SwiftUI实现限高5行的类消息式可滚动TextView问题

实现自适应高度且带最大行数限制的TextView问题

我想要实现一个类消息输入框的UITextView,要求:

  • 初始高度为1行
  • 最大高度限制为5行
  • 内容达到5行高度时,停止高度扩展并启用滚动功能

目前编写的代码可以随换行自动增高,但达到5行高度后不会停止增高,也不会启用滚动,运行环境为iOS14:

struct AutoAdjustingTextView: UIViewRepresentable {
@Binding var text: String

func makeUIView(context: Context) -> UITextView {
    let textView = UITextView()
    textView.isScrollEnabled = false
    textView.font = UIFont.systemFont(ofSize: 17)
    textView.delegate = context.coordinator
    textView.textContainerInset = .zero
    textView.textContainer.lineFragmentPadding = 0
    textView.setContentHuggingPriority(.defaultHigh, for: .vertical)
    return textView
}

func updateUIView(_ uiView: UITextView, context: Context) {
    uiView.text = text
    updateTextViewHeight(uiView)
}

private func updateTextViewHeight(_ textView: UITextView) {
    let size = textView.sizeThatFits(CGSize(width: textView.frame.width, height: .greatestFiniteMagnitude))
    if textView.frame.size.height != size.height {
        textView.frame.size.height = size.height
    }
}

func makeCoordinator() -> Coordinator {
    Coordinator(self)
}

class Coordinator: NSObject, UITextViewDelegate {
    var parent: AutoAdjustingTextView

    init(_ parent: AutoAdjustingTextView) {
        self.parent = parent
    }

    func textViewDidChange(_ textView: UITextView) {
        parent.text = textView.text
        parent.updateTextViewHeight(textView)
    }
}
}
解决方案

问题核心是缺少最大行数对应的高度计算,以及未在达到阈值时切换滚动状态。以下是修改后的完整代码:

struct AutoAdjustingTextView: UIViewRepresentable {
    @Binding var text: String
    // 定义最大行数常量
    private let maxLines: Int = 5
    private let font = UIFont.systemFont(ofSize: 17)

    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.isScrollEnabled = false
        textView.font = font
        textView.delegate = context.coordinator
        textView.textContainerInset = .zero
        textView.textContainer.lineFragmentPadding = 0
        textView.setContentHuggingPriority(.defaultHigh, for: .vertical)
        // 直接设置初始高度为单行文本高度
        textView.frame.size.height = calculateSingleLineHeight()
        return textView
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        // 避免外部更新文本时重复计算高度
        if uiView.text != text {
            uiView.text = text
            updateTextViewHeight(uiView)
        }
    }

    private func updateTextViewHeight(_ textView: UITextView) {
        let singleLineHeight = calculateSingleLineHeight()
        let maxHeight = singleLineHeight * CGFloat(maxLines)
        
        // 计算内容所需的理想高度
        let fittingSize = textView.sizeThatFits(CGSize(width: textView.frame.width, height: .greatestFiniteMagnitude))
        // 取理想高度和最大高度的较小值作为目标高度
        let targetHeight = min(fittingSize.height, maxHeight)
        
        // 仅在高度变化时更新,避免不必要的布局刷新
        if textView.frame.size.height != targetHeight {
            textView.frame.size.height = targetHeight
        }
        
        // 当内容实际高度超过最大高度时,启用滚动
        textView.isScrollEnabled = textView.contentSize.height > maxHeight
    }

    // 计算单行文本的高度(通过UILabel间接获取,确保和TextView行高一致)
    private func calculateSingleLineHeight() -> CGFloat {
        let testLabel = UILabel()
        testLabel.font = font
        testLabel.text = "测试文本"
        testLabel.sizeToFit()
        return testLabel.frame.height
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UITextViewDelegate {
        var parent: AutoAdjustingTextView

        init(_ parent: AutoAdjustingTextView) {
            self.parent = parent
        }

        func textViewDidChange(_ textView: UITextView) {
            parent.text = textView.text
            parent.updateTextViewHeight(textView)
        }
    }
}

修改要点说明:

  • 新增高度阈值计算:通过单行高度乘以最大行数,得到允许的最大高度值
  • 限制高度扩展:用min函数确保TextView高度不会超过最大行数对应的高度
  • 动态切换滚动状态:对比内容实际高度和最大高度,自动开启/关闭滚动功能
  • 设置初始高度:在初始化时直接将TextView高度设为单行高度,符合需求
  • 优化更新逻辑:仅在文本实际变化时才更新高度,减少无效布局操作

内容的提问来源于stack exchange,提问作者Haumikj Haumer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 00:01:34