Back to rankings

krzyzanowskim/STTextView

Swiftswift.best

Performant and reusable text view component (TextKit 2), with line numbers and more. UITextView / NSTextView replacement.

textviewmacostextkitswiftappkitnstextviewcococaeditortextswiftuitextkit2sttextview
Star Growth
Stars
1.6k
Forks
109
Weekly Growth
Issues
0
5001k1.5k
Feb 2022Jul 2023Jan 2025Jul 2026
README

STTextView

Performant macOS and iOS TextView with line numbers and much more. (NSTextView/UITextView reimplementation)

The goal of this project is to build NSTextView/UITextView replacement reusable component utilizing TextKit 2 framework. due to many good reasons.

The component is mainly developed to serve Swift Studio needs as a source code editor.

Poster

TextKit 2 was announced during WWDC 2021 as a TextKit 1 replacement for text layout and whatnot. Apple announced that NSTextView, the view component specialized for text editing, will adopt TextKit 2 and provide support along TextKit 1 bit. As I started to learn more about NSTextView + TextKit2, I realized that as of today (Feb 2022), neither NSTextView nor TextKit 2 classes are fully functional. Along the way, I reported several bug reports to Apple requesting DTS (support tickets). Eventually, I got blocked by specific bugs that pushed me to start this project.

Platform Requirements

  • macOS: 14.0+
  • iOS: 16.0+
  • Mac Catalyst: 16.0+
  • Swift: 5.9+
  • Xcode: 26.0+

Features

  • macOS text system integration
  • Performant Text editing
  • Display line numbers
  • Display invisible characters
  • Customization of colors and fonts
  • Toggle line wrapping on and off
  • Adjust the height of lines
  • Highlight/Select ranges in the text view
  • Multi-cursor editing
  • Search/Replace the text
  • Spelling and Grammar
  • Dictation
  • Customizable Completion support
  • Smooth scrolling of long content
  • LTR (Left To Right) / RTL (Right To Left) layout
  • Undo/Redo
  • Plugins
  • Anchored annotations (via plugin)
  • Source Code syntax highlighting (via plugin)

Roadmap

Suggest or vote for new features: Feature Requests

Getting Started

STTextView is distributed using the Swift Package Manager. Install it in a project by adding it as a dependency in your Package.swift manifest or through “Package Dependencies” in Xcode project settings

let package = Package(
    dependencies: [
        .package(url: "https://github.com/krzyzanowskim/STTextView", from: "2.2.0")
    ]
)

Demo Application

The demo applications TextEdit and TextEdit.SwiftUI lets you explore the library.

Usage

Create a TextView

The STTextView is an NSView subclass and can be initialized like any other view. It has an API that is similar to that of NSTextView.

import STTextView

let textView = STTextView()
view.addSubview(textView)

(macOS) add to scroll view

let textView = STTextView()
let scrollView = NSScrollView()
scrollView.documentView = textView
let scrollView = STTextView.scrollableTextView()
let textView = scrollView.documentView as! STTextView

Customize

The text view can be customized in a variety of ways.

let paragraph = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
// Set the line-height to 120%
paragraph.lineHeightMultiple = 1.2
paragraph.defaultTabInterval = 28

// Default Paragraph style
textView.defaultParagraphStyle = paragraph

// Set default font
textView.font = NSFont.monospacedSystemFont(ofSize: 14, weight: .regular)

// Set default text color
textView.textColor = .textColor

// Set text value
textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare lobortis sem a vulputate."
textView.addAttributes([.foregroundColor: NSColor.red], range: NSRange(location: 10, length: 5))

// Wrap/No wrap lines to editor width
textView.isHorizontallyResizable = true

// Highlight the selected line.
textView.highlightSelectedLine = true

Add gutter with line numbers

textView.showsLineNumbers = true
textView.gutterView?.drawSeparator = true

(macOS) Enable an optional search-and-replace find interface inside a view, usually a scroll view.

textView.isIncrementalSearchingEnabled = true
textView.textFinder.incrementalSearchingShouldDimContentView = true

SwiftUI

import STTextViewSwiftUI

struct ContentView: View {
    @State private var text = AttributedString("Hello World!")

    var body: some View {
        TextView(
            text: $text,
            options: [.wrapLines, .highlightSelectedLine, .showLineNumbers]
        )
        .textViewFont(.monospacedSystemFont(ofSize: 14, weight: .regular))
    }
}

Plugins

Plugins in an STTextView component offer additional functionalities and customizations beyond the simple text display. They enhance the core capabilities of the text view by adding features such as syntax highlighting, word count tracking, and more. These plugins expand the STTextView's utility while maintaining a modular and adaptable software structure.

Plugin Development

To create a custom plugin:

  1. Implement the STPlugin protocol:
class MyPlugin: STPlugin {
    func setUp(context: STPluginContext) {
        // Initialize your plugin
    }
    
    func tearDown() {
        // Clean up resources
    }
}
  1. Use STPluginContext for host communication:

    • Access the text view and its properties
    • Subscribe to text changes and events
    • Modify text attributes and selections
  2. Handle events via STPluginEvents:

    • Text changes
    • Selection changes
    • Layout updates
    • View lifecycle events
  3. Add the plugin to your text view:

let plugin = MyPlugin()
textView.addPlugin(plugin)

For a complete example, see the Plugin-Template repository.

Architecture Overview

STTextView uses a modular architecture with platform-specific implementations:

STTextView (umbrella target)
├── STTextViewCommon (shared code)
├── STTextViewAppKit (macOS implementation)
├── STTextViewUIKit (iOS/Catalyst implementation)
├── STTextViewSwiftUI (SwiftUI wrappers)
└── STObjCLandShim (Objective-C bridging)

Core Components

  • STTextView: Main view that coordinates all components
  • STTextContainerView: Renders text fragments and insertion point
  • STSelectionView: Handles selection overlays
  • STGutterView: Optional line numbers and markers
  • STLineHighlightView: Current line highlighting

TextKit 2 Integration

Text layout is managed through custom TextKit 2 components:

  • STTextLayoutManager: Custom NSTextLayoutManager subclass
  • STTextContentStorage: NSTextContentStorage subclass with performance optimizations
  • STTextLayoutFragment: Custom fragment rendering

🐛 TextKit 2 Bug Reports List

List of issues and bugs related to TextKit, NSTextView, AppKit, UIKit and related frameworks framework I reported to Apple so far:

  • FB9856587: TextKit2 unexpected additional line fragment for the last line
  • FB9925766: NSTextSelectionNavigation.deletionRanges only works at the end of the word
  • FB9925647: NSTextLayoutManager.replaceContents(in range: with attributedString:) is documented but is not part of the public API
  • FB9907261: NSTextElementProvider.replaceContents(in:with:) does not replace content as documented
  • FB9692714: Rendering attributes do not draw properly
  • FB9886911: NSTextView can't properly layout and display long lines (this one is nasty since it causes the view to "jump" whenever text attribute updates)
  • FB9713415: NSTextView drawInsertionPoint(in:color:turnedOn) is never called
  • FB9971054: NSLayoutManager.enumerateCaretOffsetsInLineFragment ignores starting location
  • FB9971054: NSTextView assert on selection when setup with TextKit2
  • FB9743449, FB10019859: NSTextContentStorage.textElements(for:) returns no element, while enumerateTextElements does return elements
  • FB11898356: textSelections(interactingAt:inContainerAt:anchors:modifiers:selecting:bounds:) produces wrong selections for certain locations
  • FB12726775: Documentation to the NSTextParagraph.paragraphContentRange is incorrect
  • FB13272586: NSTextContainer.size default value is not as documented
  • FB13290979: NSTextContainer.lineFragmentPadding does not affect end of the fragment usageBoundsForTextContainer rectangle
  • FB13291926: NSTextLayoutManager.usageBoundsForTextContainer observer is never trigerred (Fixed again in macOS 15.6)
  • FB13789916: NSTextInputClient.setMarkedText provide bogus selection range for Chinese keyboard
  • FB14700414: NSTextList doesn't work since macOS 14 (regression)
  • FB15131180: TextKit extra line frame is incorrect and does not respect layout fragment size (regression)
  • FB17020435: enumerateCaretOffsetsInLineFragmentAtLocation:usingBlock: documentation is not accurate
  • FB19698121: TextKit 2 undocumented and unexpected behavior in textViewportLayoutControllerDidLayout
  • FB21059465: NSScrollView horizontal floating subview does not respect insets
  • FB22523964: UITextView does not apply contentScaleFactor to fragment view resulting in blurry drawing
  • FB22524198: NSTextLayoutFragment.extraLineFragmentAttributes is private API that makes impossible calculate extra line fragment correctly

... I'm aware that the list of issues is not complete. I managed to workaround most of the problems in STTextView.

Why ST?

(ST prefix stands for "Swift sTudio" because SS is not good prefix since 1939)

Suggestions or Feedback

Start a new discussion topic or a pull request.

I'd love to hear from you! Get in touch via X/Twitter @krzyzanowskim, Mastodon @krzyzanowskim@mastodon.social.

License

By using the Software, you accept the terms of the License. The STTextView software is copyrighted by Marcin Krzyżanowski. If use of the software under the GPL v3.0 does not satisfy your organization’s legal department, commercial licenses are available. Feel free to contact for more details.

Related repositories
SwiftUIX/SwiftUIX

An exhaustive expansion of the standard SwiftUI library.

SwiftMIT Licenseswift-package-managertextview
8.1k496
hanks-zyh/HTextView

Animation effects to text, not really textview

JavaMavenApache License 2.0androidanimation-effects
5.6k817
SufficientlySecure/html-textview

TextView to display simple HTML

JavaMavenApache License 2.0androidandroid-library
2.6k515
HeZaiJin/SlantedTextView

Android slanted TextView.

JavaMavenandroidtextview
github.com/HeZaiJin/SlantedTextView
2.3k328
RuffianZhong/RWidgetHelper

Android UI 快速开发,专治原生控件各种不服

JavaMavenstateselector
2k181
eggswift/pull-to-refresh

#Busy Re-Building....# An easy way to use pull to refresh and infinite scrolling in Swift. Pod 'ESPullToRefresh'

SwiftMIT Licenserefresherpull-to-refresh
1.8k255
FluidGroup/NextGrowingTextView

📝 The next in the generations of 'growing textviews' optimized for iOS 8 and above.

SwiftMIT Licenseioscarthage
1.8k148
sendtion/XRichText

一个Android富文本类库,支持图文混排,支持编辑和预览,支持插入和删除图片。

JavaMavenrichtextxrichtext
1.8k264
rosenpin/fading-text-view

A TextView that changes its content automatically every few seconds

Kotlintextviewanimation
1.7k218
bravoborja/ReadMoreTextView

A Custom TextView with trim text

JavaMavenApache License 2.0javareadmoretextview
1.7k228
klinker24/Android-TextView-LinkBuilder

Insanely easy way to define clickable links within a TextView.

KotlinMIT Licenseclickable-linkstextview
1.6k197
mdgspace/RotatingText

A periodic text updating library

JavaMavenMIT Licenseandroidtextview
1.6k167