9
votes

Comment ajouter du texte d'espace réservé à TextEditor dans SwiftUI?

Lorsque vous utilisez le nouveau TextEditor de SwiftUI, vous pouvez modifier son contenu directement à l'aide d'un @State. Cependant, je ne vois pas de moyen d'y ajouter un texte d'espace réservé. Est-ce faisable maintenant?

entrez la description de l'image ici

J'ai ajouté un exemple qu'Apple a utilisé dans sa propre application de traduction. Ce qui semble être une vue d'éditeur de texte à plusieurs lignes prenant en charge un texte d'espace réservé.


5 commentaires

Je ne pense pas que ce soit possible maintenant. C'est toujours la version bêta, donc cela pourrait changer.


Je crois à peine que ce sera jamais, c'est TextEditor, pas TextField. Il n'y avait pas non plus d'espace réservé dans UITextView.


@Asperi J'ai ajouté un exemple de l'application de traduction d'Apple, qui semble avoir une vue TextEditor qui prend en charge l'espace réservé. J'essaye de réaliser la même chose.


Le mot clé est semble ... regarde cette solution Comment créer un TextField multiligne dans SwiftUI?


J'ai créé un assistant de rétroaction demandant que cela soit disponible dans la version finale de Xcode 12 🙏 (FB8118309)


5 Réponses :


9
votes

Ce n'est pas possible dès le départ, mais vous pouvez obtenir cet effet avec ZStack ou la propriété .overla y.

Ce que vous devez faire est de vérifier la propriété détenant votre état. S'il est vide, affichez votre texte d'espace réservé. Si ce n'est pas le cas, affichez plutôt le texte saisi.

Et voici un exemple de code:

TextEditor(text: stringProperty)        
        .opacity(stringProperty.isEmpty ? 0.25 : 1)

Remarque: J'ai volontairement laissé le style .font et .padding pour que vous voyiez qu'il devrait correspondre à la fois sur TextEditor et sur Text.

EDIT: En gardant à l'esprit les deux problèmes mentionnés dans le commentaire de Legolas Wang, voici comment les problèmes d'alignement et d'opacité pourraient être traités:

  • Pour que le texte commence à gauche de la vue, enveloppez-le simplement dans HStack et ajoutez Spacer immédiatement après comme ceci:
HStack {
   Text("Some placeholder text")
   Spacer()
}
  • Afin de résoudre le problème d'opacité, vous pouvez jouer avec l'opacité conditionnelle - le moyen le plus simple serait d'utiliser l'opérateur ternaire comme celui-ci:
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)
}

Bien sûr, cette solution n'est qu'une solution de contournement stupide jusqu'à ce que le support soit ajouté pour TextEditors.


3 commentaires

C'est une pensée brillante, mais malheureusement elle a souffert de deux problèmes. Le premier est la vue TextEditor, qui est opaque, elle bloquera donc la vue d'espace réservé lors de la superposition par-dessus dans un ZStack. Ajuster avec l'opacité pourrait aider un peu dans ce cas. Le deuxième problème est la logique du cadre avec Text et TextEditor, TextEditor commence à partir du coin supérieur gauche et le texte commence à partir du centre de la vue. Ce qui les rend très difficiles à superposer exactement sur le dessus. Avez-vous des réflexions sur le problème d'alignement?


@LegolasWang Je ne voulais rien inclure de très spécifique sur le style, mais j'ai laissé la police et le remplissage uniquement pour démontrer que le style, l'alignement, etc. doivent correspondre. J'ajoute une modification à ma réponse pour montrer comment ces 2 problèmes mentionnés ci-dessus pourraient être traités.


Vous pouvez en fait placer le HStack sous TextEditor et lui donner un .contentShape de NoShape : `` `` struct NoShape: Shape {func path (in rect: CGRect) -> Path {return Path ()}} // ... HStack {Text ("Some placeholder text") .contentShape (NoShape ())} `` `



5
votes

Jusqu'à ce que nous ayons un support API, une option serait d'utiliser la chaîne de liaison comme espace réservé et onTapGesture pour la supprimer

TextEditor(text: self.$note)
                .padding(.top, 20)
                .foregroundColor(self.note == placeholderString ? .gray : .primary)
                .onTapGesture {
                    if self.note == placeholderString {
                        self.note = ""
                    }
                }


0 commentaires

2
votes

J'ai créé une vue personnalisée qui peut être utilisée comme celle-ci (jusqu'à ce que TextEditor le prenne officiellement en charge - peut-être l'année prochaine)

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

Solution complète ci-dessous:

TextArea("This is my placeholder", text: $text)

J'utilise le remplissage par défaut de TextEditor ici, mais n'hésitez pas à vous adapter à vos préférences.


3 commentaires

en quelque sorte, il y a un avion blanc superposant l'espace réservé 🤔


Je l'utilise toujours sur iOS 14.2 (mode clair et sombre) et aucun problème jusqu'à présent. Si vous l'utilisez avec d'autres vues personnalisées, vous voudrez peut-être modifier un peu le code en fonction de vos besoins. N'hésitez pas à partager votre capture d'écran et votre code 😊


Le jour où vous pouvez utiliser un TextEditor pour supprimer le clavier, semblable à un TextField, est le jour où je me réjouis.



1
votes

Comme je le sais, c'est la meilleure façon d'ajouter un texte d'espace réservé à TextEditor dans 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"
                        }
                    }
                }
            }
    }
}


0 commentaires

1
votes

Il y a de bonnes réponses ici, mais je voulais évoquer un cas particulier. Lorsqu'un TextEditor est placé dans un formulaire, il y a quelques problèmes, principalement avec l'espacement.

  1. TextEditor ne s'aligne pas horizontalement avec d'autres éléments de formulaire (par exemple, TextField)
  2. Le texte de l'espace réservé ne s'aligne pas horizontalement avec le curseur TextEditor.
  3. Lorsqu'il y a des espaces ou des retours chariot / nouvelle ligne sont ajoutés, l'espace réservé se repositionne au milieu vertical (facultatif).
  4. L'ajout d'espaces de début fait disparaître l'espace réservé (facultatif).

Une façon de résoudre ces problèmes:

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


0 commentaires