归因于文本附件和截断的字符串

时间:2014-04-01 09:45:00

标签: objective-c nsattributedstring nstextattachment

我有一个带附加图像的属性字符串(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];

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

5 个答案:

答案 0 :(得分:3)

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

所以你有几个选择。

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

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

答案 1 :(得分:3)

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

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

当你住在慕尼黑时,我认为你懂德语。因此,我想提一下,在我的书的第二部分第6章中有一个代码示例,用于在中间的孔周围布局文本的视图。你可以通过在最后省略一个矩形来做同样的事。

希望有所帮助!

答案 2 :(得分:0)

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

它适合你的目的吗?

答案 3 :(得分:0)

当您在UILabel中显示它时,您设置了

self.titleLabel.lineBreakMode = NSLineBreakByTruncatingMiddle

答案 4 :(得分:0)

最近我也遇到了类似的情况,这是我最终找到的解决方案,在我的情况下效果很好。

我有一个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))