How to add placeholder text to TextEditor in SwiftUI?
Solution 1
It is not possible out of the box but you can achieve this effect with ZStack or the .overlay property.
What you should do is check the property holding your state. If it is empty display your placeholder text. If it's not then display the inputted text instead.
And here is a code example:
ZStack(alignment: .leading) {
if email.isEmpty {
Text(Translation.email)
.font(.custom("Helvetica", size: 24))
.padding(.all)
}
TextEditor(text: $email)
.font(.custom("Helvetica", size: 24))
.padding(.all)
}
Note: I have purposely left the .font and .padding styling for you to see that it should match on both the TextEditor and the Text.
EDIT: Having in mind the two problems mentioned in Legolas Wang's comment here is how the alignment and opacity issues could be handled:
- In order to make the Text start at the left of the view simply wrap it in HStack and append Spacer immediately after it like this:
HStack {
Text("Some placeholder text")
Spacer()
}
- In order to solve the opaque problem you could play with conditional opacity - the simplest way would be using the ternary operator like this:
TextEditor(text: stringProperty)
.opacity(stringProperty.isEmpty ? 0.25 : 1)
Of course this solution is just a silly workaround until support gets added for TextEditors.
Solution 2
You can use a ZStack with a disabled TextEditor
containing your placeholder text behind. For example:
ZStack {
if self.content.isEmpty {
TextEditor(text:$placeholderText)
.font(.body)
.foregroundColor(.gray)
.disabled(true)
.padding()
}
TextEditor(text: $content)
.font(.body)
.opacity(self.content.isEmpty ? 0.25 : 1)
.padding()
}
Solution 3
Until we have some API support, an option would be to use the binding string as placeholder and onTapGesture to remove it
TextEditor(text: self.$note)
.padding(.top, 20)
.foregroundColor(self.note == placeholderString ? .gray : .primary)
.onTapGesture {
if self.note == placeholderString {
self.note = ""
}
}
Solution 4
I built a custom view that can be used like this (until TextEditor officially supports it - maybe next year)
TextArea("This is my placeholder", text: $text)
Full solution below:
struct TextArea: View {
private let placeholder: String
@Binding var text: String
init(_ placeholder: String, text: Binding<String>) {
self.placeholder = placeholder
self._text = text
}
var body: some View {
TextEditor(text: $text)
.background(
HStack(alignment: .top) {
text.isBlank ? Text(placeholder) : Text("")
Spacer()
}
.foregroundColor(Color.primary.opacity(0.25))
.padding(EdgeInsets(top: 0, leading: 4, bottom: 7, trailing: 0))
)
}
}
extension String {
var isBlank: Bool {
return allSatisfy({ $0.isWhitespace })
}
}
I'm using the default padding of the TextEditor here, but feel free to adjust to your preference.
Solution 5
There are some good answers here, but I wanted to bring up a special case. When a TextEditor is placed in a Form, there are a few issues, primarily with spacing.
- TextEditor does not horizontally align with other form elements (e.g. TextField)
- The placeholder text does not horizontally align with the TextEditor cursor.
- When there is whitespace or carriage return/newline are added, the placeholder re-positions to the vertical-middle (optional).
- Adding leading spaces causes the placeholder to disappear (optional).
One way to fix these issues:
Form {
TextField("Text Field", text: $text)
ZStack(alignment: .topLeading) {
if comments.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
Text("Long Text Field").foregroundColor(Color(UIColor.placeholderText)).padding(.top, 8)
}
TextEditor(text: $comments).padding(.leading, -3)
}
}
Legolas Wang
Updated on June 10, 2022Comments
-
Legolas Wang almost 2 years
When using SwiftUI's new TextEditor, you can modify its content directly using a @State. However, I haven't see a way to add a placeholder text to it. Is it doable right now?
I added an example that Apple used in their own translator app. Which appears to be a multiple lines text editor view that supports a placeholder text.
-
pawello2222 almost 4 yearsI don't think it's possible now. It's still beta so it might change though.
-
Asperi almost 4 yearsI hardly believe it will be ever, it is TextEditor, not TextField. There was no placeholder in UITextView as well.
-
Legolas Wang almost 4 years@Asperi I added an example from Apple's translator app, which seems to have a TextEditor view that supports placeholder. I'm trying to achieve the same.
-
Asperi almost 4 yearskey word is seems ... looks at this solution How do I create a multiline TextField in SwiftUI?
-
ricardopereira almost 4 yearsI created a Feedback Assistant asking for this be available in the final Xcode 12 release 🙏 (FB8118309)
-
-
Legolas Wang almost 4 yearsIt is a brilliant thought, but unfortunately it suffered from two problems. The first one is the TextEditor view, which is opaque, so it will block the placeholder view when layering on top in a ZStack. Tweaking with opacity could help a little in this case. The second problem is the frame logic with Text and TextEditor, The TextEditor begin from left top corner, and the Text starts from the center of the view. Which makes them very hard to overlay exactly on top. Do you have some thoughts about the alignment issue?
-
bde.dev almost 4 years@LegolasWang I didn't want to include anything super specific about styling but instead left the font and padding only in order to demonstrate that the styling, aligning etc. should match. I am adding an edit to my answer to demonstrate how those 2 for-mentioned problems could be handled.
-
RndmTsk over 3 yearsYou can actually put the
HStack
below theTextEditor
and give it a.contentShape
ofNoShape
: ``` struct NoShape: Shape { func path(in rect: CGRect) -> Path { return Path() } } // ... HStack { Text("Some placeholder text") .contentShape(NoShape()) } ``` -
unixb0y over 3 yearssomehow, there's a white plane overlaying the placeholder 🤔
-
grey over 3 yearsStill using this on iOS 14.2 (light and dark mode) and no issues so far. If you're using it with other custom views though, you might want to change the code a bit to suit your needs. Feel free to share your screenshot and code though 😊
-
SwiftUser over 3 yearsThe day where you can use a TextEditor a dismiss the keyboard, similar to a TextField is the day I rejoice.
-
qwerty-reloader over 2 yearsFor placeholder text color you can use: .foregroundColor(Color(UIColor.placeholderText))
-
Richard Topchii over 2 yearsWhat if the user types placeholder string?
-
LilaQ about 2 yearsPlease don't do it like that, this can bring all sorts of problems.