How to add placeholder text to TextEditor in SwiftUI?

半世苍凉 提交于 2021-02-18 05:07:09

问题


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.


回答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.




回答2:


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 = ""
                    }
                }



回答3:


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.




回答4:


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.

  1. TextEditor does not horizontally align with other form elements (e.g. TextField)
  2. The placeholder text does not horizontally align with the TextEditor cursor.
  3. When there is whitespace or carriage return/newline are added, the placeholder re-positions to the vertical-middle (optional).
  4. 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)
    }
}



回答5:


As I know, this is the best way to add a placeholder text to TextEditor in SwiftUI

struct ContentView: View {
    @State var text = "Type here"
    
    var body: some View {

        TextEditor(text: self.$text)
            // make the color of the placeholder gray
            .foregroundColor(self.text == "Type here" ? .gray : .primary)
            
            .onAppear {

                // remove the placeholder text when keyboard appears
                NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { (noti) in
                    withAnimation {
                        if self.text == "Type here" {
                            self.text = ""
                        }
                    }
                }
                
                // put back the placeholder text if the user dismisses the keyboard without adding any text
                NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { (noti) in
                    withAnimation {
                        if self.text == "" {
                            self.text = "Type here"
                        }
                    }
                }
            }
    }
}



回答6:


With an overlay, you won't be able to allow touch on the placeholder text for the user to write in the textEditor. You better work on the background, which is a view.

So, create it, while deactivating the default background:

struct PlaceholderBg: View {

let text: String?

init(text:String? = nil) {
        UITextView.appearance().backgroundColor = .clear // necessary to remove the default bg
    
    self.text = text
 }

var body: some View {
    VStack {
    HStack{
    
    Text(text!)
          
    Spacer()
    }
    Spacer()
    }
}
    
}

then, in your textEditor:

 TextEditor(text: $yourVariable)
                        
                        .frame(width: x, y)
                        .background(yourVariable.isEmpty ? PlaceholderBg(texte: "my placeholder text") : PlaceholderBG(texte:""))



回答7:


SwiftUI TextEditor does not yet have support for a placeholder. As a result, we have to "fake" it.

Other solutions had problems like bad alignment or color issues. This is the closest I got to simulating a real placeholder. This solution "overlays" a TextField over the TextEditor. The TextField contains the placeholder. The TextField gets hidden as soon as a character is inputted into the TextEditor.

import SwiftUI

struct Testing: View {
  @State private var textEditorText = ""
  @State private var textFieldText = ""

  var body: some View {
    VStack {
      Text("Testing Placeholder Example")
      ZStack(alignment: Alignment(horizontal: .center, vertical: .top)) {
        TextEditor(text: $textEditorText)
          .padding(EdgeInsets(top: -7, leading: -4, bottom: -7, trailing: -4)) // fix padding not aligning with TextField
        if textEditorText.isEmpty {
          TextField("Placeholder text here", text: $textFieldText)
            .disabled(true) // don't allow for it to be tapped
        }
      }
    }
  }
}

struct Testing_Previews: PreviewProvider {
  static var previews: some View {
    Testing()
  }
}



回答8:


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()
}



回答9:


I like Umayanga's approach but his code wasn't reusable. Here's the code as a reusable view:

struct TextEditorPH: View {
    
    private var placeholder: String
    @Binding var text: String
    
    init(placeholder: String, text: Binding<String>) {
        self.placeholder = placeholder
        self._text = text
    }
    
    var body: some View {
        TextEditor(text: self.$text)
            // make the color of the placeholder gray
            .foregroundColor(self.text == placeholder ? .gray : .primary)
            
            .onAppear {
                // create placeholder
                self.text = placeholder

                // remove the placeholder text when keyboard appears
                NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { (noti) in
                    withAnimation {
                        if self.text == placeholder {
                            self.text = ""
                        }
                    }
                }
                
                // put back the placeholder text if the user dismisses the keyboard without adding any text
                NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { (noti) in
                    withAnimation {
                        if self.text == "" {
                            self.text = placeholder
                        }
                    }
                }
            }
    }
}


来源:https://stackoverflow.com/questions/62741851/how-to-add-placeholder-text-to-texteditor-in-swiftui

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!