iOS 16 计算文本尺寸崩溃
线上出现一个奇怪的崩溃, 堆栈信息比较少, 每天十几次, 发现是 iOS16 的新坑.
崩溃日志最后的堆栈是
1
| CGSize size = [self.descriLabel.text calculateBoundingRectWithSize:CGSizeMake(sessionMaxWidth, CGFLOAT_MAX) font:[ComHelper regularSystemFontOfSize:14] paragraphStyle:paragraphStyle].size;
|
我们 NSString 工具类计算尺寸出来崩溃, 计算用的 api 是boundingRectWithSize:options:attributes:context:context
, 也是很常用的接口.
复现路径
一番定位后, 复现需要满足如下几个条件
- iOS 16 版本, 各个小版本均可
- 文本中含有
@" \n"
, 这个空格是 “NonBreakingSpace” 不间断空格, 空格后面接换行符
- attributes 中使用了 ParagraphStyle, 并且
alignment
设置为了NSTextAlignmentJustified
- 换行符左边的文本足够长
什么是 “不间断空格”?
- 不间断空格 \u00a0, 主要用在office中, 让一个单词在结尾处不会换行显示, 快捷键ctrl+shift+space
- 半角空格(英文符号) \u0020, 代码中常用的
- 全角空格(中文符号) \u3000, 中文文章中使用
修复方案
这种特殊场景下换成正常的空格即可
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. NSString *enterChar = @"\n"; NSString *normalSpace = @" "; // 普通空格 \u0020 NSString *nonBreakingSpace = @" "; // \u00a0 NSString *normalEmpty = @" \n"; // \u0020\n NSString *errorEmpty = @" \n"; // \u00a0\n NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; [paragraphStyle setLineSpacing:8]; NSMutableParagraphStyle *paragraphStyle1 = [[NSMutableParagraphStyle alloc] init]; [paragraphStyle1 setLineSpacing:8]; paragraphStyle1.alignment = NSTextAlignmentJustified;
[self testA:enterChar count:30 left:@"abc" right:@"只" style:nil]; [self testA:normalSpace count:30 left:@"abc" right:@"只" style:nil]; [self testA:nonBreakingSpace count:30 left:@"abc" right:@"只" style:nil]; [self testA:normalEmpty count:30 left:@"abc" right:@"只" style:nil]; [self testA:errorEmpty count:30 left:@"abc" right:@"只" style:nil];
[self testA:enterChar count:30 left:@"abc" right:@"只" style:paragraphStyle]; [self testA:normalSpace count:30 left:@"abc" right:@"只" style:paragraphStyle]; [self testA:nonBreakingSpace count:30 left:@"abc" right:@"只" style:paragraphStyle]; [self testA:normalEmpty count:30 left:@"abc" right:@"只" style:paragraphStyle]; [self testA:errorEmpty count:30 left:@"abc" right:@"只" style:paragraphStyle];
[self testA:enterChar count:30 left:@"abc" right:@"只" style:paragraphStyle1]; [self testA:normalSpace count:30 left:@"abc" right:@"只" style:paragraphStyle1]; [self testA:nonBreakingSpace count:30 left:@"abc" right:@"只" style:paragraphStyle1]; [self testA:normalEmpty count:30 left:@"abc" right:@"只" style:paragraphStyle1]; // 只有这个会崩溃 [self testA:errorEmpty count:30 left:@"abc" right:@"只" style:paragraphStyle1]; NSLog(@"success"); }
- (void)testA:(NSString *)errorStr count:(NSUInteger)count left:(NSString *)left right:(NSString *)right style:(NSMutableParagraphStyle *)paragraphStyle { NSMutableString *leftStr = [[NSMutableString alloc] init]; NSMutableString *rightStr = [[NSMutableString alloc] init]; for (int i = 0; i < count; i++) { [leftStr appendString:left]; for (int j = 0; j < count; j++) { [rightStr appendString:right]; NSString *testStr = [NSString stringWithFormat:@"%@%@%@", leftStr, errorStr, rightStr]; NSDictionary *dic = @{ NSFontAttributeName: [UIFont systemFontOfSize:14 weight:UIFontWeightRegular], }; if (paragraphStyle) { dic = @{ NSFontAttributeName: [UIFont systemFontOfSize:14 weight:UIFontWeightRegular], NSParagraphStyleAttributeName: paragraphStyle }; } // left 比较长, 绘制导致换行, 超过了设置的宽度(300) 才会崩溃 CGRect rect = [testStr boundingRectWithSize:CGSizeMake(300, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil]; } } }
|