7.0 KiB
7.0 KiB
统一文本尺寸计算实现
概述
本次修改统一了应用中的文本尺寸计算方法,解决了阿拉伯文本显示问题,提高了 RTL(从右到左)文本的渲染精度。
主要修改
1. XPNetImageYYLabel.m
修改前:
- 使用条件分支:RTL 文本使用
boundingRectWithSize
,非 RTL 文本使用YYTextLayout
- 导致 RTL 文本计算不准确
修改后:
- 统一使用
YYTextLayout
进行所有文本尺寸计算 - 新增
createTextContainerForText:
方法,根据文本方向配置容器 - RTL 文本使用
YYTextTruncationTypeStart
,LTR 文本使用YYTextTruncationTypeEnd
2. XPMessageInfoModel.m
修改前:
- 检测到阿拉伯字符时额外增加 20 点高度
- 可能导致高度计算不准确
修改后:
- 移除额外的阿拉伯文本高度调整
- 优化
YYTextContainer
配置,根据文本方向设置截断类型 - 依赖
YYTextLayout
的精确计算
3. 新增测试文件
XPTextLayoutTest.h/m
:提供测试方法验证修改效果- 包含纯阿拉伯文本、混合文本、长文本的测试用例
技术细节
YYTextContainer 配置
- (YYTextContainer *)createTextContainerForText:(NSAttributedString *)text {
YYTextContainer *container = [YYTextContainer new];
container.size = CGSizeMake(kRoomMessageMaxWidth, MAXFLOAT);
// 根据文本方向配置容器
if (isMSRTL()) {
container.truncationType = YYTextTruncationTypeStart;
} else {
container.truncationType = YYTextTruncationTypeEnd;
}
return container;
}
统一计算逻辑
- (void)updateLayoutWithAttributedText:(NSAttributedString *)attributedText {
// 统一使用 YYTextLayout 进行文本尺寸计算
YYTextContainer *container = [self createTextContainerForText:attributedText];
YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:attributedText];
CGSize finalSize = layout.textBoundingSize;
if (self.updateLayoutSize) {
self.updateLayoutSize(finalSize);
}
}
优势
- 精确性:
YYTextLayout
对复杂文本(包括 RTL)有更好的支持 - 一致性:统一的计算方法确保所有文本类型的一致性
- 可维护性:减少条件分支,代码更简洁
- 性能:避免重复计算和额外的高度调整
测试
运行测试验证修改效果:
#import "XPTextLayoutTest.h"
// 在适当的地方调用
[XPTextLayoutTest runAllTests];
注意事项
- 确保
YYText
库版本支持所需功能 - 测试各种文本场景,特别是长文本和混合文本
- 验证在不同设备和系统版本上的表现
- 关注性能影响,特别是在大量文本的场景下
后续优化建议
- 字体优化:为阿拉伯文本自动选择合适的字体(如 "GeezaPro")
- 缓存机制:为频繁计算的文本添加缓存
- 性能监控:添加性能指标监控文本计算耗时
- 更多测试:增加边界情况和性能测试
问题修复记录
2025-08-14 修复文本截断问题
问题描述:
- 阿拉伯语正常显示,但其他语言(中文/英文/土耳其文)可能出现显示不全尾截断情况
根本原因:
XPNetImageYYLabel.m
中的setAttributedText
方法缺少maximumNumberOfRows = 0
设置- 截断类型配置不一致,部分地方硬编码为
YYTextTruncationTypeEnd
修复方案:
- 在
createTextContainerForText
方法中添加container.maximumNumberOfRows = 0
- 在
setAttributedText
方法中统一文本方向配置逻辑 - 确保所有
YYTextContainer
都正确设置maximumNumberOfRows = 0
修复后效果:
- 所有语言的长文本都能完整显示,不会被意外截断
- 保持阿拉伯语的正确显示效果
- 统一了文本容器的配置逻辑
多行文本显示问题修复 (2024-01-XX)
问题描述
在聊天室消息显示中发现多行文本无法完整显示的问题,特别是包含多语言内容(阿拉伯语、中文、英文、土耳其语等)的长文本消息。
发现的问题
1. XPNetImageYYLabel.m 中的问题
-
问题1 (第71行): 使用了错误的
YYTextLayout
创建方法// 错误的方式 YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:maxSize text:attributedTextCopy]; // 正确的方式 YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:attributedTextCopy];
-
问题2 (第62行): 容器尺寸设置不准确
// 错误:使用计算出的size.width container.size = CGSizeMake(size.width, MAXFLOAT); // 正确:使用统一的最大宽度 container.size = CGSizeMake(kRoomMessageMaxWidth, MAXFLOAT);
2. XPMessageInfoModel.m 中的问题
- 问题3 (第36-38行): 多余的
boundingRectWithSize
调用// 错误:存在未使用的计算,浪费性能 CGSize size = [content boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin context:nil].size; // 后续使用 YYTextLayout 计算,size 变量未被使用 // 正确:移除多余调用,统一使用 YYTextLayout // 直接使用 YYTextLayout 进行所有文本尺寸计算
3. XPRoomMessageTableViewCell.m 中的问题
- 问题5 (第178行): 错误的宽度约束设置
// 错误:使用textBoundingSize.width作为宽度约束可能导致多行文本显示不完整 make.width.mas_lessThanOrEqualTo(size.width).priority(UILayoutPriorityDefaultHigh); // 正确:移除此约束,让Label根据preferredMaxLayoutWidth自然确定宽度 // 保留trailing约束来限制最大宽度即可
修复内容
-
修复 YYTextLayout 创建方式: 改用
layoutWithContainer:text:
方法,确保maximumNumberOfRows=0
和truncationType
设置生效 -
统一容器尺寸设置: 在所有地方都使用
kRoomMessageMaxWidth - 24
作为容器宽度,正确处理 contentLabel 边距 -
移除冗余计算: 移除
XPMessageInfoModel.m
中未使用的boundingRectWithSize
调用,提高性能并避免混淆 -
优化外部约束逻辑: 移除基于
textBoundingSize.width
的宽度约束,避免约束冲突和多行文本显示不完整 -
修复边距计算: 文本容器宽度减去 contentLabel 的左右边距(UIEdgeInsetsMake(4, 12, 4, 12) 中的各12px)
预期效果
- ✅ 多行文本能够完整显示
- ✅ 支持多语言文本布局(阿拉伯语RTL、中文、英文、土耳其语等)
- ✅ 统一的文本布局策略
- ✅ 提高文本尺寸计算的准确性和性能
- ✅ 避免约束冲突导致的布局异常
- ✅ 修复 preferredMaxLayoutWidth 与文本容器宽度不一致导致的截断问题
风险评估
- 低风险:核心逻辑修改,但使用成熟的
YYTextLayout
- 兼容性:保持现有 API 不变
- 回滚:如有问题可快速回滚到原有逻辑