Masquer le curseur d'un UITextField

137

J'utilise un UITextFieldavec un UIPickerViewpour son inputView, de sorte que lorsque l'utilisateur appuie sur le champ de texte, un sélecteur est appelé pour qu'il sélectionne une option.

Presque tout fonctionne, mais j'ai un problème: le curseur clignote toujours dans le champ de texte lorsqu'il est actif, ce qui est moche et inapproprié, car l'utilisateur n'est pas censé taper dans le champ et ne se voit pas présenter de clavier. Je sais que je pourrais hackily résoudre ce en mettant editingà NOsur le champ de texte et le suivi des touches sur, ou en le remplaçant par un bouton de style personnalisé, et convoquant le sélecteur via le code. Cependant, je souhaite utiliser les UITextFieldDelegateméthodes pour toute la gestion des événements sur le champ de texte et des hacks tels que le remplacement du champ de texte par un bouton ne permettent pas cette approche.

Comment puis-je simplement masquer le curseur sur le UITextField?

emenegro
la source

Réponses:

277

Sous-classez simplement UITextField et remplacez caretRectForPosition

- (CGRect)caretRectForPosition:(UITextPosition *)position
{
    return CGRectZero;
}
Joseph Chiu
la source
2
Belle! Fonctionne comme un charme pour moi.
Joe Strout
1
@Joseph Chiu Cela fonctionne très bien. Mais pas avec iOS 4.3. Peux-tu m'aider avec ça?
Dinesh Raja
l'approche la plus propre et la plus courte qui mérite un double vote positif =)
Ilker Baltaci
1
Juste une note. Dans ma sous-classe, j'ai ajouté un bool hideCaret puis dans ce remplacement Si c'est vrai -> return CGRectZero sinon retourne le résultat de super.
WCByrne
6
Sachez simplement que les utilisateurs disposant d'un clavier externe peuvent modifier la valeur du champ de texte même si le curseur est masqué et que vous utilisez une vue de sélection.
Mark
156

À partir d'iOS 7, vous pouvez maintenant simplement définir le tintColor = [UIColor clearColor]sur le champ textField et le curseur disparaîtra.

jamone
la source
1
Cela fonctionne pour le moment, mais je déconseillerais de l'utiliser car cela pourrait changer à l'avenir. Optez plutôt pour la caretRectForPosition:solution de remplacement.
lipka
@lipka C'est vrai, c'est probablement une meilleure façon.
jamone
2
Assez bien pour le moment. Parfois, vous avez juste besoin d'une solution rapide.
GoldenJoe
C'est de loin la solution la plus simple!
Jay Q.
1
Cette réponse devrait être approuvée pour iOS 7+
tryp
95

Vous pouvez simplement effacer la teinte du champ de texte

self.textField.tintColor = [UIColor clearColor];

Swift 3.0

self.textField.tintColor = .clear

entrez la description de l'image ici

vieil homme
la source
Meilleure réponse la plus simple.
Nik Kov
2
Comme mentionné ci-dessus, la suppression de la couleur de la teinte n'empêche pas les utilisateurs disposant de claviers externes (iPad Pro) de modifier le texte.
Michael Long
@MichaelLong il ne s'agit pas d'empêcher l'utilisateur de modifier le texte, c'est juste un choix de style.
JRam13
21

Vous pouvez également vouloir empêcher l'utilisateur de sélectionner, copier ou coller du texte afin que la seule entrée de texte provienne de la vue du sélecteur.

- (CGRect) caretRectForPosition:(UITextPosition*) position
{
    return CGRectZero;
}

- (NSArray *)selectionRectsForRange:(UITextRange *)range
{
    return nil;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if (action == @selector(copy:) || action == @selector(selectAll:) || action == @selector(paste:))
    {
        returnNO;
    }

    return [super canPerformAction:action withSender:sender];
}

http://b2cloud.com.au/tutorial/disabling-the-caret-and-text-entry-in-uitextfields/

Nat
la source
15

Vérifiez la propriété selectedTextRange du protocole UITextInput auquel la classe se UITextField conforme. Peu! C'est là une leçon de programmation orientée objet.

Masquer le curseur

Pour masquer le signe d'insertion, supprimez la plage de texte sélectionnée dans le champ de texte.

textField.selectedTextRange = nil; // hides caret

Afficher le curseur

Voici deux façons d'afficher le curseur.

  1. Définissez la plage de texte sélectionnée dans le champ de texte à la fin du document.

    UITextPosition *end = textField.endOfDocument;
    textField.selectedTextRange = [textField textRangeFromPosition:end
                                                        toPosition:end];
  2. Pour garder le curseur au même endroit, commencez par stocker la plage de texte sélectionnée dans le champ de texte dans une variable d'instance.

    _textFieldSelectedTextRange = textField.selectedTextRange;
    textField.selectedTextRange = nil; // hides caret

    Ensuite, lorsque vous souhaitez afficher le curseur, redéfinissez simplement la plage de texte sélectionnée dans le champ de texte sur ce qu'elle était à l'origine:

    textField.selectedTextRange     = _textFieldSelectedTextRange;
    _textFieldLastSelectedTextRange = nil;
ma11hew28
la source
3
Cette solution particulière n'a pas fonctionné pour ma mise en œuvre. Le curseur clignote toujours.
Art Geigel
Eh bien, vous devriez peut-être signaler un bogue sur bugreport.apple.com car la documentation iOS dit: "Si la plage de texte a une longueur, elle indique le texte actuellement sélectionné. Si elle a une longueur nulle, elle indique le signe d'insertion point). Si l'objet de plage de texte est nul, cela indique qu'il n'y a pas de sélection actuelle. "
ma11hew28
4
Je m'en fiche assez pour déposer un rapport. Si d'autres utilisent votre «solution» et ne la voient pas fonctionner, je voulais qu'ils sachent qu'ils ne sont pas seuls.
Art Geigel
Malgré les commentaires de @ ArtGeigel, cela fonctionne parfaitement pour moi. Cependant, je préfère en quelque sorte la solution consistant à passer outre caretRectForPosition. C'est plus explicite ce qu'il fait, et les documents que vous avez cités n'indiquent pas clairement quel devrait être le comportement du curseur quand il n'y a pas de sélection actuelle. Si l'affirmation de @ ArtGeigel selon laquelle cela ne fonctionne pas était correcte (ce qui n'est pas le cas, du moins pour autant que je sache), il ne serait pas clair qu'il s'agissait d'un bogue.
Mark Amery
Cela n'a pas fonctionné pour moi non plus. Le curseur est toujours là et clignote.
CW0007007
11

Réponse fournie par le PO, copiée à partir du corps des questions pour aider à nettoyer la queue toujours croissante de questions sans réponse.

J'ai trouvé une autre solution: sous UIButton-classer et remplacer ces méthodes

- (UIView *)inputView {
    return inputView_;
}

- (void)setInputView:(UIView *)anInputView {
    if (inputView_ != anInputView) {
        [inputView_ release];
        inputView_ = [anInputView retain];
    }
}

- (BOOL)canBecomeFirstResponder {
    return YES;
}

Maintenant, le bouton, en tant que UIResponder, a un comportement similaire à celui UITextFieldet une implémentation assez simple.

Robert Höglund
la source
5
Ce n'est pas vraiment une excellente solution. Découvrez cette réponse ci-dessous: stackoverflow.com/a/13660503/1103584
DiscDev
2
Pourquoi n'est-ce pas une excellente solution? Il atteint l'effet escompté et fournit également des fonctionnalités à une classe qui ne l'avait pas auparavant. Ce n'est pas non plus un hack. Je pense que c'est vraiment cool. Alors qu'est-ce qui ne va pas avec ça?
BreadicalMD
2
@BreadicalMD Le plus gros problème que je peux voir est que vous ne pouvez pas utiliser un UITextFieldDelegateavec ceci pour gérer les événements d'édition de début et de fin. Au lieu de cela - à moins qu'il n'y ait un moyen de gérer ces événements que je ne connais pas - vous devrez remplacer becomeFirstResponderet resignFirstResponderdans la sous-classe de bouton, et éventuellement créer votre propre protocole de délégué, ajouter une delegatepropriété et appeler le délégué de ce qui précède méthodes. C'est beaucoup plus de travail que de simplement remplacer caretRectForPositiondans une UITextFieldsous - classe.
Mark Amery
1
@BreadicalMD Cela dit, bien que la réponse de Joseph Chiu soit supérieure de tout point de vue pratique, je suis toujours d'accord avec vous pour dire que c'est assez sexy. Je n'avais jamais regardé attentivement la UIResponderréférence de la classe auparavant, et je n'avais aucune idée qu'une telle astuce était possible.
Mark Amery
En retard à la fête, mais je pense que c'est une très bonne solution et cela semble beaucoup plus approprié et moins piraté que d'utiliser un UITextFieldet de cacher le curseur, ce qui est fondamentalement un hack puisque nous utilisons alors le champ de texte comme étiquette pendant n'utilisant aucune des fonctionnalités du UITextField.
Rupert
7

Version Swift 5 du post de Net

  override func caretRect(for position: UITextPosition) -> CGRect {
    return .zero
  }
  
  override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] {
    return []
  }
  
  override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    return false
  }
ROM.
la source
Dans mon cas, cela a fonctionné. Ajout d'une sous-classe de UITextField avec ces méthodes en utilisant Swift 4. Merci!
J.Fdez
3

définir la teinteCouleur sur Clear Color

textfield.tintColor = [UIColor clearColor];

et vous pouvez également définir à partir du générateur d'interface

Ahmad Al-Attal
la source
2
Vous venez de copier une réponse d'il y a plus de 2 ans.
Ashley Mills
1
désolé mon ami, mais je n'ai rien copié.
Ahmad Al-Attal
4
C'est intéressant, "mon ami". Votre réponse est assez proche de la réponse de @ oldman du 1er mai 15. Pouvez-vous me dire en quoi le vôtre est différent?
Ashley Mills
1
Comme mentionné ci-dessus, la suppression de la couleur de la teinte n'empêche pas les utilisateurs disposant de claviers externes (iPad Pro) de modifier le texte.
Michael Long
Les utilisateurs professionnels peuvent faire ce qu'ils veulent. Ce sont des pros: ils savent ce qu'ils font; ^)
Anton Tropashko
2

Si vous souhaitez masquer le curseur, vous pouvez facilement l'utiliser! Cela a fonctionné pour moi.

[[textField valueForKey:@"textInputTraits"] setValue:[UIColor clearColor] forKey:@"insertionPointColor"]
Göktuğ Aral
la source
3
Ce n'est pas documenté, pour autant que je sache. Il est possible que l'utilisation de cela entraîne le rejet de votre application pour l'appel d'API privées si vous la soumettez à l'App Store, bien que je ne connaisse aucune soumission qui l'utilise pour tester cette spéculation d'une manière ou d'une autre. C'est dommage, car ce serait bien de pouvoir résoudre ce problème sans sous-classer, et cette réponse le permet.
Mark Amery
1

Réponse fournie par le PO, copiée à partir du corps des questions pour aider à nettoyer la queue toujours croissante de questions sans réponse.

Je pense que j'ai la bonne solution mais si elle peut être améliorée sera la bienvenue :) Eh bien, j'ai créé une sous-classe de UITextField et remplacé la méthode qui renvoie le CGRect pour les limites

-(CGRect)textRectForBounds:(CGRect)bounds {
    return CGRectZero;
}

Le problème? Le texte ne s'affiche pas car le rect est zéro. Mais j'ai ajouté un UILabel en tant que sous-vue du contrôle et remplacé la méthode setText donc, lorsque nous entrons un texte comme d'habitude, le texte du champ de texte est nul et est le libellé qui montre le texte

- (void)setText:(NSString *)aText {
    [super setText:nil];

    if (aText == nil) {
        textLabel_.text = nil;
    }

    if (![aText isEqualToString:@""]) {
        textLabel_.text = aText;
    }
}

Avec cela, la chose fonctionne comme prévu. Connaissez-vous un moyen de l'améliorer?

Robert Höglund
la source
Cette réponse est fondamentalement sans valeur maintenant étant donné que l'approche alternative de Joseph Chiu est très similaire mais beaucoup plus simple. Puis-je suggérer de simplement le supprimer?
Mark Amery
1

Pour désactiver le curseur et le menu, j'utilise la sous-classe avec ces 2 méthodes:

- (CGRect)caretRectForPosition:(UITextPosition *)position {
    return CGRectZero;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    [UIMenuController sharedMenuController].menuVisible = NO;
    self.selectedTextRange = nil;

    return NO;
}
Vive
la source
0

Je sous UITextField- classe simplement et remplace layoutSubviewscomme suit:

- (void)layoutSubviews
{
    [super layoutSubviews];
    for (UIView *v in self.subviews)
    {
        if ([[[v class] description] rangeOfString:@"UITextSelectionView"].location != NSNotFound)
        {
            v.hidden = YES;
        }
    }
}

C'est un sale hack, et peut échouer à l'avenir (à quel point le curseur sera à nouveau visible - votre application ne plantera pas), mais cela fonctionne.

Mark Beaton
la source
-1

Vous pouvez ajouter une BOOL cursorlesspropriété UITextFielddans une catégorie via les objets associés.

@interface UITextField (Cursorless)

@property (nonatomic, assign) BOOL cursorless;

@end

Ensuite, utilisez la méthode swizzling pour alterner caretRectForPosition:avec une méthode qui bascule entre CGRectZeroet sa valeur par défaut à l'aide de cursorless.

Cela conduit à une interface simple via une catégorie drop-in. Ceci est démontré dans les fichiers suivants.

Déposez-les simplement et profitez de cette interface simple

UITextFieldcatégorie: https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless.h https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless .m

Swizzling de méthode: https://github.com/rexmas/RexDK/blob/master/RexDK/Foundation/NSObject%2BRXRuntimeAdditions.h https://github.com/rexmas/RexDK/blob/master/RexDK/Foundation/NSObject% 2BRXRuntimeAdditions.m

Génial-o
la source
une mauvaise solution à tant de niveaux! pour commencer: ne remplacez jamais les méthodes des catégories. Évitez la méthode Swizziling sauf si vous en avez vraiment, vraiment besoin (d'autres ont trouvé de bonnes solutions à cette question). Cela rend votre code tellement plus compliqué.
EsbenB
Si vous regardiez réellement le code, vous vous rendriez compte qu'aucune méthode n'est remplacée dans la catégorie et que le swizzling de méthode est correctement implémenté. J'utilise cette implémentation depuis des années sans faute.
Awesome-o
Expliquez également ce qui est compliqué d'ajouter une base de code isolée d'environ 150 lignes pour résoudre en permanence un problème récurrent? Non seulement cela ne compliquera PAS votre base de code, mais cela fournira l'interface la plus simple possible; un seul booléen pour dicter la fonctionnalité.
Awesome-o
jetez un oeil à cette excellente discussion sur la méthode swizzling stackoverflow.com/questions/5339276/... Surtout sur le débogage. Le swizzling de méthode est une fonctionnalité étonnante, mais IMHO ne doit être utilisé qu'en cas de besoin. Ce problème est facile à résoudre en utilisant l'une des suggestions fournies ici, et fournira un code plus facile à maintenir et à lire pour les autres programmeurs.
EsbenB
J'ai lu cela avant et j'ai suivi toutes les directives décrites dans ma mise en œuvre pour l'exactitude. On peut soutenir que c'est un exemple solide de quand la méthode swizzling l'emporte. C'est un cas isolé mais courant où les bibliothèques de base ne parviennent pas à implémenter de manière simple une fonctionnalité largement nécessaire sur toutes les plates-formes. Les autres exemples suggérés ne définissent pas sémantiquement un champ de texte sans curseur, ils ne l'exécutent que par obfuscation du cadre du curseur ou de sa couleur. Pour un nouveau programmeur, cette interface est la plus simple à lire et fait exactement ce que l'on attend d'elle.
Awesome-o