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

SwiftUI中UITextView无法自适应尺寸的问题求助

解决SwiftUI中嵌套ScrollView的HTML文本组件高度异常问题

问题背景

需在iOS14兼容环境下,用SwiftUI显示HTML内容,要求自定义字体与文本颜色,因此采用UITextView封装为UIViewRepresentable组件。但组件嵌套在ScrollView的VStack中时,出现以下问题:

  • 初始实现中文本始终单行显示,无法垂直扩展
  • 添加setContentCompressionResistancePriority(.defaultLow, for: .horizontal)后高度显示正常,但竖屏时最后一行文本不可见,横屏无异常

解决方案

核心思路是让UITextView的尺寸计算与SwiftUI布局系统深度协同,避免手动设置frame干扰布局逻辑,同时针对ScrollView的无限空间特性调整视图优先级。

修改后的完整代码

struct HTMLTextView: UIViewRepresentable {
    private var htmlString: String
    private var font: UIFont = .systemFont(ofSize: 14)
    private var textColor: UIColor = .darkText

    init(htmlString: String) {
        self.htmlString = htmlString
    }

    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.isScrollEnabled = false
        textView.isEditable = false
        textView.backgroundColor = .clear
        // 降低水平压缩优先级,允许SwiftUI约束宽度
        textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        // 提高垂直拥抱优先级,优先使用文本自然高度
        textView.setContentHuggingPriority(.defaultHigh, for: .vertical)
        return textView
    }

    func updateUIView(_ textView: UITextView, context: Context) {
        textView.attributedText = buildAttributedString(fromHTML: htmlString)
        // 强制触发固有尺寸更新,确保内容变化时重新计算高度
        textView.invalidateIntrinsicContentSize()
    }

    func font(_ font: UIFont) -> Self {
        var copy = self
        copy.font = font
        return copy
    }

    func textColor(_ textColor: UIColor) -> Self {
        var copy = self
        copy.textColor = textColor
        return copy
    }

    // MARK: - Private

    private func buildAttributedString(fromHTML htmlString: String) -> NSAttributedString {
        let htmlData = Data(htmlString.utf8)
        let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ]
        
        guard let attributedString = try? NSMutableAttributedString(data: htmlData, options: options, documentAttributes: nil) else {
            return NSAttributedString(string: "")
        }
        
        let range = NSRange(location: 0, length: attributedString.length)
        // 先移除HTML自带的字体属性,避免与自定义字体冲突
        attributedString.removeAttribute(.font, range: range)
        attributedString.addAttributes([
            .font: font,
            .foregroundColor: textColor
        ], range: range)
        
        return attributedString
    }
}

调用方式

在ScrollView中使用时,通过SwiftUI的frame约束宽度,让组件自适应高度:

ScrollView {
    VStack(spacing: 16) {
        HTMLTextView(htmlString: "<p>这是一段多行HTML文本,测试换行与高度自适应</p><p>第二行内容,确保最后一行在竖屏时正常显示</p>")
            .font(.systemFont(ofSize: 15))
            .textColor(.darkText)
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding()
        
        // 其他视图...
    }
}

关键修改说明

  1. 移除手动frame设置:依赖UITextView的intrinsicContentSize让SwiftUI自动计算高度,避免布局冲突
  2. 调整视图优先级:通过setContentHuggingPriority确保垂直方向优先使用文本自然高度,setContentCompressionResistancePriority允许SwiftUI约束水平宽度
  3. 优化HTML转富文本逻辑:先移除HTML自带的字体属性,避免自定义字体被覆盖
  4. 强制更新固有尺寸:在内容变化时调用invalidateIntrinsicContentSize(),确保高度实时更新

内容的提问来源于stack exchange,提问作者Ievgen Leichenko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 14:05:24