如何从 iOS UILabel 对象中的 NSAttributedString(从 HTML 转换而来)消除多余的换行符?

How to eliminate superfluous line breaks from NSAttributedString (converted from HTML) in iOS UILabel object?

我有一些 table 视图单元格需要显示来自我们服务器的一些简单 html。我正在将 html 转换为 NSAttributedString,以便它可以显示在单元格上的 UILabel 对象中。但是当 table 视图绘制单元格时,UILabel 对象似乎在它们的末尾添加了一些换行符。注意这里额外的space:

(顺便说一句,图片提供了两个 table 视图单元格,因为我尝试了两种不同形式的 html 并且在 both 情况下似乎插入了换行符) 我认为这可能是由于我的布局,UILabel 可能被布局强制进入其高度,而不是根据分配的文本自由 resize/shrink 本身。但是当我向同一个标签提供一个简单的 NSAttributedString 时,它是这样创建的:

 sRet = [[NSAttributedString alloc] initWithString:@"[Dummy text string 111]"];

(黄色)UILabel 确实会像这样缩小响应:

充分证明我的布局允许 UILabel 根据分配给它的文本自由调整大小。

下面是分配给标签的 html 以及当我将它们记录到控制台时生成的 NSAttributedString 值。这些对应于您在上面第一张图片中的黄色 UILabel 对象中看到的内容。这是正在转换为属性字符串并分配的 html:

<h2>Student did poorly</h2>

<p><span style="font-family:comic sans ms,cursive;"><span style="font-size:14px;">Student did ok</span></span></p>

这里是相应的属性字符串:

Student did poorly
{
    NSColor = "kCGColorSpaceModelRGB 0 0 0 1 ";
    NSFont = "<UICTFont: 0x7fce3f8798e0> font-family: \"TimesNewRomanPS-BoldMT\"; font-weight: bold; font-style: normal; font-size: 18.00pt";
    NSKern = 0;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 22/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n), DefaultTabInterval 36, Blocks (null), Lists (null), BaseWritingDirection 0, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 2";
    NSStrokeColor = "kCGColorSpaceModelRGB 0 0 0 1 ";
    NSStrokeWidth = 0;
}

Student did ok{
    NSColor = "kCGColorSpaceModelRGB 0 0 0 1 ";
    NSFont = "<UICTFont: 0x7fce3d5a96c0> font-family: \"Snell Roundhand\"; font-weight: normal; font-style: normal; font-size: 14.00pt";
    NSKern = 0;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 19/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n), DefaultTabInterval 36, Blocks (\n), Lists (\n), BaseWritingDirection 0, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0";
    NSStrokeColor = "kCGColorSpaceModelRGB 0 0 0 1 ";
    NSStrokeWidth = 0;
}
{
    NSColor = "kCGColorSpaceModelRGB 0 0 0 1 ";
    NSFont = "<UICTFont: 0x7fce3d7959e0> font-family: \"Times New Roman\"; font-weight: normal; font-style: normal; font-size: 12.00pt";
    NSKern = 0;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 15/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n), DefaultTabInterval 36, Blocks (\n), Lists (\n), BaseWritingDirection 0, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0";
    NSStrokeColor = "kCGColorSpaceModelRGB 0 0 0 1 ";
    NSStrokeWidth = 0;
}

在 html 和属性字符串中,我都看不到 UILabel 对象具有额外垂直 space 的原因。 Fwiw,每个单元格(黄色和白色)上的一对 UILabel 对象都包含在 UIStackView 中,所以它可能以某种方式造成了恶作剧。但是只有四个约束,尾随、前导、底部和顶部,并且如上所述,标签可以很好地调整大小,因此这些约束似乎对此没有贡献。

我遇到了同样的问题,需要删除我的 <p> 标签添加的不需要的换行符,但无法在服务器端进行更改。所以我首先想到了这个解决方案:

Swift

while !attributedString.string.isEmpty //Validates if the attributed string has at least one character
    && CharacterSet.newlines.contains(attributedString.string.unicodeScalars.last!) //Compares if the last unicode character of the attributed string is inside the `CharacterSet` of newlines
    {
    attributedString.deleteCharacters(in: NSRange(location: attributedString.length - 1, length: 1)) //Deletes the last character of the attributed string
}

Obj-C(未测试)

while ([attributedString.string length] > 0
    && [[attributedString.string substringFromIndex:[attributedString.string length] - 1] rangeOfCharacterFromSet:NSCharacterSet.newlineCharacterSet].location != NSNotFound)
    {
    [attributedString deleteCharactersInRange:NSMakeRange([attributedString length] - 1, 1)];
}

(我在将 html 代码转换为属性字符串后立即执行此代码。)


但我可以从答案中看出 <h#> 标签也添加了换行符。因此,对于扩展解决方案,可能类似于为每对标签制作一个属性字符串;应用上面的过滤器;然后将它们加入一个属性字符串或将它们显示在不同的标签中。


编辑

我使用 String 的扩展将 HTML 代码转换为 NSMutableAttributedString:

extension String {

    /// Returns a new attributed string loading any HTML tags that the receiver contains.
    ///
    /// If the HTML code is malformed or can not format it, this method returns the receiver but as a `NSMutableAttributedString`. If the formated resulting string contains one or more newlines at the end, they are removed.
    func htmlFormat() -> NSMutableAttributedString {
        var attributedString = NSMutableAttributedString(string: self, attributes: [.font : UIFont.preferredFont(forTextStyle: .body)])

        if let data = data(using: .utf8),
            let formatedString = try? NSMutableAttributedString(data: data,
                                                                options: [.documentType: NSAttributedString.DocumentType.html,
                                                                          .characterEncoding: String.Encoding.utf8.rawValue],
                                                                documentAttributes: nil) {
            attributedString = formatedString
        }

        while !attributedString.string.isEmpty
        && CharacterSet.newlines.contains(attributedString.string.unicodeScalars.last!) {
            attributedString.deleteCharacters(in: NSRange(location: attributedString.length - 1, length: 1))
        }

        return attributedString
    }

}