Keeping parts of the text unchanged by Writing Tools
Learn how to define parts of the text in which Writing Tools should not apply changes to.
When it comes to supporting Writing Tools for a UITextView
, we can exclude specific parts of the text editor's textual content, which may contain content that should not be modified by AI features such as quotes, templates, code, or legal text.
To exclude specific chunk of text from being modified by Writing Tools, a new method named textView(_:writingToolsIgnoredRangesInEnclosingRange:)
needs to be defined within the Coordinator
class.
func textView(_ textView: UITextView, writingToolsIgnoredRangesInEnclosingRange enclosingRange: NSRange) -> [NSValue] {
// Convert the text of the UITextView to NSString for range operations
let fullText = textView.text as NSString
// Define the specific text to ignore by writing tools
let searchString = "Here's to the crazy ones. The misfits. The rebels. The troublemakers. The round pegs in the square holes. The ones who see things differently"
// Find the range of the specified string
let range = fullText.range(of: searchString)
// If the string is found, return the range wrapped in NSValue
if range.location != NSNotFound {
return [NSValue(range: range)]
}
// If the string is not found, return an empty array
return []
}
In the example above the method searches for a predefined String
within the text view, converting the text to an NSString
for executing some range operations. If the specified string is found, its range is returned as an array of NSValue
objects, allowing the Writing Tools to ignore that portion of the text.
The following is an example of a SwiftUI view with a UITextView
component applying the ignored range method example mentioned before.
import SwiftUI
import UIKit
struct CustomTextView: UIViewRepresentable {
@Binding var text: String
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.text = text
textView.isEditable = true
textView.font = UIFont.systemFont(ofSize: 17, weight: .regular)
textView.delegate = context.coordinator
textView.writingToolsBehavior = .complete
return textView
}
func updateUIView(_ textView: UITextView, context: Context) {
if textView.text != text {
textView.text = text
}
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
class Coordinator: NSObject, UITextViewDelegate {
var parent: CustomTextView
init(parent: CustomTextView) {
self.parent = parent
}
func textViewDidChange(_ textView: UITextView) {
if parent.text != textView.text {
parent.text = textView.text
}
}
func textView(_ textView: UITextView, writingToolsIgnoredRangesInEnclosingRange enclosingRange: NSRange) -> [NSValue] {
let fullText = textView.text as NSString
let searchString = "Here's to the crazy ones. The misfits. The rebels. The troublemakers. The round pegs in the square holes. The ones who see things differently"
let range = fullText.range(of: searchString)
if range.location != NSNotFound {
return [NSValue(range: range)]
}
return []
}
}
}
struct ContentView: View {
@State var textContent: String = """
Here's to the crazy ones. The misfits. The rebels. The troublemakers. The round pegs in the square holes. The ones who see things differently. They're not fond of rules. And they have no respect for the status quo. You can quote them, disagree with them, glorify or vilify them. About the only thing you can't do is ignore them. Because they change things. They push the human race forward. And while some may see them as the crazy ones, we see genius. Because the people who are crazy enough to think they can change the world, are the ones who do.
"""
var body: some View {
NavigationStack {
Form {
CustomTextView(text: $textContent)
.frame(height: 300)
}
.navigationTitle("Writing Tools ✏️")
}
}
}