Nsattributedstring Inserting a Bullet Point

NSAttributedString inserting a bullet point?

The easy bit: [mutableAttributedString insertAttributedString: @"•\t" atIndex:0];

The hard bit. Something along the following lines. (This is extracted from a bigger project, but it may give you a decent start.)

NSMutableAttributedString * string = [[NSMutableAttributedString alloc] initWithString:@"•\texample bullet fill out the text to check what happens on the second line and make sure it is lining up OK"];

CTTextAlignment alignment = kCTLeftTextAlignment;
CGFloat paragraphSpacing = 0.0;
CGFloat paragraphSpacingBefore = 0.0;
CGFloat firstLineHeadIndent = 15.0;
CGFloat headIndent = 30.0;

CGFloat firstTabStop = 15.0; // width of your indent
CGFloat lineSpacing = 0.45;

CTTextTabRef tabArray[] = { CTTextTabCreate(0, firstTabStop, NULL) };

CFArrayRef tabStops = CFArrayCreate( kCFAllocatorDefault, (const void**) tabArray, 1, &kCFTypeArrayCallBacks );
CFRelease(tabArray[0]);

CTParagraphStyleSetting altSettings[] =
{
{ kCTParagraphStyleSpecifierLineSpacing, sizeof(CGFloat), &lineSpacing},
{ kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment},
{ kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &firstLineHeadIndent},
{ kCTParagraphStyleSpecifierHeadIndent, sizeof(CGFloat), &headIndent},
{ kCTParagraphStyleSpecifierTabStops, sizeof(CFArrayRef), &tabStops},
{ kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat), ¶graphSpacing},
{ kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(CGFloat), ¶graphSpacingBefore}
};

CTParagraphStyleRef style;
style = CTParagraphStyleCreate( altSettings, sizeof(altSettings) / sizeof(CTParagraphStyleSetting) );

if ( style == NULL )
{
NSLog(@"*** Unable To Create CTParagraphStyle in apply paragraph formatting" );
return;
}

[string addAttributes:[NSDictionary dictionaryWithObjectsAndKeys:(NSObject*)style,(NSString*) kCTParagraphStyleAttributeName, nil] range:NSMakeRange(0,[string length])];

CFRelease(tabStops);
CFRelease(style);

You need to include the CoreText framework and then import CoreText/CoreText.h

NSAttributedString bullet list issues

@the4kman, @Krunal, Thank you for your responses! The solution was even more simple. Replacing space symbol with \t in let formattedString = "\u{2022} \(string)\n gives you valid indentation.

For completeness, the full solution code is (just replacing one char):

func add(bulletList strings: [String],
font: UIFont,
indentation: CGFloat = 15,
lineSpacing: CGFloat = 3,
paragraphSpacing: CGFloat = 10,
textColor: UIColor = .black,
bulletColor: UIColor = .red) -> NSAttributedString {

func createParagraphAttirbute() -> NSParagraphStyle {
var paragraphStyle: NSMutableParagraphStyle
let nonOptions = NSDictionary() as! [NSTextTab.OptionKey: Any]

paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
paragraphStyle.tabStops = [
NSTextTab(textAlignment: .left, location: indentation, options: nonOptions)]
paragraphStyle.defaultTabInterval = indentation
paragraphStyle.firstLineHeadIndent = 0
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.paragraphSpacing = paragraphSpacing
paragraphStyle.headIndent = indentation
return paragraphStyle
}

let bulletPoint = "\u{2022}"
let textAttributes: [NSAttributedStringKey: Any] = [.font: font, .foregroundColor: textColor]
let bulletAttributes: [NSAttributedStringKey: Any] = [.font: font, .foregroundColor: bulletColor]
let buffer = NSMutableAttributedString.init()

for string in strings {
let formattedString = "\(bulletPoint)\t\(string)\n"
let attributedString = NSMutableAttributedString(string: formattedString)
let paragraphStyle = createParagraphAttirbute()

attributedString.addAttributes(
[NSAttributedStringKey.paragraphStyle : paragraphStyle],
range: NSMakeRange(0, attributedString.length))

attributedString.addAttributes(
textAttributes,
range: NSMakeRange(0, attributedString.length))

let string:NSString = NSString(string: formattedString)
let rangeForBullet:NSRange = string.range(of: bulletPoint)
attributedString.addAttributes(bulletAttributes, range: rangeForBullet)
buffer.append(attributedString)
}

return buffer
}

NSAttributedString and html styling (bullet alignment)

I had to add custom styles to a list and this is the paragraph style I ended up using for each bullet in an NSAttributedString

The headIndent and firstLineHeadIndent can be changed but the NSTextTab location should be the same as headIndent

NSMutableParagraphStyle *const bulletParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
bulletParagraphStyle.headIndent = 60;
bulletParagraphStyle.firstLineHeadIndent = 30;
NSTextTab *listTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentNatural location:60 options:@{}];
bulletParagraphStyle.tabStops = @[listTab];

This works assuming your bullet point has a \t after it (and then then text)

How to make a bullet list with Swift?

use 2 labels inside a view for the columns. both labels being multulined

class Helper {

static func bulletedList(strings:[String], textColor:UIColor, font:UIFont, bulletColor:UIColor, bulletSize:BulletSize) -> NSAttributedString {
let textAttributesDictionary = [NSFontAttributeName : font, NSForegroundColorAttributeName:textColor]

let bulletAttributesDictionary = [NSFontAttributeName : font.withSize(bulletSize.rawValue), NSForegroundColorAttributeName:bulletColor]
let fullAttributedString = NSMutableAttributedString.init()

for string: String in strings
{
let bulletPoint: String = "\u{2022}"
let formattedString: String = "\(bulletPoint) \(string)\n"
let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: formattedString)
let paragraphStyle = createParagraphAttribute()

attributedString.addAttributes([NSParagraphStyleAttributeName: paragraphStyle], range: NSMakeRange(0, attributedString.length))
attributedString.addAttributes(textAttributesDictionary, range: NSMakeRange(0, attributedString.length))

let string:NSString = NSString(string: formattedString)
let rangeForBullet:NSRange = string.range(of: bulletPoint)

attributedString.addAttributes(bulletAttributesDictionary, range: rangeForBullet)
fullAttributedString.append(attributedString)
}
return fullAttributedString
}

static func createParagraphAttribute() -> NSParagraphStyle {

var paragraphStyle: NSMutableParagraphStyle
paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: 15, options: NSDictionary() as! [String : AnyObject])]
paragraphStyle.defaultTabInterval = 15
paragraphStyle.firstLineHeadIndent = 0
paragraphStyle.lineSpacing = 3
paragraphStyle.headIndent = 10
return paragraphStyle
}
}

and simply use Helper.bulletedList to create your bulletted list as Attributed text for the label

Format UILabel with bullet points?

Perhaps use the Unicode code point for the bullet character in your string?

Objective-c

myLabel.text = @"\u2022 This is a list item!";

Swift 4

myLabel.text = "\u{2022} This is a list item!"

Unable to render an NSAttributedString as a 2 column tabbed bullet list in a PDF

It turns out that I was in the ballpark, but definitely not close.

The following provides the desired split column effect:

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.tabStops = [
// 274 would be the midpoint of my document
NSTextTab(textAlignment: .left, location: 274, options: [:])
]

let string = "\u{2022} This\t\u{2022} is\n\u{2022} getting\t\u{2022} really\n\u{2022} old"

let attributedString = NSAttributedString(
string: string,
attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle]
)

Sample Image

For bonus points, should you want to have multiple columns in your document, the following will accomplish this (pardon my crude formatting):

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.tabStops = [
NSTextTab(textAlignment: .left, location: 100, options: [:]),
NSTextTab(textAlignment: .left, location: 300, options: [:])
]

let string = "\u{2022} This\t\u{2022} is\t\u{2022} getting\n\u{2022} really\t\u{2022} old"

let attributedString = NSAttributedString(
string: string,
attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle]
)

And will look like this:

Sample Image

What is going on here?

So, what I learned here is that the tabStops tells iOS what location within the line to place the tab:

  1. The first tab will go to position 100
  2. The second tab will go to position 300
  3. A third tab will wrap around the document and go to position 100 as well

Regarding tabbing, if you assign a tab with location 0 in the first index, then tabbing to a newline will get it aligned with the left edge.

As to what fixed the issue for me. I was relying on an approach where each component of the string was added as it was encountered. However, this string would fail to format properly. Instead, by merging everything into a single string and applying the attributes seen in my working code, I was able to get it to align properly.

I also tested using the individual components as seen in my question, but with the paragraph style attributes applied as well, and that resulted in a working solution as well.

Based on this, it appears that my mistake was to mix strings that had, and did not have, the desired tabbing behavior.

iOS7 TextKit: bullet point alignment

Below it the code I use to set a bulleted paragraph. This comes straight out of a working app and is used to apply the style to the entire paragraph in response to a user clicking on a formatting button. I have tried to put in all the dependent methods but may have missed some.

Note that I am setting most indents in centimetres and hence the use of the conversion functions at the end of the listing.

I am also checking for the presence of a tab character (no tab key on iOS!) and automatically insert a dash and a tab.

If all you need is the paragraph style then look at the last few methods below where the firstLineIndent etc get set up.

Note that these calls all get wrapped in [textStorage beginEditing/endEditing]. Despite the (IBAction) below the method is not getting called by a UI object directly.

        - (IBAction) styleBullet1:(id)sender
{
NSRange charRange = [self rangeForUserParagraphAttributeChange];
NSTextStorage *myTextStorage = [self textStorage];

// Check for "-\t" at beginning of string and add if not found
NSAttributedString *attrString = [myTextStorage attributedSubstringFromRange:charRange];
NSString *string = [attrString string];

if ([string rangeOfString:@"\t"].location == NSNotFound) {
NSLog(@"string does not contain tab so insert one");
NSAttributedString * aStr = [[NSAttributedString alloc] initWithString:@"-\t"];
// Insert a bullet and tab
[[self textStorage] insertAttributedString:aStr atIndex:charRange.location];

} else {
NSLog(@"string contains tab");
}

if ([self isEditable] && charRange.location != NSNotFound)
{
[myTextStorage setAttributes:[self bullet1Style] range:charRange];
}
}

- (NSDictionary*)bullet1Style
{
return [self createStyle:[self getBullet1ParagraphStyle] font:[self normalFont] fontColor:[UIColor blackColor] underlineStyle:NSUnderlineStyleNone];

}

- (NSDictionary*)createStyle:(NSParagraphStyle*)paraStyle font:(UIFont*)font fontColor:(UIColor*)color underlineStyle:(int)underlineStyle
{
NSMutableDictionary *style = [[NSMutableDictionary alloc] init];
[style setValue:paraStyle forKey:NSParagraphStyleAttributeName];
[style setValue:font forKey:NSFontAttributeName];
[style setValue:color forKey:NSForegroundColorAttributeName];
[style setValue:[NSNumber numberWithInt: underlineStyle] forKey:NSUnderlineStyleAttributeName];

FLOG(@" font is %@", font);

return style;
}

- (NSParagraphStyle*)getBullet1ParagraphStyle
{
NSMutableParagraphStyle *para;
para = [self getDefaultParagraphStyle];
NSMutableArray *tabs = [[NSMutableArray alloc] init];
[tabs addObject:[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:[self ptsFromCMF:1.0] options:nil]];
//[tabs addObject:[[NSTextTab alloc] initWithType:NSLeftTabStopType location:[self ptsFromCMF:1.0]]];
[para setTabStops:tabs];
[para setDefaultTabInterval:[self ptsFromCMF:2.0]];
[para setFirstLineHeadIndent:[self ptsFromCMF:0.0]];
//[para setHeaderLevel:0];
[para setHeadIndent:[self ptsFromCMF:1.0]];
[para setParagraphSpacing:3];
[para setParagraphSpacingBefore:3];
return para;
}
- (NSMutableParagraphStyle*)getDefaultParagraphStyle
{
NSMutableParagraphStyle *para;
para = [[NSParagraphStyle defaultParagraphStyle]mutableCopy];
[para setTabStops:nil];
[para setAlignment:NSTextAlignmentLeft];
[para setBaseWritingDirection:NSWritingDirectionLeftToRight];
[para setDefaultTabInterval:[self ptsFromCMF:3.0]];
[para setFirstLineHeadIndent:0];
//[para setHeaderLevel:0];
[para setHeadIndent:0.0];
[para setHyphenationFactor:0.0];
[para setLineBreakMode:NSLineBreakByWordWrapping];
[para setLineHeightMultiple:1.0];
[para setLineSpacing:0.0];
[para setMaximumLineHeight:0];
[para setMinimumLineHeight:0];
[para setParagraphSpacing:6];
[para setParagraphSpacingBefore:3];
//[para setTabStops:<#(NSArray *)#>];
[para setTailIndent:0.0];
return para;
}
-(NSNumber*)ptsFromCMN:(float)cm
{
return [NSNumber numberWithFloat:[self ptsFromCMF:cm]];
}
-(float)ptsFromCMF:(float)cm
{
return cm * 28.3464567;
}

Remove bulletpoints from NSMutableAttributedString

In your current code, I have absolutely no idea of what is that supposed to do:

let bulletPoint: String = "\u{2022}"
let attributedString = NSMutableAttributedString(string: bulletPoint)
formattedString.enumerateAttributes(in: NSRange(0..<attributedString.length), options: []) { (attributes, range, _) -> Void in
for (attribute, _) in attributes {
formattedString.removeAttribute(attribute, range: range)
}
}

The fact that you use NSRange(0..<attributedString.length), should cause a crash if formattedString is shorter than attributedString. That's a bullet point, so you shouldn't have the issue, but still.
And the goal behind it escapes my reach.

The bullet or \u{2022} is created after the parsing, so you need to parse the formattedString.string, what you could do:

let regex = try! NSRegularExpression(pattern: "\\s\u{2022}\\s", options: [])
let matches = regex.matches(in: formattedString.string, options: [], range: NSMakeRange(0, formattedString.string.utf16.count))
matches.reversed().forEach { formattedString.replaceCharacters(in: $0.range, with: "")}

I didn't understood if you wanted to remove only the bullets or the rest of the text (was the others tries with .replacingOccurrences(of: "Click to open image!", with: "") just to see that you were indeed able to reach that line and remove the substrings or not.



Related Topics



Leave a reply



Submit