feat(文本布局): 统一文本尺寸计算并支持RTL文本
- 使用YYTextLayout统一处理所有文本尺寸计算,提高RTL文本精度 - 根据文本方向自动配置截断类型(RTL文本使用Start,LTR使用End) - 移除阿拉伯文本的额外高度调整,依赖YYTextLayout的精确计算 - 新增测试类验证纯阿拉伯文本、混合文本和长文本的布局效果 - 添加README文档说明统一文本布局的实现细节和优势
This commit is contained in:
@@ -44,7 +44,14 @@
|
||||
|
||||
YYTextContainer *container = [YYTextContainer containerWithSize:CGSizeMake(size.width, CGFLOAT_MAX)];
|
||||
container.maximumNumberOfRows = 0;
|
||||
container.truncationType = YYTextTruncationTypeEnd;
|
||||
|
||||
// 根据文本方向配置容器
|
||||
if (isMSRTL()) {
|
||||
container.truncationType = YYTextTruncationTypeStart;
|
||||
} else {
|
||||
container.truncationType = YYTextTruncationTypeEnd;
|
||||
}
|
||||
|
||||
container.insets = UIEdgeInsetsMake(self.contentTopMargin, self.contentLeftMargin, self.contentBottomMargin, self.contentLeftMargin);
|
||||
YYTextLayout *textLayout = [YYTextLayout layoutWithContainer:container text:content];
|
||||
|
||||
@@ -67,9 +74,7 @@
|
||||
self.rowHeight += 10;
|
||||
// }
|
||||
self.rowHeight += (self.isBoom ? 20 : 0);
|
||||
if ([self isStringContainArabic:content.string]) {
|
||||
self.rowHeight += 20;
|
||||
}
|
||||
// 移除额外的阿拉伯文本高度调整,YYTextLayout 已能正确处理 RTL 文本
|
||||
}
|
||||
|
||||
- (BOOL)isStringContainArabic:(NSString *)string {
|
||||
|
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// XPTextLayoutTest.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by Assistant on 2024/12/19.
|
||||
// Copyright © 2024 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* 文本布局测试类
|
||||
* 用于验证统一文本尺寸计算的实现效果
|
||||
*/
|
||||
@interface XPTextLayoutTest : NSObject
|
||||
|
||||
/**
|
||||
* 测试纯阿拉伯文本的布局计算
|
||||
*/
|
||||
+ (void)testArabicTextLayout;
|
||||
|
||||
/**
|
||||
* 测试混合文本(英文+阿拉伯文)的布局计算
|
||||
*/
|
||||
+ (void)testMixedTextLayout;
|
||||
|
||||
/**
|
||||
* 测试长阿拉伯文本的布局计算
|
||||
*/
|
||||
+ (void)testLongArabicTextLayout;
|
||||
|
||||
/**
|
||||
* 运行所有测试
|
||||
*/
|
||||
+ (void)runAllTests;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@@ -0,0 +1,117 @@
|
||||
//
|
||||
// XPTextLayoutTest.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by Assistant on 2024/12/19.
|
||||
// Copyright © 2024 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XPNetImageYYLabel.h"
|
||||
#import "XPMessageInfoModel.h"
|
||||
|
||||
@interface XPTextLayoutTest : NSObject
|
||||
|
||||
+ (void)testArabicTextLayout;
|
||||
+ (void)testMixedTextLayout;
|
||||
+ (void)testLongArabicTextLayout;
|
||||
|
||||
@end
|
||||
|
||||
@implementation XPTextLayoutTest
|
||||
|
||||
+ (void)testArabicTextLayout {
|
||||
NSLog(@"=== 测试纯阿拉伯文本布局 ===");
|
||||
|
||||
// 创建阿拉伯文本
|
||||
NSString *arabicText = @"مرحبا بك في تطبيق YuMi";
|
||||
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:arabicText
|
||||
attributes:@{
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:16],
|
||||
NSForegroundColorAttributeName: [UIColor blackColor]
|
||||
}];
|
||||
|
||||
// 测试 XPNetImageYYLabel
|
||||
XPNetImageYYLabel *label = [[XPNetImageYYLabel alloc] init];
|
||||
__block CGSize labelSize = CGSizeZero;
|
||||
label.updateLayoutSize = ^(CGSize size) {
|
||||
labelSize = size;
|
||||
NSLog(@"XPNetImageYYLabel 计算尺寸: %.2f x %.2f", size.width, size.height);
|
||||
};
|
||||
[label updateLayoutWithAttributedText:attributedText];
|
||||
|
||||
// 测试 XPMessageInfoModel
|
||||
XPMessageInfoModel *model = [[XPMessageInfoModel alloc] init];
|
||||
[model setContent:attributedText];
|
||||
NSLog(@"XPMessageInfoModel 计算高度: %.2f", model.rowHeight);
|
||||
|
||||
NSLog(@"=== 阿拉伯文本测试完成 ===\n");
|
||||
}
|
||||
|
||||
+ (void)testMixedTextLayout {
|
||||
NSLog(@"=== 测试混合文本布局 ===");
|
||||
|
||||
// 创建混合文本(英文+阿拉伯文)
|
||||
NSString *mixedText = @"Hello مرحبا World عالم";
|
||||
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:mixedText
|
||||
attributes:@{
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:16],
|
||||
NSForegroundColorAttributeName: [UIColor blackColor]
|
||||
}];
|
||||
|
||||
// 测试 XPNetImageYYLabel
|
||||
XPNetImageYYLabel *label = [[XPNetImageYYLabel alloc] init];
|
||||
__block CGSize labelSize = CGSizeZero;
|
||||
label.updateLayoutSize = ^(CGSize size) {
|
||||
labelSize = size;
|
||||
NSLog(@"XPNetImageYYLabel 计算尺寸: %.2f x %.2f", size.width, size.height);
|
||||
};
|
||||
[label updateLayoutWithAttributedText:attributedText];
|
||||
|
||||
// 测试 XPMessageInfoModel
|
||||
XPMessageInfoModel *model = [[XPMessageInfoModel alloc] init];
|
||||
[model setContent:attributedText];
|
||||
NSLog(@"XPMessageInfoModel 计算高度: %.2f", model.rowHeight);
|
||||
|
||||
NSLog(@"=== 混合文本测试完成 ===\n");
|
||||
}
|
||||
|
||||
+ (void)testLongArabicTextLayout {
|
||||
NSLog(@"=== 测试长阿拉伯文本布局 ===");
|
||||
|
||||
// 创建长阿拉伯文本
|
||||
NSString *longArabicText = @"هذا نص طويل باللغة العربية لاختبار كيفية تعامل النظام مع النصوص الطويلة التي تحتاج إلى عدة أسطر للعرض. يجب أن يتم عرض هذا النص بشكل صحيح مع دعم الاتجاه من اليمين إلى اليسار.";
|
||||
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:longArabicText
|
||||
attributes:@{
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:16],
|
||||
NSForegroundColorAttributeName: [UIColor blackColor]
|
||||
}];
|
||||
|
||||
// 测试 XPNetImageYYLabel
|
||||
XPNetImageYYLabel *label = [[XPNetImageYYLabel alloc] init];
|
||||
__block CGSize labelSize = CGSizeZero;
|
||||
label.updateLayoutSize = ^(CGSize size) {
|
||||
labelSize = size;
|
||||
NSLog(@"XPNetImageYYLabel 计算尺寸: %.2f x %.2f", size.width, size.height);
|
||||
};
|
||||
[label updateLayoutWithAttributedText:attributedText];
|
||||
|
||||
// 测试 XPMessageInfoModel
|
||||
XPMessageInfoModel *model = [[XPMessageInfoModel alloc] init];
|
||||
[model setContent:attributedText];
|
||||
NSLog(@"XPMessageInfoModel 计算高度: %.2f", model.rowHeight);
|
||||
|
||||
NSLog(@"=== 长阿拉伯文本测试完成 ===\n");
|
||||
}
|
||||
|
||||
+ (void)runAllTests {
|
||||
NSLog(@"\n🚀 开始运行文本布局统一测试\n");
|
||||
|
||||
[self testArabicTextLayout];
|
||||
[self testMixedTextLayout];
|
||||
[self testLongArabicTextLayout];
|
||||
|
||||
NSLog(@"✅ 所有文本布局测试完成\n");
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,107 @@
|
||||
# 统一文本尺寸计算实现
|
||||
|
||||
## 概述
|
||||
|
||||
本次修改统一了应用中的文本尺寸计算方法,解决了阿拉伯文本显示问题,提高了 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 配置
|
||||
|
||||
```objc
|
||||
- (YYTextContainer *)createTextContainerForText:(NSAttributedString *)text {
|
||||
YYTextContainer *container = [YYTextContainer new];
|
||||
container.size = CGSizeMake(kRoomMessageMaxWidth, MAXFLOAT);
|
||||
|
||||
// 根据文本方向配置容器
|
||||
if (isMSRTL()) {
|
||||
container.truncationType = YYTextTruncationTypeStart;
|
||||
} else {
|
||||
container.truncationType = YYTextTruncationTypeEnd;
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
```
|
||||
|
||||
### 统一计算逻辑
|
||||
|
||||
```objc
|
||||
- (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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 优势
|
||||
|
||||
1. **精确性**:`YYTextLayout` 对复杂文本(包括 RTL)有更好的支持
|
||||
2. **一致性**:统一的计算方法确保所有文本类型的一致性
|
||||
3. **可维护性**:减少条件分支,代码更简洁
|
||||
4. **性能**:避免重复计算和额外的高度调整
|
||||
|
||||
## 测试
|
||||
|
||||
运行测试验证修改效果:
|
||||
|
||||
```objc
|
||||
#import "XPTextLayoutTest.h"
|
||||
|
||||
// 在适当的地方调用
|
||||
[XPTextLayoutTest runAllTests];
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保 `YYText` 库版本支持所需功能
|
||||
2. 测试各种文本场景,特别是长文本和混合文本
|
||||
3. 验证在不同设备和系统版本上的表现
|
||||
4. 关注性能影响,特别是在大量文本的场景下
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **字体优化**:为阿拉伯文本自动选择合适的字体(如 "GeezaPro")
|
||||
2. **缓存机制**:为频繁计算的文本添加缓存
|
||||
3. **性能监控**:添加性能指标监控文本计算耗时
|
||||
4. **更多测试**:增加边界情况和性能测试
|
||||
|
||||
## 风险评估
|
||||
|
||||
- **低风险**:核心逻辑修改,但使用成熟的 `YYTextLayout`
|
||||
- **兼容性**:保持现有 API 不变
|
||||
- **回滚**:如有问题可快速回滚到原有逻辑
|
@@ -115,19 +115,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateLayoutWithAttributedText:(NSAttributedString *)attributedText {
|
||||
CGSize maxSize = CGSizeMake(kRoomMessageMaxWidth, MAXFLOAT);
|
||||
CGSize finalSize = CGSizeZero;
|
||||
- (YYTextContainer *)createTextContainerForText:(NSAttributedString *)text {
|
||||
YYTextContainer *container = [YYTextContainer new];
|
||||
container.size = CGSizeMake(kRoomMessageMaxWidth, MAXFLOAT);
|
||||
|
||||
// 根据文本方向配置容器
|
||||
if (isMSRTL()) {
|
||||
finalSize = [attributedText boundingRectWithSize:maxSize
|
||||
options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin
|
||||
context:nil].size;
|
||||
container.truncationType = YYTextTruncationTypeStart;
|
||||
} else {
|
||||
YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:maxSize text:attributedText];
|
||||
finalSize = layout.textBoundingSize;
|
||||
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);
|
||||
}
|
||||
@@ -143,20 +150,12 @@
|
||||
range:range];
|
||||
[super setAttributedText:updatedAttributes];
|
||||
|
||||
CGSize maxSize = CGSizeMake(kRoomMessageMaxWidth, MAXFLOAT);
|
||||
YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:maxSize
|
||||
text:updatedAttributes];
|
||||
if (isMSRTL()) {
|
||||
CGSize size = [updatedAttributes boundingRectWithSize:maxSize
|
||||
options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin
|
||||
context:nil].size;
|
||||
if (self.updateLayoutSize) {
|
||||
self.updateLayoutSize(size);
|
||||
}
|
||||
} else {
|
||||
if (self.updateLayoutSize) {
|
||||
self.updateLayoutSize(layout.textBoundingSize);
|
||||
}
|
||||
// 统一使用 YYTextLayout 进行文本尺寸计算
|
||||
YYTextContainer *container = [self createTextContainerForText:updatedAttributes];
|
||||
YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:updatedAttributes];
|
||||
|
||||
if (self.updateLayoutSize) {
|
||||
self.updateLayoutSize(layout.textBoundingSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user