3

So my goal is to have a more convenient method for adding a placeholder text value on SwiftUI's TextEditor, since there doesn't appear to be one. The approach I'm trying has uncovered something I really don't understand around Binding<> wrapped types. (Maybe this is a red flag that I'm doing something not recommended?)

Anyway, on to my question: are we able to programmatically update the underlying values on Bindings? If I accept some Binding<String> value, can I update it from within my method here? If so, will the updated value be referenced by the @State originator? The below example places my placeholder value in as text where I'm trying to type when you click into it, and does not even attempt it again if I clear it out.

Imported this code from other posts I found some time ago to make it display a placeholder if the body is empty.

import Foundation
import SwiftUI

struct TextEditorViewThing: View {
  @State private var noteText = ""
  var body: some View {
    VStack{
      TextEditor(text: $noteText)
        .textPlaceholder(placeholder: "PLACEHOLDER", text: $noteText)
        .padding()
    }
  }
}

extension TextEditor {
  @ViewBuilder func textPlaceholder(placeholder: String, text: Binding<String>) -> some View {
    self.onAppear {
      // remove the placeholder text when keyboard appears
      NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { (noti) in
        withAnimation {
          if text.wrappedValue == placeholder {
            text.wrappedValue = placeholder
          }
        }
      }
      
      // 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 text.wrappedValue == "" {
            text.wrappedValue = placeholder
          }
        }
      }
    }
  }
}

1 Answer 1

1

Customize this setup as per your requirement:

struct ContentView: View {
    @State private var text: String = ""
    
    var body: some View {
        VStack {
            ZStack(alignment: .leading) {
                if self.text.isEmpty {
                    VStack {
                        Text("Placeholder Text")
                            .multilineTextAlignment(.leading)
                            .padding(.leading, 25)
                            .padding(.top, 8)
                            .opacity(0.5)
                        Spacer()
                    }
                }
                TextEditor(text: $text)
                    .padding(.leading, 20)
                    .opacity(self.text.isEmpty ? 0.5 : 1)
            }
            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/2)
            .overlay(
                Rectangle().stroke()
                    .foregroundColor(Color.black)
                    .padding(.horizontal, 15)
            )
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Good enough answer for me! Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.