'Change background color of TextEditor in SwiftUI

TextEditor seems to have a default white background. So the following is not working and it displayed as white instead of defined red:

var body: some View {
    TextEditor(text: .constant("Placeholder"))
        .background(Color.red)
}

Is it possible to change the color to a custom one?



Solution 1:[1]

TextEditor is backed by UITextView. So you need to get rid of the UITextView's backgroundColor first and then you can set any View to the background.

struct ContentView: View {
    init() {
        UITextView.appearance().backgroundColor = .clear
    }

    var body: some View {
        List {
            TextEditor(text: .constant("Placeholder"))
                .background(Color.red)
        }
    }
}

Demo

enter image description here

You can find my simple trick for growing TextEditor here in this answer

Solution 2:[2]

Pure SwiftUI solution on iOS and macOS

colorMultiply is your friend.

struct ContentView: View {
    
    @State private var editingText: String = ""
    
    var body: some View {
        
        TextEditor(text: $editingText)
            .frame(width: 400, height: 100, alignment: .center)
            .cornerRadius(3.0)
            .colorMultiply(.gray)
    }
}

Solution 3:[3]

Custom Background color with SwiftUI on macOS

On macOS, unfortunately, you have to fallback to AppKit and wrap NSTextView.

You need to declare a view that conforms to NSViewRepresentable

This should give you pretty much the same behaviour as SwiftUI's TextEditor-View and since the wrapped NSTextView does not draw its background, you can use the .background-ViewModifier to change the background

struct CustomizableTextEditor: View {
    @Binding var text: String
    
    var body: some View {
        GeometryReader { geometry in
            NSScrollableTextViewRepresentable(text: $text, size: geometry.size)
        }
    }
    
}

struct NSScrollableTextViewRepresentable: NSViewRepresentable {
    typealias Representable = Self
    
    // Hook this binding up with the parent View
    @Binding var text: String
    var size: CGSize
    
    // Get the UndoManager
    @Environment(\.undoManager) var undoManger
    
    // create an NSTextView
    func makeNSView(context: Context) -> NSScrollView {
        
        // create NSTextView inside NSScrollView
        let scrollView = NSTextView.scrollableTextView()
        let nsTextView = scrollView.documentView as! NSTextView
        
        // use SwiftUI Coordinator as the delegate
        nsTextView.delegate = context.coordinator
        
        // set drawsBackground to false (=> clear Background)
        // use .background-modifier later with SwiftUI-View
        nsTextView.drawsBackground = false
        
        // allow undo/redo
        nsTextView.allowsUndo = true
        
        return scrollView
    }
    
    func updateNSView(_ scrollView: NSScrollView, context: Context) {
        // get wrapped nsTextView
        guard let nsTextView = scrollView.documentView as? NSTextView else {
            return
        }
        
        // fill entire given size
        nsTextView.minSize = size
        
        // set NSTextView string from SwiftUI-Binding
        nsTextView.string = text
    }
    
    // Create Coordinator for this View
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    // Declare nested Coordinator class which conforms to NSTextViewDelegate
    class Coordinator: NSObject, NSTextViewDelegate {
        var parent: Representable // store reference to parent
        
        init(_ textEditor: Representable) {
            self.parent = textEditor
        }
        
        // delegate method to retrieve changed text
        func textDidChange(_ notification: Notification) {
            // check that Notification.name is of expected notification
            // cast Notification.object as NSTextView

            guard notification.name == NSText.didChangeNotification,
                let nsTextView = notification.object as? NSTextView else {
                return
            }
            // set SwiftUI-Binding
            parent.text = nsTextView.string
        }
        
        // Pass SwiftUI UndoManager to NSTextView
        func undoManager(for view: NSTextView) -> UndoManager? {
            parent.undoManger
        }

        // feel free to implement more delegate methods...
        
    }
    
}

Usage

ContenView: View {
    @State private var text: String

    var body: some View {
        VStack {
            Text("Enter your text here:")
            CustomizableTextEditor(text: $text)
                .background(Color.red)
        }
            .frame(minWidth: 600, minHeight: 400)

    }
}

Edit:

  • Pass reference to SwiftUI UndoManager so that default undo/redo actions are available.
  • Wrap NSTextView in NSScrollView so that it is scrollable. Set minSize property of NSTextView to enclosing SwiftUIView-Size so that it fills the entire allowed space.

Caveat: Only first line of this custom TextEditor is clickable to enable text editing.

Solution 4:[4]

extension View {
/// Layers the given views behind this ``TextEditor``.
    func textEditorBackground<V>(@ViewBuilder _ content: () -> V) -> some View where V : View {
        self
            .onAppear {
                UITextView.appearance().backgroundColor = .clear
            }
            .background(content())
    }
}

Solution 5:[5]

You can use Mojtaba's answer (the approved answer). It works in most cases. However, if you run into this error:

"Return from initializer without initializing all stored properties"

when trying to use the init{ ... } method, try adding UITextView.appearance().backgroundColor = .clear to .onAppear{ ... } instead.

Example:

var body: some View {
    VStack(alignment: .leading) {

        ...

    }
    .onAppear {
        UITextView.appearance().backgroundColor = .clear
    }
}

Solution 6:[6]

This works for me on macOS

extension NSTextView {
  open override var frame: CGRect {
    didSet {
      backgroundColor = .clear
      drawsBackground = true
    }
  }
}
struct ContentView: View {
    @State var text = ""
    var body: some View {        
        TextEditor(text: $text)           
            .background(Color.red)    
    }

Reference this answer

Solution 7:[7]

Using the Introspect library, you can use .introspectTextView for changing the background color.

TextEditor(text: .constant("Placeholder"))
.cornerRadius(8)
.frame(height: 100)
.introspectTextView { textView in
    textView.backgroundColor = UIColor(Color.red)
}

Result

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Marc T.
Solution 3
Solution 4 Rebeloper
Solution 5 Michael Alfano
Solution 6 voorjaar
Solution 7 Tamás Sengel