TSMarkdownParser icon indicating copy to clipboard operation
TSMarkdownParser copied to clipboard

Apply NSParagraphStyleAttributeName to list items via listAttributes

Open nateirwin opened this issue 8 years ago • 14 comments

I'm trying to add consistent indentation to ordered and unordered list items coming down in a Markdown string. If I change the defaultAttributes property on the TSMarkdownParser instance, adding an NSParagraphStyleAttributeName, the indentation settings work but are applied to all of the Markdown text. What I'm hoping to do is only apply the NSParagraphStyleAttributeName to the text that's associated with a Markdown list.

Pared down code here:

NSMutableParagraphStyle *listStyle;
listStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[listStyle setDefaultTabInterval:18];
[listStyle setFirstLineHeadIndent:0];
[listStyle setHeadIndent:18];
[listStyle setLineSpacing:7.5];
[listStyle setTabStops:@[ [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:18 options:nil] ]];
NSString *markdown = @"## Title\n\n- Item 1\n- Item 2\n- Item 3\n\n1. Item 1\n2. Item 2\n3. Item 3\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed interdum tortor odio, vitae porttitor lacus dapibus non. Etiam sodales euismod sapien a porta. Vivamus lectus ipsum, ultrices vel efficitur eget, sollicitudin in libero. Duis posuere felis velit, sit amet sodales nulla pharetra gravida. Aenean non justo venenatis, auctor nibh ut, hendrerit tortor.";
TSMarkdownParser *parser = [TSMarkdownParser standardParser];

parser.defaultAttributes = @{
    // This works, but affects all of the text, including text that isn't part of a list.
    // NSParagraphStyleAttributeName: listStyle
};
parser.listAttributes = @[
    // This does not affect the styling of the list items. Perhaps it's getting overridden?
    @{ NSParagraphStyleAttributeName: listStyle }
];

NSAttributedString *string = [parser attributedStringFromMarkdown:markdown];

Hopefully this explanation makes sense. I'm an Objective-C newb.

nateirwin avatar May 17 '16 01:05 nateirwin

Hello @nateirwin, defaultAttributes is for the whole attributed string, and listAttributes is only for list items.

I haven't tried NSParagraphStyleAttributeName before, so I will make some tests tonight. But to make things obvious, you can try this in the mean time:

parser.listAttributes = @[
    @{ NSForegroundColorAttributeName: [UIColor redColor] }
];

Coeur avatar May 17 '16 02:05 Coeur

Hey @Coeur, thanks for getting back to me.

Adding the code from your post does work correctly, so it definitely seems like there's something going on with NSParagraphStyleAttributeName. I'll try to dig in some more, but any help is much appreciated!

nateirwin avatar May 17 '16 02:05 nateirwin

@nateirwin, did you find a reason or a workaround?

Coeur avatar Jun 04 '16 02:06 Coeur

Nope, no more progress on this. It isn't a show stopper for us, though it would be nice to be able to apply different styling to lists.

nateirwin avatar Jun 06 '16 16:06 nateirwin

This seems to be an issue with NSAttributedString. Applying the paragraph style for each bulleted line does not work. Instead, the style needs to be applied across all the bulleted lines. A workaround might be to use a separate attributed string for bulleted lists.

phatmann avatar Aug 18 '16 10:08 phatmann

@Coeur Any progress on this issue? or workaround?

kyledold avatar Jun 07 '17 11:06 kyledold

@kyledold @Coeur actually this works fine right now in my App if I simply set it for the listAttributes. Like this:

NSMutableParagraphStyle *style = [NSMutableParagraphStyle new]; style.headIndent = 28; NSDictionary *listAttributes = @{NSFontAttributeName:[UIFont fontWithName:kFontLatoRegular size:16.0f], NSForegroundColorAttributeName:[AppAppearance textDarkGray], NSParagraphStyleAttributeName:style}; parser.listAttributes = @[listAttributes];

So I think this issue can be closed?

BobDG avatar Jun 07 '17 11:06 BobDG

So my issue was setting the NSParagraphStyleAttributeName attribute for the "listAttributes" property of the parser wasn't having any effect, even after trying @BobDG's example.

The only way I could get this to work was to set the NSParagraphStyleAttributeName attribute of the "defaultAttributes" property.

kyledold avatar Jun 07 '17 12:06 kyledold

@kyledold hm strange, it works perfectly for me. Only for bullet lists unordered though, nut numbered lists because the regex doesn't take those into account.

BobDG avatar Jun 07 '17 12:06 BobDG

@BobDG which version are you using? I've got v2.1.3

kyledold avatar Jun 07 '17 12:06 kyledold

@kyledold me too.. what you can check is perhaps if the regex actually captures your lists by putting some logs at the right places?

BobDG avatar Jun 07 '17 12:06 BobDG

I am also having this issue. Version 2.1.3. Things look great when applying the style to the whole string using defaultAttributes but using listAttributes doesn't seem to do anything once the string wraps.

I think this may be a mutable string bug though. I have experienced issues in the past trying to apply multiple paragraph styles to a attributed string.

Kind of a huge bummer because it makes an existing view more complicated to break out all the strings into their own labels.

khill25 avatar Jun 09 '17 23:06 khill25

@khill25 @BobDG I found the only way to get NSParagraphStyleAttributeName set for list elements is to create your own version of the TSMarkdownParser object and then in the init method where you add all the parsing logic you need to replace the "addListParsingWithMaxLevel" method with the following:

+ (instancetype)standardParser {

  LURMarkDownParser *defaultParser = [self new];

  ....

  NSMutableParagraphStyle *listStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
  [listStyle setDefaultTabInterval:18];
  [listStyle setFirstLineHeadIndent:0];
  [listStyle setHeadIndent:28];

  [defaultParser addListParsingWithMaxLevel:0 leadFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range, NSUInteger level) {
    NSMutableString *listString = [NSMutableString string];
    while (--level)
     [listString appendString:@"\t"];
   [listString appendString:@"•\t"];
   [attributedString replaceCharactersInRange:range withString:listString];
	
    // Apply paragraph style attributes to string 
    [attributedString addAttributes:@{NSFontAttributeName:[UIFont fontWithName:defaultFontFamily size:16], NSParagraphStyleAttributeName: listStyle} range:range];
	
  } textFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range, NSUInteger level) {
    [LURMarkDownParser addAttributes:weakParser.listAttributes atIndex:level - 1 toString:attributedString range:range];
  }];

  .....

}

Not the cleanest solution but it works.

kyledold avatar Jun 12 '17 09:06 kyledold

@kyledold ok nice you got it working! If I look at the code I still don't understand why mine is working without any adaptations and that you need these changes. But I'm glad it works for you now :)

BobDG avatar Jun 12 '17 09:06 BobDG