Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change background color of TextEditor in SwiftUI

Tags:

swift

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?

like image 499
Lorenzo Fiamingo Avatar asked Jul 11 '20 10:07

Lorenzo Fiamingo


People also ask

How do you change the background color on TextEditor?

Go to Window > Preferences, then navigate to General > Editors > Text Editors. In the panel on the right, from the Appearance color options list select Background color, then use the color picker to choose the desired color (the System default checkbox must be unchecked).

How do I change TextEditor background in SwiftUI?

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 .

How do I change the background of a list in SwiftUI?

We can change the background color of a list row in SwiftUI with listRowBackground(_:) modifier. To set a list row background color, add listRowBackground(_:) modifier to the list row item.


7 Answers

iOS 16

You should hide the default background to see your desired one:

TextEditor(text: .constant("Placeholder"))
    .scrollContentBackground(.hidden) // <- Hide it
    .background(Color.red) // To see this

iOS 15 and below

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

like image 102
Mojtaba Hosseini Avatar answered Sep 28 '22 19:09

Mojtaba Hosseini


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)
    }
}
like image 30
Marc T. Avatar answered Sep 28 '22 20:09

Marc T.


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())
    }
}
like image 29
Rebeloper Avatar answered Sep 28 '22 19:09

Rebeloper


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.

like image 38
tillhain Avatar answered Sep 28 '22 19:09

tillhain


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
    }
}
like image 30
Michael Alfano Avatar answered Sep 28 '22 19:09

Michael Alfano


Update iOS 16 / SwiftUI 4.0

You need to use .scrollContentBackground(.hidden) instead of UITextView.appearance().backgroundColor = .clear

https://twitter.com/StuFFmc/status/1556561422431174656

Warning: This is an iOS 16 only so you'll probably need some if #available and potentially two different TextEditor component.

like image 25
StuFF mc Avatar answered Sep 28 '22 21:09

StuFF mc


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

like image 37
voorjaar Avatar answered Sep 28 '22 20:09

voorjaar