Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have some table view cells that need to display some simple html from our server. Am converting the html into an NSAttributedString so that it can be displayed in UILabel objects on the cells. But when the table view draws the cells the UILabel objects seem to have some line breaks added to the end of them. Notice extra space here:

enter image description here

(Btw, the pic provides two tableview cells because I tried two different forms of html and in both cases there seem to be inserted line breaks) I thought perhaps it was due to my layout, that maybe the UILabel was being forced into its height by the layout and not free to resize/shrink itself based upon the text it is assigned. But when I supply to the same label a simple NSAttributedString created this way:

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

the (yellow) UILabel indeed shrinks in response like so:

enter image description here

pretty much demonstrating that my layout is allowing the UILabel to freely resize according to the text assigned to it.

Below is the html being assigned to the labels and the resulting NSAttributedString values when I log them to the console. These correspond to what you see in the yellow UILabel objects in the first image above. Here is the html being converted to an attributed string and assigned:

<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>

And here are the corresponding attributed strings:

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;
}

In neither the html nor the attributed strings do I see reason for the UILabel objects having that extra vertical space. Fwiw, the pair of UILabel object on each cell (the yellow and the white) are contained within a UIStackView, so perhaps it could somehow be doing the mischief. But only four constraints, trailing, leading, bottom and top, and as mentioned above the label is able to be resized fine so these constraints don't seem to be contributing to this.

like image 783
Alyoshak Avatar asked Nov 13 '18 02:11

Alyoshak


1 Answers

I faced the same problem where needed to delete the unwanted newlines added by my <p> tags and was not able to change things on server side. So I came first with this solution:

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 (not tested)

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

(I execute this code right after converting the html code into an attributed string.)


But I can see by the answers that also the <h#> tags adds newlines. So for a extended solution could be something like make an attributed string for every pair of tags; apply them the filter above; and after that, join them in one attributed string or display them in different labels.


Edit

I use an extension of String to convert the HTML code to an 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
    }

}
like image 164
Ángel Téllez Avatar answered Sep 18 '22 23:09

Ángel Téllez