使用排除路径时,小屏UITextView无法完整显示文本的问题
原因分析
排除路径的frame获取时机错误
你在viewDidLoad阶段直接获取mainActionButton.frame,但此时视图还未完成最终布局,按钮的frame只是临时计算值,并非实际渲染后的真实尺寸。小屏幕设备布局空间紧凑,这个错误的frame会打乱文本容器的换行逻辑,再加上padding缩小导致可用空间进一步压缩,最后一行文本就会被挤出可视区域。文本容器内边距叠加引发空间计算偏差
UITextView默认带有textContainerInset(默认值约8pt),当你设置的外部约束padding小于4时,默认内边距会和你的自定义padding叠加,进一步压缩文本的实际可用高度,和titleView的高度约束产生冲突,最终导致最后一行文本被截断。小屏幕布局精度放大误差
小屏幕的空间余量极少,padding减小后,文本布局的容错空间几乎为零,布局计算时的微小浮点偏差(比如坐标四舍五入)都会触发文本换行计算错误,直接导致最后一行无法显示。
修复方案
1. 延迟设置排除路径到布局完成后
将设置exclusionPaths的代码移到viewDidLayoutSubviews方法中,这个方法会在所有视图完成布局计算后调用,此时按钮的frame是最终的真实尺寸:
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 将按钮的frame转换为textView坐标系下的rect(exclusionPaths基于文本容器的坐标) let buttonRectInTextView = textView.convert(mainActionButton.frame, from: titleView) textView.textContainer.exclusionPaths = [UIBezierPath(rect: buttonRectInTextView)] }
2. 统一文本容器内边距,避免叠加冲突
显式设置UITextView的textContainerInset,和外部约束的padding保持一致,消除默认内边距的干扰:
var textView: UITextView = { let textView = UITextView() textView.textColor = .orange textView.isScrollEnabled = false textView.isEditable = false // 设置和外部约束一致的内边距,这里以padding=3为例 textView.textContainerInset = UIEdgeInsets(top: 3, left: 3, bottom: 3, right: 3) return textView }()
同时可以调整textView的外部约束,去掉重复的padding(或根据视觉需求保持一致):
let padding: CGFloat = 3 NSLayoutConstraint.activate([ textView.leadingAnchor.constraint(equalTo: titleView.leadingAnchor), textView.trailingAnchor.constraint(equalTo: titleView.trailingAnchor), textView.topAnchor.constraint(equalTo: titleView.topAnchor), textView.bottomAnchor.constraint(equalTo: titleView.bottomAnchor) ])
3. 额外优化:确保titleView高度自适应
给titleView设置优先级,避免StackView压缩它的高度,保证文本能完整显示:
titleView.setContentHuggingPriority(.defaultHigh, for: .vertical) titleView.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
补充说明
- 无排除路径时,文本布局无需计算绕开元素,逻辑更稳定,即使空间紧凑也能正常换行;
- 大屏幕设备空间充足,即使存在frame误差或内边距叠加,最后一行文本仍有足够空间显示;
- padding>3时,文本容器的可用空间足够大,能容纳布局计算的微小误差,不会触发截断问题。
内容的提问来源于stack exchange,提问作者Moses Harding

