SwiftUI: How to disable the "smart quotes" in TextEditor

1.8k Views Asked by At

I'm developing a Python-based graphic calculator for MacOS using SwiftUI.

https://github.com/snakajima/macplot

I am using SwiftUI's TextEditor as the editor for Python code, but I am not able to figure out how to disable the smart quote (UITextInputTraits, smartQuotesType: UITextSmartQuotesType).

        VStack {
            TextEditor(text: $pythonScript.script)
            HStack {
                Button(action: {
                    pythonScript.run(clear: settings.shouldClear)
                }, label: {
                    Text("Plot")
                })
                Toggle("Clear", isOn: $settings.shouldClear)
            }
            if let errorMsg = pythonScript.errorMsg {
                Text(errorMsg)
                    .foregroundColor(.pink)
            }
        }
6

There are 6 best solutions below

0
Satoshi Nakajima On

After several trials, I came up with the following work-around. It relies on the fact that TextEditor is implemented on top of NSTextView, and changes its behavior across the entire application. It is ugly, but works.

// HACK to work-around the smart quote issue
extension NSTextView {
    open override var frame: CGRect {
        didSet {
            self.isAutomaticQuoteSubstitutionEnabled = false
        }
    }
}
1
Jared Updike On

For those who are looking for an answer for UIKit (iOS, iPadOS) instead of AppKit (macOS), this works for me using a similar approach. Thanks Satoshi!

extension UITextView {
    open override var frame: CGRect {
        didSet {
            self.smartQuotesType = UITextSmartQuotesType.no
        }
    }
}

This has the same drawbacks, which is that all text fields in your application will lose auto-smart-quotes, but at least you can control this if you need it.

0
Cristik On

Another solution would be to write an NSTextView wrapper:

struct TextView: NSViewRepresentable {
    @Binding var text: String
    private var customizations = [(NSTextView) -> Void]()

    init(text: Binding<String>) {
        _text = text
    }

    func makeNSView(context: Context) -> NSView {
        NSTextView()
    }

    func updateNSView(_ nsView: NSView, context: Context) {
        let textView = nsView as! NSTextView
        textView.string = text
        customizations.forEach { $0(textView) }
    }

    func automaticDashSubstitutionEnabled(_ enabled: Bool) -> Self {
        customized { $0.isAutomaticDashSubstitutionEnabled = enabled }
    }

    func automaticQuoteSubstitutionEnabled(_ enabled: Bool) -> Self {
        customized { $0.isAutomaticQuoteSubstitutionEnabled = enabled }
    }

    func automaticSpellingCorrectionEnabled(_ enabled: Bool) -> Self {
        customized { $0.isAutomaticSpellingCorrectionEnabled = enabled }
    }
}

private extension TextView {
    func customized(_ customization: @escaping (NSTextView) -> Void) -> Self {
        var copy = self
        copy.customizations.append(customization)
        return copy
    }
}

, which can be used like this:

TextView(text: $myText)
    .automaticDashSubstitutionEnabled(false)
    .automaticQuoteSubstitutionEnabled(false)
    .automaticSpellingCorrectionEnabled(false)
1
James Abela On

I also struggled with this and so in the end I fixed the smart quotes by replacing them back to straight quotes as the Textfield was typed. I know its a hack, but it works.

TextEditor(text: $Mytext)
.onChange(of: Mytext) { 
                        newValue in
                        Mytext = Mytext.replacingOccurrences(of: "“", with: "\"") // replaces smart quotes
                        Mytext = Mytext.replacingOccurrences(of: "”", with: "\"")
                        Mytext = Mytext.replacingOccurrences(of: "‘", with: "'")
                        Mytext = Mytext.replacingOccurrences(of: "’", with: "'")
}
0
Xiaoxin On

I used SwiftUI Introspect to turn this off for my TextEditor.

            TextEditor(text: $text)
                .introspect(.textEditor, on: .macOS(.v14, .v13)) { nsTextView in
                    nsTextView.isAutomaticQuoteSubstitutionEnabled = false
                    nsTextView.isAutomaticDashSubstitutionEnabled = false
                }
1
Martin On

a very simple way to disable all smart transformations on a SwiftUI TextField is to set the keyboardType to asciiCapable like so

TextEditor(text: $Mytext)
.keyboardType(.asciiCapable)

All the characters transformation will be disable with this keyboard. Hope it helps