SwiftUI自定义富文本视图无法自动换行的问题求助
解决UITextView封装后文本不自动换行的问题
问题根源
- 硬设宽度+padding叠加冲突:调用时直接给
RichTextView设置width: UIScreen.screenWidth,再叠加leading/trailingpadding,导致UITextView的实际可布局宽度超出预期,无法触发换行逻辑。 - 高度计算传入无效宽度:
calculateHeight方法传width: .infinity,sizeThatFits无法计算正确的换行宽度,返回的高度不准确,进而导致视图高度被错误设置。 - UIKit视图配置干扰布局:
textContainer.widthTracksTextView = true会让文本容器宽度无限跟随TextView,加上updateUIView里调用sizeToFit,打乱了Auto Layout的正常布局流程。 - 误用SwiftUI修饰符:
multilineTextAlignment、lineLimit、lineSpacing是SwiftUI原生Text的专属修饰符,对UIKit封装的UITextView完全无效。
分步解决方案
1. 修正RichTextView的UIKit配置
移除干扰布局的属性,让UITextView完全响应SwiftUI的布局约束:
struct RichTextView: UIViewRepresentable { var text: String var lineSpacing: CGFloat = 5 var paragraphSpacing: CGFloat = 10 var textAlignment: NSTextAlignment = .left func makeUIView(context: Context) -> UITextView { let textView = UITextView() textView.backgroundColor = .clear textView.isEditable = false textView.isScrollEnabled = false textView.font = UIFont.systemFont(ofSize: 18) textView.dataDetectorTypes = .all textView.linkTextAttributes = [NSAttributedString.Key.foregroundColor: Colors.colorPrimary()] textView.translatesAutoresizingMaskIntoConstraints = false textView.textContainer.lineBreakMode = .byWordWrapping textView.textAlignment = textAlignment return textView } func updateUIView(_ uiView: UITextView, context: Context) { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = lineSpacing paragraphStyle.paragraphSpacing = paragraphSpacing paragraphStyle.lineBreakMode = .byWordWrapping paragraphStyle.lineBreakStrategy = .standard let attributedString = NSAttributedString( string: text, attributes: [ .font: UIFont.systemFont(ofSize: 18), .paragraphStyle: paragraphStyle, .foregroundColor: UIColor.label ] ) uiView.attributedText = attributedString uiView.textAlignment = textAlignment uiView.isScrollEnabled = false } }
2. 修正调用代码的布局逻辑
去掉无效修饰符,用正确方式约束视图宽度:
private let horizontalPadding: CGFloat = 16 // 和padding值保持一致 RichTextView(text: description) .padding([.leading, .trailing], horizontalPadding) .padding(.bottom, 16) .frame(maxWidth: .infinity) // 让视图占满父布局可用宽度,触发换行 .onAppear { // 传入实际可用宽度:屏幕宽度 - 左右padding总和 self.viewHeight = calculateHeight(for: description, width: UIScreen.screenWidth - 2 * horizontalPadding) } .onChange(of: description) { _ in self.viewHeight = calculateHeight(for: description, width: UIScreen.screenWidth - 2 * horizontalPadding) }
3. 修正高度计算方法
传入实际可用宽度,保持和RichTextView一致的配置:
private func calculateHeight(for text: String, width: CGFloat) -> CGFloat { let textView = UITextView() textView.font = UIFont.systemFont(ofSize: 18) textView.dataDetectorTypes = .all // 和RichTextView保持一致,避免链接识别影响高度计算 textView.isScrollEnabled = false let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = 5 paragraphStyle.paragraphSpacing = 10 paragraphStyle.lineBreakMode = .byWordWrapping paragraphStyle.lineBreakStrategy = .standard let attributedString = NSAttributedString( string: text, attributes: [ .font: UIFont.systemFont(ofSize: 18), .paragraphStyle: paragraphStyle ] ) textView.attributedText = attributedString textView.textContainer.lineBreakMode = .byWordWrapping textView.translatesAutoresizingMaskIntoConstraints = false textView.textContainer.maximumNumberOfLines = 0 // 使用传入的具体可用宽度计算高度 let size = textView.sizeThatFits(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)) return size.height }
关键说明
- 移除
textContainer.widthTracksTextView = true:该属性会让文本容器宽度无限扩展,导致文本永远不换行,必须去掉才能让UITextView根据布局宽度自动换行。 - 放弃SwiftUI原生文本修饰符:UIKit视图的配置必须在UITextView内部完成,SwiftUI的
lineLimit、lineSpacing等修饰符对它无效。 - 高度计算必须用实际可用宽度:如果传
.infinity,sizeThatFits会认为文本可以无限宽,自然不会计算换行后的高度。
内容的提问来源于stack exchange,提问作者Lilia
相关产品推荐
相关产品推荐

