NSMutableAttributedString add different alignments
Asked Answered
S

2

15

Is it possible to add left and right alignments to different parts of the string?

I tried to add alignment attribute to the right part:

    NSMutableParagraphStyle *paragrahStyle = [[NSMutableParagraphStyle alloc] init];
    [paragrahStyle setAlignment:NSTextAlignmentRight];
    [mutableAttributedString addAttribute:NSParagraphStyleAttributeName value:paragrahStyle range:rangeOfDate];

But the whole string is aligned to the left.

Stilly answered 24/5, 2013 at 14:34 Comment(0)
K
43

What you do in your code is to set the paragraph style (the text from \n to \n) and it is not possible to have multiple text alignments in the same paragraph. I had the same problem and ended up using multiple UILabels in iOS 6 :-(

However, in iOS 7 I noticed TabStops for ParagraphStyle. This actually makes it possible, but I find the documentation quite inadequate. However, I ended up getting it to work. You can set a right aligned TapStop, which causes your text to get rendered to the left of your specified stop (until that space is filled). In order to get my simple example to work I had to add at least one more attribute besides the paragraph one - it could have been just a clear UIColor for the background though :-)

The following is a simple example of drawRect in a UIView:

- (void)drawRect:(CGRect)rect
{
    NSString *str = @"to the left\tto the right\nleft again\tright again";
    NSMutableAttributedString *att = [[NSMutableAttributedString alloc] initWithString:str];

    NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
    paragraph.maximumLineHeight = 12.0f;
    paragraph.alignment = NSTextAlignmentLeft;

    NSTextTab *t = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight location:rect.size.width options:nil];
    paragraph.tabStops = @[t];

    [att addAttribute:NSBackgroundColorAttributeName value:[UIColor magentaColor] range:NSMakeRange(0, @"to the left".length)];
    [att addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Trebuchet-BoldItalic" size:12.0] range:NSMakeRange(12, 5)];
    [att addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(str.length - 4, 2)];
    [att addAttribute:NSParagraphStyleAttributeName value:paragraph range:NSMakeRange(0, str.length)];

    [att drawInRect:rect];
}

That code will render the following:

enter image description here

Knotgrass answered 20/9, 2013 at 21:37 Comment(7)
I'm confused why, if I remove all the attributes besides NSParagraphStyleAttributeName, that it breaks the formatting. Is this a bug or am I misunderstanding something?Galacto
Yes, sounds like a bug to me. I came across it while doing this: #24435052Araucaria
Why you draw attributed string in drawRect? What if I just want to set it into my label?Mediterranean
I see that UILabel has a property "attributedText", which may render the attributed string properly.Knotgrass
iOS9, was able to just add the single attribute - but in the NSTextTab if I tried to set the option (which worked fine with right tab stops), it created a problem, so I left it empty. Thanks so much for this!Engud
What is rect.size.width? What if I don't want to use that. What should I put in location:??Motionless
You need to specify that the text after the stop is right-aligned. This alignment is set to be equal to the width of the containing view, causing the right of the text to match the right of the view. Makes sense?Knotgrass
G
9

Building on @Verglas's answer...

The way you'd normally do something like this in HTML is via floating. Something like this::

<div><p style='float: left;'>Left</p><p style='float: right;'>Right</p><div style='clear: both;'></div></div>

It would be great if you could transform this into a NSAttributedString and have it work:

NSString* html = @"<div><p style='float: left;'>Left</p><p style='float: right;'>Right</p><div style='clear: both;'></div></div>";

NSData* d = [html dataUsingEncoding: NSUTF8StringEncoding];

NSAttributedString* as = [[NSMutableAttributedString alloc] initWithData: d
                                               options: @{
                                                          NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                          NSCharacterEncodingDocumentAttribute : @(NSUTF8StringEncoding)
                                                          }
                                    documentAttributes: nil
                                                 error: nil];

Sadly, it does not work.

For a second attempt, we can try using a HTML table:

html = @"<table style='width:100%'><tr><td>Left</td><td style='text-align:right;'>Right</td></tr></table>";

Curiously, this works as intended. What's even more curious are the attributes it generates:

2014-08-27 14:27:31.443 testParagraphStyles[2095:60b] range: {0, 5} attributes: {
     NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n), DefaultTabInterval 36, Blocks (\n    \"<NSTextTableBlock: 0x8d9c920>\"\n), Lists (null), BaseWritingDirection 0, HyphenationFactor 0, TighteningFactor 0, HeaderLevel 0";

2014-08-27 14:27:31.444 testParagraphStyles[2095:60b] range: {5, 6} attributes: {
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n), DefaultTabInterval 36, Blocks (\n    \"<NSTextTableBlock: 0x8da1550>\"\n), Lists (null), BaseWritingDirection 0, HyphenationFactor 0, TighteningFactor 0, HeaderLevel 0";
}

Scroll to the right and notice the reference to NSTextTableBlock. NSTextTable isn't a public API on iOS, but NSAttributedString initWithData:options:documentAttributes:error: used it to generate our attributed string from HTML. This is painful because it means we can't construct a NSAttributedString by hand (we must generate it fro HTML using this API).

Building attributed strings from HTML is slow and largely undocumented. I avoid it whenever I can.

Galacto answered 27/8, 2014 at 22:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.