在嵌入NSTextAttachment时,较高的UILabel中可能会出现缺失行

20

我可以使用转义换行符 (@"\n") 创建一个多行的 NSAttributedString。在 iOS 7 中,我现在可以通过 NSTextAttachment 在属性字符串中嵌入一个 UIImage

我注意到,每当我将一个带有嵌入图像的多行属性字符串设置为 UILabelattributedText 时,实际显示的行数与标签高度成反比例关系。例如,当标签的高度为80时,会显示两行;当高度约为100时,只显示第二行;当高度约为130时,则什么也不显示。

这个问题出现在试图在 UITableViewCell 中侧边放置多个 UILabels 并使标签 (纵向) 随单元格高度增长时。

有人能解释这是为什么吗?有没有不需要使 UILabel 变小的解决方法?


示例代码:

@implementation SOViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSMutableAttributedString *text1 = [[NSMutableAttributedString alloc] init];
    [text1 appendAttributedString:[[NSAttributedString alloc] initWithString:@"Line 1\n"]];
    [text1 appendAttributedString:[[NSAttributedString alloc] initWithString:@"Line 2"]];

    UIImage *image = [UIImage imageNamed:@"17x10"]; //some PNG image (17px by 10px)

    NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
    attachment.image = image;
    attachment.bounds = CGRectMake(0, 0, image.size.width, image.size.height);

    NSMutableAttributedString *text2 = [[NSMutableAttributedString alloc] init];
    [text2 appendAttributedString:[[NSAttributedString alloc] initWithString:@"Line 1\n"]];
    [text2 appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
    [text2 appendAttributedString:[[NSAttributedString alloc] initWithString:@"Line 2"]];

    CGFloat margin = 20;

    //shows both lines when height == 80
    //shows line 2 when 90 <= height <= 120
    //shows nothing when height == 130
    CGFloat height = ???;
    CGFloat width = 200;

    UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMake(margin, margin, width, height)];
    UILabel *label2 = [[UILabel alloc] initWithFrame:CGRectMake(margin, margin + height, width, height)];
    [self.view addSubview:label1];
    [self.view addSubview:label2];
    label1.backgroundColor = [UIColor orangeColor];
    label2.backgroundColor = [UIColor blueColor];
    label2.textColor = [UIColor whiteColor];
    label1.numberOfLines = 0;
    label2.numberOfLines = 0;
    label1.attributedText = text1;
    label2.attributedText = text2;

    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    imageView.frame = CGRectMake(margin + width, margin + height, image.size.width, image.size.height);
    [self.view addSubview:imageView];
}

@end

...将此代码放入“单视图应用程序”的默认视图控制器中。(您可以选择自己的图像。)


看起来这个 bug 将在 iOS 7.1 中得到修复。 - matt
@matt,你能确认修复了吗? - EthanB
@EthanB 是的,“appears” 只是合法的 CYA。 - matt
4个回答

21

这真的与NSTextAttachment没有关系。问题在于,在迄今发布的iOS 7中,UILabel不太擅长绘制属性字符串。一个简单的带有下划线和居中段落样式的属性字符串将在UILabel中显示为空白或部分为空白;同样的属性字符串在UITextView中绘制得很好。

因此,目前的一种解决方案是:改用UITextView。这实际上是一个相当好的解决方案,因为在iOS 7中,UITextView只是Text Kit堆栈的包装器。因此,它以一种简单直接的方式绘制属性字符串。它不会受到先前iOS版本中与Web Kit的内在关系的限制。

另一方面,我也发现了一个解决这个UILabel bug的方法;您必须以某种方式调整标签的行数和字符串,使文本紧贴标签的顶部:请参见我的答案 - https://dev59.com/BGIk5IYBdhLWcg3wl_PE#19409962

或者您可以等待Apple修复错误,并祈祷好运。编辑:在iOS 7.1中,似乎将修复该错误,不需要任何解决方法。


花了几个小时试图弄清楚为什么UILabel在模拟器和大多数设备上都能正常工作,但在客户设备上却不能。结果发现他们仍在使用iOS 7.0,因此遇到了这个bug。改用UITextView效果很好。 - NigelG
1
我在iOS 7.1中没有看到修复。你能告诉我们你从哪里听说会发生这种情况吗? - michaelsnowden
@doctordoder 示例:https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch10p507tabStops/TextTabTest/ViewController.m 添加一个标签,并将最终的属性字符串放入标签中,而不是文本视图。在7.0中,标签为空。在7.1中,完美运行。 - matt

2

看起来是UILabel中的一个bug。当使用UITextView时,相同的代码可以正常工作(因为UITextView的默认字体大小不同,所以我在不同的高度上进行了测试)。


@EthanB:不,我已经花了一些时间研究它,但是没有取得任何成功(除了使用 UITextView 之外)。那些 API 是新的,所以也许 Apple 还没有充分测试过它们。 - Arek Holko

1
我已经找到了另一种解决这个 bug 的方法,与我的先前答案有所不同,我将其作为另一个答案提供:让标签设置自己的高度。
在这段代码中,我删除了具有固定宽度约束的标签的高度约束,并用大于高度约束替换它(我相信还有其他实现相同结果的方法):
[self.lab removeConstraint:self.labelHeight];
[self.lab addConstraint:
 [NSLayoutConstraint constraintWithItem:self.lab 
 attribute:NSLayoutAttributeHeight 
 relatedBy:NSLayoutRelationGreaterThanOrEqual 
 toItem:nil attribute:0 multiplier:1 constant:20]];

那个标签可以正确地显示我输入的所有属性字符串!当然,你会失去字符串自动垂直居中的功能,但这正是错误的根源,所以失去它并不可怕。

0

你尝试使用boundingRectWithSize方法获取文本的实际高度了吗:

NSAttributedString *text;
CGFloat width = 200;
CGRect rect = [text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil];
CGFloat height = rect.size.height;

如果您有一个固定的宽度,当您增加标签的大小时,文本的“边界”高度不应该改变,对吗? - EthanB
随着文本的增长,您可以通过将边框大小的高度设置为CGFLOAT_MAX来实现。 - cescofry
1
OP问为什么更改UILabel的高度会影响文本的可见行。请参考示例代码。对于给定的不可变NSAttributedString,边界高度(您的rect.size.height)不应取决于UILabel的高度。 - EthanB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接