Bullet Point List – UIKit (Archive)

I recently wanted to make a bullet point list in an app so thought I would share how I did it.

Firstly just set up a new Swift single view project with just a UILabel at the top of the view. Use Autolayout to pin the label view to the left, top and right sides of the view. Also set your label to have lines = 0 so we can have a multiline label that grows with content. Then just create an IBOutlet for the label in your ViewController file. Below is my setup.

When I first tried this I thought it would be easy, create an empty string, iterate through an array of strings prepend a bullet point, appending a new line and then append the strings to the empty string. Pass it to the label\’s text property and done. So something like this:

import UIKit

class ViewController: UIViewController 
{
    var strings:[String] = []
    @IBOutlet weak var bulletLabel: UILabel!
    
    override func viewDidLoad() 
    {
        super.viewDidLoad()
        
        let bullet1 = "This is a small string"
        let bullet2 = "This is more of medium string with a few more words etc."
        let bullet3 = "Well this is certainly a longer string, with many more words than either of the previuos two strings"
        
        strings = [bullet1, bullet2, bullet3]
        
        var fullString = ""
        
        for string: String in strings 
        {
            let bulletPoint: String = "\\u{2022}"
            let formattedString: String = "\\(bulletPoint) \\(string)\\n"
            
            fullString = fullString + formattedString
        }
        
        bulletLabel.text = fullString
    }
}

Unfortunately this gave me the following output:

We have the bullet points and new lines for each new bullet entry but I think bullet points should have any subsequent lines inline with the text not the bullets. At this point I did a bit of googling and found NSParagraphStyle, which lets you define how a paragraph should be formatted and then you can pass it in as an attribute to an NSAttributedString. So lets try that again, notice that I now use attributed strings and the label\’s AttributedString property rather than the text property.

class ViewController: UIViewController
{
    var strings:[String] = []
    @IBOutlet weak var bulletLabel: UILabel!
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        let bullet1 = "This is a small string"
        let bullet2 = "This is more of medium string with a few more words etc."
        let bullet3 = "Well this is certainly a longer string, with many more words than either of the previuos two strings"
        
        strings = [bullet1, bullet2, bullet3]
        
        let attributesDictionary = [NSFontAttributeName : bulletLabel.font]
        let fullAttributedString = NSMutableAttributedString(string: "", attributes: attributesDictionary)
        
        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))
            
            fullAttributedString.appendAttributedString(attributedString)
        }
        
        bulletLabel.attributedText = fullAttributedString
    }
    
    func createParagraphAttribute() ->NSParagraphStyle
    {
        var paragraphStyle: NSMutableParagraphStyle
        paragraphStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle
        paragraphStyle.tabStops = [NSTextTab(textAlignment: .Left, location: 15, options: NSDictionary() as! [String : AnyObject])]
        paragraphStyle.defaultTabInterval = 15
        paragraphStyle.firstLineHeadIndent = 0
        paragraphStyle.headIndent = 15
        
        return paragraphStyle
    }
}

So how did that work out?

Excellent, everything is formatted correctly. Example Gist here!

note on why NSTextTab needs an empty dictionary for options param.