Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing numbered lists in UITextView using Text Kit 2 API

I am trying to implement a nested numbered list in my text editor app like apple's notes app does. I was able to achieve some of it using NSTextList in UITextView. I want to do nested numbered list but there seem to be no way available. If I add indent to NSParagraphStyle which has NSTextList, it does not work, Attributed String does not reflect indent for some reason.

I came across NSTextListElement watching WWDC 22 videos but there is no documentation or sample code which explains how to implement it.

Can you tell me if it is possible to do nested lists using Text Kit 2 in UITextView (UIKit)? If Yes, What is the correct way to implement it?

class ViewController: UIViewController {

var textView: UITextView!

override func viewDidLoad() {
super.viewDidLoad()

textView = UITextView(usingTextLayoutManager: true)
textView.alwaysBounceVertical = true
textView.keyboardDismissMode = .interactive
textView.layer.borderColor = UIColor.black.cgColor
textView.layer.borderWidth = 2

view.addSubview(textView)

textView.translatesAutoresizingMaskIntoConstraints = false
[ textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
textView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
textView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
].forEach {
$0.isActive = true
}

self.loadText()
}

func loadText() {
let list = NSTextList(markerFormat: .decimal, options: 0)
list.startingItemNumber = 1

let paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
paragraphStyle.textLists = [list]

paragraphStyle.firstLineHeadIndent = 40
paragraphStyle.headIndent = 40

let attributes = [NSAttributedString.Key.paragraphStyle : paragraphStyle]

let nsmutableAttributedString = NSMutableAttributedString(string: "Hello World!", attributes: attributes)

textView.textStorage.setAttributedString(nsmutableAttributedString)

}
}

like image 857
FE_Tech Avatar asked Oct 20 '25 06:10

FE_Tech


1 Answers

Nested lists are accomplished by including multiple NSTextLists in your NSParagraphStyle. Each textList in the list of textLists is a level of the nested list hierarchy, with the first list acting as the outermost and last list being the innermost. Sample code in SwiftUI using the RichTextKit library:

import Foundation
import SwiftUI
import RichTextEditor

struct ListTestView: View {
    @State
    var attrStr: NSAttributedString
    var richTextContext = RichTextContext()

    init() {
        let listLevel1 = NSTextList(markerFormat: .decimal, options: 0)
        let listLevel1Style = NSMutableParagraphStyle()
        listLevel1Style.textLists = [
            listLevel1
        ]
        let listLevel2Style = NSMutableParagraphStyle()
        listLevel2Style.textLists = [
            listLevel1,
            NSTextList(markerFormat: .lowercaseAlpha, options: 0)
        ]
        
        let firstItems = NSMutableAttributedString(string: "item 1 at level 1\nitem 2 at level 1\n", attributes: [
                .paragraphStyle: listLevel1Style
        ])
        let subItem = NSMutableAttributedString(string: "item at level 2\n", attributes: [
            .paragraphStyle: listLevel2Style
        ])
        let lastItem = NSMutableAttributedString(string: "item at level 1 again", attributes: [
            .paragraphStyle: listLevel1Style
        ])
        
        firstItems.append(subItem)
        firstItems.append(lastItem)
        attrStr = firstItems
    }

    var body: some View {
        return VStack(alignment: .leading) {
            RichTextEditor(text: $attrStr, context: richTextContext)
        }
    }
}

struct Preview_ListTestView: PreviewProvider {
    static var previews: some View {
        ListTestView()
    }
}

And the output:

Nested list example screenshot

like image 143
Egg Avatar answered Oct 21 '25 19:10

Egg