HighlightedTextEditor

repository·main·Indexed 20 days ago

https://github.com/kyle-n/highlightedtexteditor

A SwiftUI-based text editor for iOS 13.0+ and macOS 10.15+ that provides live syntax highlighting using regular expressions. It allows developers to apply complex text styles to specific patterns via HighlightRule and TextFormattingRule, and includes built-in presets for Markdown and URLs. The library provides modifiers for text and selection changes, as well as an .introspect modifier to access the underlying UITextView or NSTextView.

Tokens
1.8K
Snippets
5
Records
8
Agent score
23%

What's inside HighlightedTextEditor

  1. Install HighlightedTextEditor via Swift Package Manager or CocoaPods

    main

    HighlightedTextEditor supports iOS 13.0+ and macOS 10.15+.

    Swift Package Manager

    Use the following URL to add the package dependency: https://github.com/kyle-n/HighlightedTextEditor

    CocoaPods

    Add the following line to your Podfile and run pod install:

    pod 'HighlightedTextEditor'
    pod 'HighlightedTextEditor'
  2. Use built-in syntax presets

    main

    The library provides several built-in presets for common syntax highlighting tasks. These are available as static properties on [HighlightRule].

    Available presets:

    • .markdown
    • .url

    Example usage:

    HighlightedTextEditor(text: $text, highlightRules: .markdown)
  3. Use Regex Presets for full-string matching

    main

    You can use NSRegularExpression.all to easily select and apply formatting to the entire string content.

    HighlightedTextEditor(text: $text, highlightRules: [
        HighlightRule(pattern: .all, formattingRule: TextFormattingRule(key: .underlineStyle, value: NSUnderlineStyle.single.rawValue))
    ])
  4. Basic usage of HighlightedTextEditor

    main

    To use HighlightedTextEditor, provide a Binding<String> for the text content and an array of HighlightRule objects to define how text should be styled based on regex patterns.

    Performance Tip: Always instantiate NSRegularExpression objects once (e.g., as a constant or static property) rather than recreating them inside a view's body to avoid performance degradation during redraws.

    import HighlightedTextEditor
    
    // matches text between underscores
    let betweenUnderscores = try! NSRegularExpression(pattern: "_[^_]+_", options: [])
    
    struct ContentView: View {
        @State private var text: String = ""
        
        private let rules: [HighlightRule] = [
            HighlightRule(pattern: betweenUnderscores, formattingRules: [
                TextFormattingRule(fontTraits: [.traitItalic, .traitBold]),
                TextFormattingRule(key: .foregroundColor, value: UIColor.red),
                TextFormattingRule(key: .underlineStyle) { content, range in
                    if content.count > 10 { return NSUnderlineStyle.double.rawValue }
                    else { return NSUnderlineStyle.single.rawValue }
                }
            ])
        ]
        
        var body: some View {
            VStack {
                HighlightedTextEditor(text: $text, highlightRules: rules)
                    .onCommit { print("commited") }
                    .onEditingChanged { print("editing changed") }
                    .onTextChange { print("latest text value", $0) }
                    .onSelectionChange { (range: NSRange) in
                        print(range)
                    }
                    .introspect { editor in
                        // access underlying UITextView or NSTextView
                        editor.textView.backgroundColor = .green
                    }
            }
        }
    }
  5. Configure HighlightedTextEditor modifiers

    main

    The HighlightedTextEditor view supports several modifiers for handling user interaction and accessing underlying components:

    • .onCommit(_ callback: @escaping () -> Void): Triggered when the user stops editing.
    • .onEditingChanged(_ callback: @escaping () -> Void): Triggered when the user begins or ends editing.
    • .onTextChange(_ callback: @escaping (_ editorContent: String) -> Void): Triggered whenever the text content changes.
    • .onSelectionChange(_ callback: @escaping (_ selectedRange: NSRange) -> Void): Triggered when the selection changes (NSRange).
    • .onSelectionChange(_ callback: @escaping (_ selectedRanges: [NSRange]) -> Void): (AppKit only) Triggered when multiple selection ranges change.
    • .introspect(callback: (_ editor: HighlightedTextEditorInternals) -> Void): Provides access to the underlying UITextView (iOS) or NSTextView (macOS) via the HighlightedTextEditorInternals object.
  6. Access underlying text views via .introspect()

    main

    The .introspect modifier passes a HighlightedTextEditorInternals object to your callback, allowing you to customize the underlying UIKit or AppKit components.

    HighlightedTextEditorInternals Properties:

    • textView: The underlying UITextView (UIKit) or NSTextView (AppKit).
    • scrollView: The NSScrollView wrapper (AppKit only; returns nil in UIKit).
    .introspect { editor in
        // access underlying UITextView or NSTextView
        editor.textView.backgroundColor = .green
    }
  7. Define HighlightRule

    main

    A HighlightRule maps a regex pattern to specific text formatting styles.

    Parameters:

    • pattern: An NSRegularExpression defining the content to highlight. (Instantiate once for performance).
    • formattingRule: A single TextFormattingRule to apply to all text matching the pattern.
    • formattingRules: An array of [TextFormattingRule] to apply multiple styles to the matched text.
  8. Configure TextFormattingRule

    main

    A TextFormattingRule defines how text matching a pattern should be styled using NSAttributedString.Keys. There are three ways to initialize a rule:

    1. Static Value: Set a specific style for a key.

      • key: NSAttributedString.Key (e.g., .foregroundColor, .underlineStyle).
      • value: The style value (e.g., UIColor.red, NSUnderlineStyle.single.rawValue).
    2. Dynamic Value (Callback): Calculate the style based on the matched content.

      • key: NSAttributedString.Key.
      • calculateValue: A closure (String, Range<String.Index>) -> Any where the first parameter is the matched text and the second is the match's range in the overall string.
    3. Font Traits: Set symbolic font traits.

      • fontTraits: UIFontDescriptor.SymbolicTraits (iOS) or NSFontDescriptor.SymbolicTraits (macOS) (e.g., [.traitBold]).
    4. SwiftUI Color (iOS 14+ / macOS 11+):

      • foregroundColor: A SwiftUI Color.
      • fontTraits: Symbolic font traits. Note: If using Xcode beta, you may need to add the -DBETA flag to your Build Settings to enable this initializer.