Reputation: 4109
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?
Upvotes: 121
Views: 42921
Reputation: 1
I was looking for a solution as well and here is what I have figured it out.
I think a more proper way is that we can just create a custom wrapper for UITextView
to remove the default background color, and then we apply the style in View instead of interacting with the UIKit stuff inside the View.
import Foundation
import SwiftUI
struct TextViewWrapper: UIViewRepresentable {
@Binding var text: String
func makeUIView(context: Context) -> UITextView {
let uiTextView = UITextView()
uiTextView.font = .preferredFont(forTextStyle: .body)
uiTextView.backgroundColor = .clear
return uiTextView
}
func updateUIView(_ uiTextView: UITextView, context: Context) {
uiTextView.text = text
}
}
@State private var description = ""
TextViewWrapper(text: $description)
.frame(height: 72)
.padding(
EdgeInsets(
top: 8,
leading: 8,
bottom: 8,
trailing: 8
)
)
.background(Color.secondary.opacity(0.2))
.cornerRadius(12)
Upvotes: 0
Reputation: 207
To change TextEditor background color, this modifier works well for me. Just we need to consider both the cases of iOS 16 and below.
extension View {
func textEditorBackground(_ content: Color) -> some View {
if #available(iOS 16.0, *) {
return self.scrollContentBackground(.hidden)
.background(content)
} else {
UITextView.appearance().backgroundColor = .clear
return self.background(content)
}
}
}
TextEditor(text: $message)
.modifier(TextEditorBackground(color: Color("PrimaryGray")))
Upvotes: 3
Reputation: 1286
Well, this soultion seems to work with me in iOS 16 and some earlier versions as well.
TextEditor(text: $text)
.scrollContentBackgroundHidden()
.background(Style.backgroundColor) // Color works with iOS 16
.textEditorBackground {
Color(Style.backgroundColor) // Color works with earlier versions
}
With the help of both extensions to support earlier versions as well
extension View {
/// Hides the standard system background of the view.
///
func scrollContentBackgroundHidden() -> some View {
if #available(iOS 16.0, *) {
return self.scrollContentBackground(.hidden)
} else {
return self
}
}
/// 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())
}
}
Upvotes: 1
Reputation: 1
Ios 16
Make sure to add .scrollContentBackground(.hidden) this lines then background will apply
TextEditor(text:.constant(hint))
.scrollContentBackground(.hidden)
.background(.red) // To see this
This work for me
Upvotes: 12
Reputation: 25443
As many have stated, with iOS 16 you need to use scrollContentBackground
. I created an extension method to handle both cases:
struct ContentView: View {
@State private var editingText: String = ""
var body: some View {
TextEditor(text: $editingText)
.transparentScrolling()
.background(Color.red)
}
}
public extension View {
func transparentScrolling() -> some View {
if #available(iOS 16.0, *) {
return scrollContentBackground(.hidden)
} else {
return onAppear {
UITextView.appearance().backgroundColor = .clear
}
}
}
}
Upvotes: 33
Reputation: 675
It appears the UITextView.appearance().backgroundColor = .clear
trick in IOS 16,
only works for the first time you open the view and the effect disappear when the second time it loads.
So we need to provide both ways in the app. Answer from StuFF mc works.
var body: some View {
if #available(iOS 16.0, *) {
mainView.scrollContentBackground(.hidden)
} else {
mainView.onAppear {
UITextView.appearance().backgroundColor = .clear
}
}
}
// rename body to mainView
var mainView: some View {
TextEditor(text: $notes).background(Color.red)
}
Upvotes: 0
Reputation: 3711
import SwiftUI
struct AddCommentView: View {
init() {
UITextView.appearance().backgroundColor = .clear
}
var body: some View {
VStack {
if #available(iOS 16.0, *) {
TextEditor(text: $viewModel.commentText)
.scrollContentBackground(.hidden)
} else {
TextEditor(text: $viewModel.commentText)
}
}
.background(Color.blue)
.frame(height: UIScreen.main.bounds.width / 2)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.red, lineWidth: 1)
)
}
}
Upvotes: 0
Reputation: 81
TextField(
"free_form",
text: $comment,
prompt: Text("Type your feedback..."),
axis: .vertical
)
.lineSpacing(10.0)
.lineLimit(10...)
.padding(16)
.background(Color.themeSeashell)
.cornerRadius(16)
ZStack(alignment: .topLeading) {
RoundedRectangle(cornerRadius: 16)
.foregroundColor(.gray)
TextEditor(text: $comment)
.padding()
.focused($isFocused)
if !isFocused {
Text("Type your feedback...")
.padding()
}
}
.frame(height: 132)
.onAppear() {
UITextView.appearance().backgroundColor = .clear
}
Upvotes: 7
Reputation: 119917
You should hide the default background to see your desired one:
TextEditor(text: .constant("Placeholder"))
.scrollContentBackground(.hidden) // <- Hide it
.background(.red) // To see this
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(.red)
}
}
}
You can find my simple trick for growing TextEditor here in this answer
Upvotes: 232
Reputation: 4169
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.
Upvotes: 12
Reputation: 51
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)
}
Upvotes: 1
Reputation: 181
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)
}
Upvotes: 10
Reputation: 46
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
}
}
Upvotes: 1
Reputation: 788
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())
}
}
Upvotes: 6
Reputation: 5340
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)
}
}
Upvotes: 20
Reputation: 51
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...
}
}
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)
}
}
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.
Upvotes: 5