How to Make a Bullet List with Swift

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

How to add numbered/bulleted list in SwiftUI?

You can observe changes to the TextEditor's text with onChange. Then, by doing [text] newText, you can capture both the old and new value.

  • text is the previous text
  • newText is the current text

If you compare these, you can ensure that bullet points are only added when the user is typing, and not when they're deleting.

Note that my implementation doesn't handle pasting large ranges of text yet.

struct ContentView: View {
@State var text = "\u{2022} "

var body: some View {
TextEditor(text: $text)
.frame(height: 400)
.border(Color.black)
.onChange(of: text) { [text] newText in
if newText.suffix(1) == "\n" && newText > text {
self.text.append("\u{2022} ")
}
}
}
}

When entering new lines, a bullet point is inserted at the start of the line

How do I add an unordered list or a bullet point every new line in a UITextView

The problem is that you're using

if ([myTextField.text isEqualToString:@"\n"]) {

as your conditional, so the block executes if the entirety of your myTextField.text equals "\n". But the entirety of your myTextField.text only equals "\n" if you haven't entered anything but "\n". That's why right now, this code is only working "the first time the user presses enter"; and when you say "I can't even backspace it," the problem is actually that the bullet point's being re-added with the call to textViewDidChange: since the same conditional is still being met.

Instead of using textViewDidChange: I recommend using shouldChangeTextInRange: in this case so you can know what that replacement text is no matter it's position within the UITextView text string. By using this method, you can automatically insert the bullet point even when the newline is entered in the middle of the block of text... For example, if the user decides to enter a bunch of info, then jump back up a few lines to enter some more info, then tries to press newline in between, the following should still work. Here's what I recommend:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {

// If the replacement text is "\n" and the
// text view is the one you want bullet points
// for
if ([text isEqualToString:@"\n"]) {

// If the replacement text is being added to the end of the
// text view, i.e. the new index is the length of the old
// text view's text...
if (range.location == textView.text.length) {
// Simply add the newline and bullet point to the end
NSString *updatedText = [textView.text stringByAppendingString:@"\n\u2022 "];
[textView setText:updatedText];
}

// Else if the replacement text is being added in the middle of
// the text view's text...
else {

// Get the replacement range of the UITextView
UITextPosition *beginning = textView.beginningOfDocument;
UITextPosition *start = [textView positionFromPosition:beginning offset:range.location];
UITextPosition *end = [textView positionFromPosition:start offset:range.length];
UITextRange *textRange = [textView textRangeFromPosition:start toPosition:end];

// Insert that newline character *and* a bullet point
// at the point at which the user inputted just the
// newline character
[textView replaceRange:textRange withText:@"\n\u2022 "];

// Update the cursor position accordingly
NSRange cursor = NSMakeRange(range.location + @"\n\u2022 ".length, 0);
textView.selectedRange = cursor;

}
// Then return "NO, don't change the characters in range" since
// you've just done the work already
return NO;
}

// Else return yes
return YES;
}

show bullets in label on tableview cell swift ios

Good case for stack views...

  • Vertical UIStackView
  • Each "row" is a Horizontal UIStackView with Distribution: Fill Equally

Looks like this - showing Bounds Rectangles:

Sample Image

without the Bounds Rectangles:

Sample Image

view hierarchy:

Sample Image


Edit

A second approach (which you may find easier)...

Use a single horizontal stack view with a single label on each "side" and use .attributedText.

Set the .attributedText of each label to a formatted, bulleted attributed string.

The cell prototype could look like this:

Sample Image

Then, use this code (slightly modified from https://stackoverflow.com/a/46889728/6257435) to generate a formatted, bulleted attributed string:

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: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: textColor]
let bulletAttributes: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: bulletColor]
let buffer = NSMutableAttributedString.init()

for string in strings {
var formattedString = "\(bulletPoint)\t\(string)"

// don't add newLine if it's the last bullet
if string != strings.last {
formattedString += "\n"
}

let attributedString = NSMutableAttributedString(string: formattedString)
let paragraphStyle = createParagraphAttirbute()

attributedString.addAttributes(
[NSAttributedString.Key.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
}

A quick implementation of that using your image as sample data results in this:

Sample Image

iphone bullet point list

The "bullet" character is at Unicode code point U+2022. You can use it in a string with @"\u2022" or [NSString stringWithFormat:@"%C", 0x2022].

The "line feed" character is at Unicode code point U+000A, and is used as UIKit's newline character. You can use it in a string with @"\n".

For example, if you had an array of strings, you could make a bulleted list with something like this:

NSArray * items = ...;
NSMutableString * bulletList = [NSMutableString stringWithCapacity:items.count*30];
for (NSString * s in items)
{
[bulletList appendFormat:@"\u2022 %@\n", s];
}
textView.text = bulletList;

It won't indent lines like a "proper" word processor. "Bad things" will happen if your list items include newline characters (but what did you expect?).

(Apple doesn't guarantee that "\uXXXX" escapes work in NSString literals, but in practice they do if you use Apple's compiler.)



Related Topics



Leave a reply



Submit