Skip to content

Repository files navigation

Hero

Litext

A lightweight, high-performance rich-text library for all Apple platforms β€” UIKit, AppKit, and SwiftUI (including watchOS).

Note: This fork is reimplemented in Swift 6.0 with strict concurrency. Version 2.0 uses renamed public APIs such as TextLabelView, TextLabel, and TextLabel.Attachment.

Features

  • ⚑️ High performance text layout and rendering via CoreText
  • πŸ“Ž Native view embedding via attachments
  • πŸ”— Clickable links support
  • ✏️ Text selection with copy/paste
  • πŸ”— One selection across several labels, such as the cells of a table
  • 🎨 Custom per-line drawing callbacks
  • πŸ“ Auto layout integration (experimental)
  • πŸ–₯️ SwiftUI support on all platforms, including watchOS

Screenshot

Supported Platforms

Litext supports iOS 15, macOS 12, Mac Catalyst 15, tvOS 15, visionOS 1 and watchOS 8, the oldest systems current toolchains deploy to. It requires Swift 6.2 (Xcode 26) or later.

Platform Minimum Version TextLabelView (UIKit/AppKit) TextLabel (SwiftUI)
iOS 15.0+ βœ… βœ…
macOS 12.0+ βœ… βœ…
tvOS 15.0+ βœ… βœ…
visionOS 1.0+ βœ… βœ…
Mac Catalyst 15.0+ βœ… βœ…
watchOS 8.0+ β€” βœ…

Installation

Add Litext as a dependency in your Package.swift file:

dependencies: [
    .package(url: "https://github.com/Helixform/Litext.git", branch: "main")
]

Or in Xcode: File β†’ Add Package Dependencies and enter the repository URL.

Usage

UIKit / AppKit

import Litext

let label = TextLabelView()
view.addSubview(label)

let attributedString = NSMutableAttributedString(
    string: "Hello, Litext!",
    attributes: [
        .font: PlatformFont.systemFont(ofSize: 16),
        .foregroundColor: PlatformColor.label
    ]
)
label.attributedText = attributedString

SwiftUI

TextLabel works on all platforms, including watchOS:

import Litext
import SwiftUI

struct ContentView: View {
    var body: some View {
        TextLabel("Hello, Litext!")
            .selectable()
            .onTapLink { url in
                UIApplication.shared.open(url)
            }
    }
}

You can also initialise with an NSAttributedString or AttributedString:

TextLabel(attributedString: myNSAttributedString)
TextLabel(attributedString: myAttributedString)

Link Handling

let mutable = NSMutableAttributedString(string: "Visit GitHub")
mutable.addAttribute(.link, value: URL(string: "https://github.com")!, range: NSRange(location: 6, length: 6))
label.attributedText = mutable

// UIKit/AppKit β€” implement TextLabelViewDelegate
label.delegate = self

func textLabelView(
    _ textLabelView: TextLabelView,
    didTapHighlightRegion region: TextLabel.HighlightRegion,
    at location: CGPoint
) {
    if let url = region.linkURL {
        UIApplication.shared.open(url)
    }
}

// SwiftUI β€” use the modifier
TextLabel(attributedString: mutable)
    .onTapLink { url in
        UIApplication.shared.open(url)
    }

Text Selection

// Enable selection
label.isSelectable = true
label.selectionBackgroundColor = UIColor.systemBlue.withAlphaComponent(0.2)

// Access selected text
let text = label.selectedPlainText()
let attributed = label.selectedAttributedText()

// Programmatic selection
label.selectionRange = NSRange(location: 0, length: 5)
label.selectAll()
label.clearSelection()

// SwiftUI
TextLabel("Some selectable text")
    .selectable()

Selection Across Labels

Labels that share a TextSelectionGroup share one selection, so a selection can run from one label into the next, for example across the cells of a table. Copy, Look Up, Translate and Share act on all of it.

let group = TextSelectionGroup()
// The order is the reading order: list a table row by row.
group.labels = cells
// Put a tab between cells of a row and a line break between rows.
group.separator = { previous, next in row(of: previous) == row(of: next) ? "\t" : "\n" }
group.delegate = self

// TextSelectionGroupDelegate
func textSelectionGroupDidChangeSelection(_ group: TextSelectionGroup) {
    print(group.selectedPlainText() ?? "")
}

// Add commands to the menu (iOS 16, Mac Catalyst 16 or later).
func textSelectionGroup(
    _ group: TextSelectionGroup,
    editMenuForSuggestedActions suggestedActions: [UIMenuElement],
) -> UIMenu? {
    UIMenu(children: suggestedActions + [copyAsMarkdownAction])
}

The group holds its labels weakly; keep the labels alive as usual. group.selectedSegments lists the selected range in each label.

Embedding Native Views (Attachments)

Use TextLabel.Attachment to embed any view inline in the text:

// UIKit / AppKit
let attachment = TextLabel.Attachment()
attachment.view = myCustomView          // UIView or NSView
attachment.size = myCustomView.intrinsicContentSize

// watchOS (SwiftUI view instead)
let attachment = TextLabel.Attachment()
attachment.swiftUIView = AnyView(MyCustomView())
attachment.size = CGSize(width: 100, height: 50)

// Insert attachment into attributed string
let attachmentString = attachment.attributedString()

Custom Per-Line Drawing

let drawingAction = TextLabel.LineDrawingAction { context, line, origin in
    // Custom drawing for each line
    context.setStrokeColor(UIColor.red.cgColor)
    context.move(to: CGPoint(x: origin.x, y: origin.y - 2))
    context.addLine(to: CGPoint(x: origin.x + 100, y: origin.y - 2))
    context.strokePath()
}

attributedString.addAttribute(
    .litextLineDrawingAction,
    value: drawingAction,
    range: fullRange
)

watchOS

On watchOS, TextLabelView (the UIView/NSView subclass) is not available. Use TextLabel instead β€” it renders via an off-screen CGContext and displays the result as a SwiftUI Image.

import Litext
import SwiftUI

struct WatchContentView: View {
    var body: some View {
        TextLabel(attributedString: styledText)
    }

    var styledText: NSAttributedString {
        let s = NSMutableAttributedString(string: "Hello from Watch!")
        s.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: 14), range: NSRange(location: 0, length: 17))
        return s
    }
}

For inline attachments on watchOS, provide a SwiftUI view via swiftUIView instead of view.

License

This project is licensed under the MIT License β€” see the LICENSE file for details.

Fable 5 Verified

About

A tiny rich-text supporting library for Apple platform.

Resources

Stars

38 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages