【问题标题】:Attributed string with text attachment and truncation带有文本附件和截断的属性字符串
【发布时间】:2014-04-01 09:45:00
【问题描述】:

我有一个带有图像的属性字符串 (NSTextAttachment)。这没问题,但我遇到了一个似乎无法解决的截断问题。

在示例中,假设字符串## 是图像。所以我的字符串看起来像Hello world! ##。段落样式设置尾部截断。

现在,如果空间有限,文本将被省略号截断(这是我想要的)。但不幸的是,图像也被截断了。

所以结果是这样的:

Hello w...

但我希望它看起来像:

Hello...##

也就是说,我希望图片附件不会被截断,它应该始终可见。

附件的原因是我希望图像始终位于字符串的末尾,因此当文本较短时,图像位于末尾,而当文本换行为多行时,我也希望图像位于到最后。尝试手动将图像“放在外面”是行不通的,因为文本不会被正确截断。

那么,有没有办法告诉NSAttributedString不要截断图像?

生成属性字符串的示例代码:

NSString *title;
NSMutableAttributedString *attributedString;
NSMutableParagraphStyle *paragraph;
NSDictionary *attributes;
NSTextAttachment *attachment;

paragraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paragraph.hyphenationFactor = 1.0;
paragraph.lineBreakMode = NSLineBreakByTruncatingTail;

attributes = @{
    NSForegroundColorAttributeName : [self titleTextColor],
    NSParagraphStyleAttributeName : paragraph,
};

title = @"Hello world!";
attributedString = [[NSMutableAttributedString alloc] initWithString:title
                                                          attributes:attributes];

attachment = [[NSTextAttachment alloc] init];
attachment.image = [UIImage imageNamed:@"myImage"];
[attributedString appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
[attachment release];

self.titleLabel.attributedText = attributedString;

[attributedString release];
[paragraph release];

编辑:其中一个重要部分(在上面的描述中有点丢失)是该解决方案需要适用于多行文本。

【问题讨论】:

    标签: objective-c nsattributedstring nstextattachment


    【解决方案1】:

    这并不容易实现。您可以将段落样式设置为NSLineBreakByTruncatingMiddle,但结果将是最后一行在中间被截断("Hel...rld!##"

    所以你有几个选择。

    • 您可以重新设计,使图片不会放在文本的末尾。
    • 您可以自己计算字符串的结尾,截断它并添加文本附件 - 做起来并不简单。
    • 使用 iOS7 的 Text Kit 实现自定义截断逻辑。不幸的是,尽管在许多博客和 Apple 自己的文档上都写错了,UILabel 不使用 TextKit 来呈现文本,它使用 CoreText,从而使事情变得更加困难。我建议完全放弃 UILabel 并使用带有 Text Kit 支持的自定义 UIView 实现。您可以找到here 一个使用 Text Kit 绘制文本的视图小示例,从而生成类似于标签的视图。现在您可以获取绘制字形的边界框,并正确放置图像。

    这两种选择都不是完美的。您没有在最后一行的末尾提到图像是什么以及为什么需要它。我可能会选择选项 #1 并稍微改变设计,尽管 Text Kit 选项并不难实现。

    【讨论】:

    • 如果我必须这样做,CoreText 可能是一个解决方案,但需要做很多工作(和needs extra work for the image)。我不在乎为此实施我自己的UIView,但我正在寻找“最理智”的解决方案。
    • @DarkDust 您正在描述一个困难的案例。我不认为“TextKit”很疯狂,只是苹果变得懒惰并且没有完成在标签中使用它的工作。可能是Apple的控件仍然存在TextKit的错误,因此他们直接将标签留给CT。用于使用 WebKit 的标签;至少这是一种改进。 >_
    【解决方案2】:

    我认为您不会通过“技巧”获得预期的结果。你必须做真正的工作:子类NSTextContainer并覆盖-lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:。这包括设置您自己的文本堆栈。

    代码太长,无法在此处发布。但是文档中有一些示例。 (OS X 的文档更丰富,IIRC。有区别,但你可以用它来对部分有一个基本的了解。)所以这个答案仍然是一个指针。

    由于您住在慕尼黑,我假设您懂德语。因此我想提一下,在我的书的第二部分第 6 章中有一个代码示例,用于在中间的一个洞周围布置文本的视图。您可以通过在末尾省略一个 rect 来做同样的事情。

    希望有帮助!

    【讨论】:

    • 能详细说明一下吗?我已经用 NSTextContainer 做了一些实验,我什至无法让它跟踪多行字符串的结尾。那么我会在被覆盖的lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect: 中做什么?
    • 我的基本想法是,当您到达文本末尾并看到它不适合建议的矩形时,您只需减去右侧图像的空间(假设从左到- 正确的书写方向)以使文本系统缩短字符串。比你在右侧绘制图像。如果有足够的空间,你知道文本使用的最后位置并在那里绘制图像。
    • 要识别字符串的结尾,characterIndex 应该会有所帮助。也许你必须从字符索引开始计算下一个字符的布局,看看是否合适。
    • 好主意。到目前为止,这看起来是最精确的解决方案:尽可能让它正常绘制,提供缩短的最后一行以正确绘制省略号,然后手动绘制图像。今天或明天我会玩这个。
    • 玩了一个多小时后,我无法让它做我想做的事。让 NSTextStorage 绘制 2 行并截断最后一行已经是一个问题。 drawWithRect:options:context: 忽略文本容器中的值,让布局管理器绘制字形始终绘制整个字符串而不截断。哦,亲爱的……
    【解决方案3】:

    我想一种方法是获取完整字符串的长度,截断字符串的长度,然后使用Hello world! 生成一个新的NSString,截断差+ 3,然后在末尾添加...##

    它符合你的目的吗?

    【讨论】:

    • 对于像这样的单行字符串可以工作,是的。不幸的是,我需要它来处理多行字符串。
    【解决方案4】:

    当你在UILabel中显示的时候你设置了吗

    self.titleLabel.lineBreakMode = NSLineBreakByTruncatingMiddle
    

    【讨论】:

    • 对不起,但这根本不是我想要的。输出完全不同。
    【解决方案5】:

    最近我也遇到了类似的情况,这是我最终得到的解决方案,对我来说效果非常好。

    我有一个 UILabel,我想显示一个带有图像的字符串。但是如果这个组合超过了 UILabel 的宽度,我不得不截断字符串,然后在末尾附加图像。

    我将在下面发布我的解决方案,它会对某人有所帮助。

      /// Provide a single line attibuted string with the image at the end of the string, and will truncate the string if the whole composision will exceed the frame size.
      /// - Parameters:
      ///   - string: The string which we are going to append the image
      ///   - rect: The UILabel rect of the string
      ///   - font: The Font to use in the attributed string
      ///   - imageName: The image name
      ///   - imageOrigin: The image location
      /// - Returns: The attibuted string with the image at the end. This string will be truncated if needed.
      private func getAttributedStringWithImage(string: String,
                                                rect: CGSize,
                                                font: UIFont,
                                                imageName: String,
                                                imageOrigin: CGPoint) -> NSAttributedString {
        
        // creating image to append at the end of the string
        let iconImage = UIImage(named: imageName)!
        let icon = NSTextAttachment()
        icon.bounds = CGRect(x: imageOrigin.x, y: imageOrigin.y, width: iconImage.size.width, height: iconImage.size.height)
        icon.image = iconImage
        let iconString = NSAttributedString(attachment: icon)
        
        // we will calculate the "attributed string length with image at the end" to deside whether we need to truncate
        // the string to append the image or not by looping the string
        var newStr = ""
        for char in string {
          newStr += String(char)
          
          let attStr = NSMutableAttributedString(string: (newStr + "..."), attributes: [ .font: font, .kern: 0.0 ])
          let stringRect = attStr.boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: rect.height),
                                               options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
          
          if stringRect.width + imageOrigin.x +  iconImage.size.width + 5 > rect.width {
            newStr += "..."
            break
          }
        }
        
        let titleString = NSMutableAttributedString(string: newStr, attributes: [ .font: font, .kern: 0.0 ])
        titleString.append(iconString)
        
        return titleString
      }
    

    然后这样称呼它:

      let attributedText = getAttributedStringWithImage(string: contactEmailOrNumber,
                                                        rect: self.titleLabel.frame.size,
                                                        font: UIFont(name: "FontName-Bold", size: xx.0)!,
                                                        imageName: "ImageName",
                                                        imageOrigin: CGPoint(x: 6, y: -3))
    

    【讨论】:

      猜你喜欢
      • 2021-10-28
      • 2012-01-20
      • 1970-01-01
      • 2014-09-02
      • 2017-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-12
      相关资源
      最近更新 更多