Skip to content

A simpler, more powerful HighlightedTextEditor

Compare
Choose a tag to compare
@kyle-n kyle-n released this 27 May 17:25
· 8 commits to main since this release
e7f0db3

HighlightedTextEditor is turning 2.0! This is a huge update that gives you even more flexibility and control while dramatically simplifying the package’s code.

Quick overview

New features

  • .introspect() modifier allows access to the underlying UITextView or NSTextView
  • NSRegularExpression.all is pre-made regex for selecting an entire string
  • Nests some internal HLTE classes inside HighlightedTextEditor so they're not polluting your project namespace

Breaking changes

  • Removes modifiers now covered by .introspect() (full list below)
  • .onCommit(), .onEditingChanged(), and .onTextChange() are now modifiers, not init() arguments (just like TextEditor!)
  • Markdown presets are only accessible from [HighlightRule]. For example, [HighlightRule].markdown

There are a lot of big changes in here, so here’s why I made them.

Problem: HLTE 1.0 was slowly replicating the API for UITextView and NSTextView

Under the hood, HLTE uses a UITextView or an NSTextView inside an NSScrollView. I threw a couple modifiers into v1 to set some options on the underlying UIKit/AppKit editor. For example:

HighlightedTextEditor(text: $text, highlightRules: [])
    .multilineTextAlignment(.center)

The problem was, those options weren’t enough. A lot of users needed new HLTE modifiers to set special properties on the underlying UIKit/AppKit editor, modifiers I hadn’t thought to make.

That’s not sustainable. That means HLTE was (slowly, poorly) replicating the API for UITextView and NSTextView.

Solution: Give users the UITextView or NSTextView

My solution was inspired by SwiftUI-Introspect, a cool library that lets you "introspect" the underlying UIKit or AppKit elements. It's a great way to write SwiftUI and dip into UIKit or AppKit to customize just one thing.

HighlightedTextEditor 2.0 removes all the previous modifiers that set UIKit or AppKit properties. Now there is only one god, .introspect():

HighlightedTextEditor(text: $text, highlightRules: .markdown)
	.introspect { internals in
		internals.textView.backgroundColor = .red
	}

internals is a struct containing two properties, textView and scrollView. The latter is only used in the AppKit editor and returns nil in UIKit editors.

public struct Internals {
	public let textView: SystemTextView
	public let scrollView: SystemScrollView? // always nil in UIKit
}

#if os(macOS)
import AppKit

public typealias SystemTextView = NSTextView
public typealias SystemScrollView = NSScrollView

#else
import UIKit

public typealias SystemTextView = UITextView
public typealias SystemScrollView = UIScrollView

#endif

.introspect() runs every time SwiftUI redraws the HLTE view.

Now you have access to every property in AppKit and UIKit. Customize away!

Removed modifiers

The following modifiers have been removed. If you were using them, simply set the equivalent UIKit / AppKit property using .introspect().

  • .allowsDocumentBackgroundColorChange(_ allowsChange: Bool)
  • .autocapitalizationType(_ type: UITextAutocapitalizationType)
  • .autocorrectionType(_ type: UITextAutocorrectionType)
  • .backgroundColor(_ color: UIColor)
  • .defaultColor(_ color: UIColor)
  • .defaultFont(_ font: UIFont)
  • .drawsBackground(_ shouldDraw: Bool)
  • .keyboardType(_ type: UIKeyboardType)
  • .insertionPointColor(_ color: UIColor)
  • .multilineTextAlignment(_ alignment: TextAlignment)

Behind the scenes

  • Deletes a lot of test code covering the old modifiers
  • UIKit tests run on iPhone 12 simulators, up from 11
  • Renames source code files to reflect that UIKit runs on the Mac
  • Automatic formating and linting thanks to SwiftFormat and SwiftLint