Blenden Sie den Cursor eines UITextField aus

137

Ich benutze ein UITextFieldmit einemUIPickerView für sein inputView, so dass, wenn der Benutzer auf das Textfeld tippt, eine Auswahl aufgerufen wird, aus der er eine Option auswählen kann.

Fast alles funktioniert, aber ich habe ein Problem: Der Cursor blinkt immer noch im Textfeld, wenn er aktiv ist, was hässlich und unangemessen ist, da vom Benutzer nicht erwartet wird, dass er in das Feld tippt und keine Tastatur angezeigt wird. Ich weiß , ich könnte hackily dieses Problem lösen , indem Sie editingauf NOauf dem Textfeld und Tracking berührt es, oder es mit einem individuell gestalteten Knopf ersetzt, und beschwören der Picker über Code. Ich möchte jedoch die UITextFieldDelegateMethoden für die gesamte Ereignisbehandlung im Textfeld verwenden, und Hacks wie das Ersetzen des Textfelds durch eine Schaltfläche erlauben diesen Ansatz nicht.

Wie kann ich UITextFieldstattdessen einfach den Cursor auf dem verstecken ?

emenegro
quelle

Antworten:

277

Unterklassen Sie einfach UITextField und überschreiben Sie caretRectForPosition

- (CGRect)caretRectForPosition:(UITextPosition *)position
{
    return CGRectZero;
}
Joseph Chiu
quelle
2
Wunderschönen! Funktioniert wie ein Zauber für mich.
Joe Strout
1
@ Joseph Chiu Es funktioniert großartig. Aber nicht mit iOS 4.3. Könnten Sie mir dabei helfen?
Dinesh Raja
der sauberste und kürzeste Ansatz, der eine doppelte Gegenstimme verdient =)
Ilker Baltaci
1
Nur eine Notiz. In meiner Unterklasse habe ich ein bool hideCaret hinzugefügt und dann in dieser Überschreibung. Wenn es wahr ist -> CGRectZero zurückgeben, sonst das Ergebnis von super zurückgeben.
WCByrne
6
Beachten Sie jedoch, dass Benutzer mit einer externen Tastatur den Wert des Textfelds ändern können, auch wenn der Cursor ausgeblendet ist und Sie eine Auswahlansicht verwenden.
Mark
156

Ab iOS 7 können Sie jetzt einfach tintColor = [UIColor clearColor]das Textfeld einstellen und das Caret verschwindet.

Jamone
quelle
1
Dies funktioniert im Moment, ich würde jedoch davon abraten, es zu verwenden, da es sich in Zukunft ändern könnte. Entscheiden Sie sich lieber für die caretRectForPosition:Override-Lösung.
Lipka
@ Lipka Stimmt, das ist wahrscheinlich ein besserer Weg.
Jamone
2
Gut genug für jetzt. Manchmal brauchen Sie nur eine schnelle Lösung.
GoldenJoe
Dies ist bei weitem die einfachste Lösung!
Jay Q.
1
Diese Antwort sollte für iOS 7+
Tryp
95

Sie können einfach die tintColor des Textfelds löschen

self.textField.tintColor = [UIColor clearColor];

Swift 3.0

self.textField.tintColor = .clear

Geben Sie hier die Bildbeschreibung ein

alter Mann
quelle
Beste einfachste Antwort.
Nik Kov
2
Wie oben erwähnt, hindert das Löschen der Farbtonfarbe Benutzer mit externen Tastaturen (iPad Pro) nicht daran, den Text zu ändern.
Michael Long
@MichaelLong geht es nicht darum, den Benutzer daran zu hindern, den Text zu ändern, sondern nur um die Wahl des Stils.
JRam13
21

Möglicherweise möchten Sie den Benutzer auch daran hindern, Text auszuwählen, zu kopieren oder einzufügen, damit die einzige Texteingabe aus der Auswahlansicht stammt.

- (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
quelle
15

Überprüfen Sie die Eigenschaft selectedTextRange des Protokolls UITextInput , dem die Klasse UITextField entspricht. Wenige! Das ist genau dort eine Lektion in objektorientierter Programmierung.

Caret verstecken

Um das Caret auszublenden, deaktivieren Sie den ausgewählten Textbereich des Textfelds.

textField.selectedTextRange = nil; // hides caret

Caret einblenden

Hier sind zwei Möglichkeiten, um das Caret einzublenden.

  1. Stellen Sie den ausgewählten Textbereich des Textfelds auf das Ende des Dokuments ein.

    UITextPosition *end = textField.endOfDocument;
    textField.selectedTextRange = [textField textRangeFromPosition:end
                                                        toPosition:end];
  2. Um das Caret an derselben Stelle zu halten, speichern Sie zunächst den ausgewählten Textbereich des Textfelds in einer Instanzvariablen.

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

    Wenn Sie das Caret einblenden möchten, setzen Sie einfach den ausgewählten Textbereich des Textfelds auf den ursprünglichen Wert zurück:

    textField.selectedTextRange     = _textFieldSelectedTextRange;
    _textFieldLastSelectedTextRange = nil;
ma11hew28
quelle
3
Diese spezielle Lösung hat bei meiner Implementierung nicht funktioniert. Der Cursor blinkt immer noch.
Art Geigel
Nun, vielleicht sollten Sie einen Fehler unter bugreport.apple.com melden, da in den iOS-Dokumenten Folgendes steht : "Wenn der Textbereich eine Länge hat, zeigt er den aktuell ausgewählten Text an. Wenn er eine Länge von Null hat, zeigt er das Caret an (Einfügen) Punkt). Wenn das Textbereichsobjekt Null ist, zeigt dies an, dass keine aktuelle Auswahl vorhanden ist. "
ma11hew28
4
Es ist mir nicht wichtig genug, einen Bericht einzureichen. Wenn andere Ihre "Lösung" verwenden und nicht sehen, dass sie funktioniert, wollte ich, dass sie wissen, dass sie nicht allein sind.
Art Geigel
Trotz der Kommentare von @ ArtGeigel funktioniert dies perfekt für mich. Ich bevorzuge jedoch die Lösung mit Überschreiben caretRectForPosition. Es ist expliziter, was es tut, und die von Ihnen zitierten Dokumente machen nicht klar, wie sich das Caret verhalten soll, wenn keine aktuelle Auswahl vorliegt. Wenn @ ArtGeigels Behauptung, dass dies nicht funktioniert, richtig wäre (was zumindest meines Erachtens nicht der Fall ist), wäre es nicht klar, dass dies ein Fehler wäre.
Mark Amery
Hat auch bei mir nicht funktioniert. Caret ist immer noch da und blinkt.
CW0007007
11

Die vom OP bereitgestellte Antwort wurde aus dem Fragenkörper kopiert, um den ständig wachsenden Schwanz unbeantworteter Fragen zu beseitigen.

Ich habe eine andere Lösung gefunden: Unterklasse UIButtonund überschreiben diese Methoden

- (UIView *)inputView {
    return inputView_;
}

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

- (BOOL)canBecomeFirstResponder {
    return YES;
}

Jetzt hat die Schaltfläche als ein UIResponderähnliches Verhalten wie UITextFieldund eine Implementierung ziemlich einfach.

Robert Höglund
quelle
5
Dies ist keine wirklich gute Lösung. Überprüfen Sie diese Antwort unten: stackoverflow.com/a/13660503/1103584
DiscDev
2
Warum ist das keine großartige Lösung? Es erzielt den beabsichtigten Effekt und bietet Funktionen für eine Klasse, die es zuvor nicht hatte. Es ist auch kein Hack. Ich finde es echt cool. Also, was ist daran falsch?
BreadicalMD
2
@BreadicalMD Das größte Problem, das ich sehen kann, ist, dass Sie a nicht verwenden UITextFieldDelegatekönnen, um Bearbeitungsereignisse zu beginnen und zu beenden. Stattdessen müssen Sie - es sei denn, es gibt eine Möglichkeit, mit Ereignissen umzugehen, von denen ich nichts weiß - überschreiben becomeFirstResponderund resignFirstResponderin der Schaltflächenunterklasse möglicherweise Ihr eigenes Delegatenprotokoll erstellen, eine delegateEigenschaft hinzufügen und den Delegaten über die oben genannten Elemente aufrufen Methoden. Dies alles ist weit mehr Arbeit als nur das Überschreiben caretRectForPositionin einer UITextFieldUnterklasse.
Mark Amery
1
@BreadicalMD Das heißt, obwohl Joseph Chius Antwort in praktischer Hinsicht überlegen ist, stimme ich Ihnen dennoch zu, dass dies ziemlich sexy ist. Ich hatte mir die UIResponderKlassenreferenz noch nie genau angesehen und hatte keine Ahnung, dass ein solcher Trick möglich war.
Mark Amery
Spät zur Party, aber ich denke, dies ist eine sehr gute Lösung und es fühlt sich viel angemessener und weniger hackig an, als UITextFieldden Cursor zu verwenden und zu verstecken, was im Grunde ein Hack ist, da wir dann das Textfeld als Beschriftung verwenden keine der Funktionen der UITextField.
Rupert
7

Swift 5-Version von Net's Post

  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.
quelle
In meinem Fall hat es funktioniert. Mit Swift 4 wurde eine Unterklasse von UITextField mit diesen Methoden hinzugefügt. Danke!
J. Fdez
3

Stellen Sie tintColor auf Clear Color

textfield.tintColor = [UIColor clearColor];

und Sie können auch über den Interface Builder festlegen

Ahmad Al-Attal
quelle
2
Sie haben gerade eine Antwort von vor über 2 Jahren kopiert.
Ashley Mills
1
Entschuldigung, mein Freund, aber ich habe nichts kopiert.
Ahmad Al-Attal
4
Das ist interessant, "mein Freund". Ihre Antwort kommt der Antwort von @ oldman vom 1. Mai 15 ziemlich nahe. Kannst du mir sagen, wie deine anders ist?
Ashley Mills
1
Wie oben erwähnt, hindert das Löschen der Farbtonfarbe Benutzer mit externen Tastaturen (iPad Pro) nicht daran, den Text zu ändern.
Michael Long
Die Pro-Benutzer können tun, was sie wollen. Sie sind Profis: Sie wissen, was sie tun; ^)
Anton Tropashko
2

Wenn Sie den Cursor ausblenden möchten, können Sie dies einfach verwenden! Es hat bei mir funktioniert ..

[[textField valueForKey:@"textInputTraits"] setValue:[UIColor clearColor] forKey:@"insertionPointColor"]
Göktuğ Aral
quelle
3
Soweit ich weiß, ist dies nicht dokumentiert. Es ist möglich, dass bei Verwendung dieser Option Ihre App für den Aufruf privater APIs abgelehnt wird, wenn Sie sie an den App Store senden. Ich kenne jedoch keine Übermittlungen, die diese verwenden, um diese Spekulation auf die eine oder andere Weise zu testen. Schade, denn es wäre schön, dieses Problem ohne Unterklassen lösen zu können, und diese Antwort ermöglicht dies.
Mark Amery
1

Die vom OP bereitgestellte Antwort wurde aus dem Fragenkörper kopiert, um den ständig wachsenden Schwanz unbeantworteter Fragen zu beseitigen.

Ich denke, ich habe die richtige Lösung, aber wenn sie verbessert werden kann, ist dies willkommen :) Nun, ich habe eine Unterklasse von UITextField erstellt und die Methode überschrieben, die CGRect für die Grenzen zurückgibt

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

Das Problem? Der Text wird nicht angezeigt, da der Rect Null ist. Aber ich habe ein UILabel als Unteransicht des Steuerelements hinzugefügt und die setText-Methode überschrieben. Wenn wir also wie gewohnt einen Text eingeben, ist der Textfeldtext null und die Bezeichnung, die den Text anzeigt

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

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

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

Damit funktioniert das Ding wie erwartet. Kennen Sie einen Weg, um es zu verbessern?

Robert Höglund
quelle
Diese Antwort ist jetzt im Grunde genommen wertlos, da Joseph Chius alternativer Ansatz sehr ähnlich, aber viel einfacher ist. Darf ich vorschlagen, es einfach zu löschen?
Mark Amery
1

Um sowohl den Cursor als auch das Menü zu deaktivieren, verwende ich die Unterklasse mit diesen beiden Methoden:

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

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

    return NO;
}
Vive
quelle
0

Ich unterteile einfach UITextFieldund überschreibe layoutSubviewswie folgt:

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

Es ist ein schmutziger Hack und kann in Zukunft fehlschlagen (an diesem Punkt ist der Cursor wieder sichtbar - Ihre App stürzt nicht ab), aber es funktioniert.

Mark Beaton
quelle
-1

Sie können einer Kategorie über zugeordnete Objekte eine BOOL cursorlessEigenschaft hinzufügen UITextField.

@interface UITextField (Cursorless)

@property (nonatomic, assign) BOOL cursorless;

@end

Verwenden Sie dann die Methode swizzling, um caretRectForPosition:mit einer Methode zu wechseln , die zwischen CGRectZeround ihrem Standardwert mit umschaltet cursorless.

Dies führt zu einer einfachen Schnittstelle über eine Drop-In-Kategorie. Dies wird in den folgenden Dateien demonstriert.

Legen Sie sie einfach ab und profitieren Sie von dieser einfachen Benutzeroberfläche

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

Methoden-Swizzling: https://github.com/rexmas/RexDK/blob/master/RexDK/Foundation/NSObject%2BRXRuntimeAdditions.h https://github.com/rexmas/RexDK/blob/master/RexDK/Foundation/NSObject% 2BRXRuntimeAdditions.m

Super-o
quelle
Eine schlechte Lösung auf so vielen Ebenen! für den Anfang: Überschreiben Sie niemals Methoden in Kategorien. Vermeiden Sie Method Swizziling, es sei denn, Sie brauchen es wirklich, wirklich (andere haben gute Lösungen für diese Frage gefunden). Es macht Ihren Code so viel komplizierter.
EsbenB
Wenn Sie sich den Code tatsächlich ansehen, werden Sie feststellen, dass in der Kategorie keine Methode überschrieben wird und das Methoden-Swizzling korrekt implementiert ist. Ich benutze diese Implementierung seit Jahren ohne Fehler.
Super-o
Erklären Sie auch, was beim Hinzufügen einer isolierten Codebasis von ~ 150 Zeilen kompliziert ist, um ein ständig wiederkehrendes Problem dauerhaft zu lösen. Dies kompliziert nicht nur NICHT Ihre Codebasis, sondern bietet auch die einfachste mögliche Schnittstelle. Ein einzelner Boolescher Wert, der die Funktionalität bestimmt.
-o
Werfen Sie einen Blick auf diese hervorragende Diskussion über das Swizzling von Methoden stackoverflow.com/questions/5339276/… Insbesondere beim Debuggen. Methoden-Swizzling ist eine erstaunliche Funktion, aber IMHO sollte nur bei Bedarf verwendet werden. Dieses Problem lässt sich mit einem der hier aufgeführten Vorschläge leicht lösen und bietet Code, der für andere Programmierer einfacher zu warten und zu lesen ist.
EsbenB
Ich habe das bereits gelesen und alle in meiner Implementierung beschriebenen Richtlinien zur Richtigkeit befolgt. Dies ist wohl ein gutes Beispiel dafür, wann das Methoden-Swizzling gewinnt. Es ist ein isolierter, aber häufiger Fall, in dem die Basisbibliotheken eine häufig benötigte Funktion nicht auf einfache Weise plattformübergreifend implementieren. Die anderen vorgeschlagenen Beispiele definieren ein cursorloses Textfeld nicht semantisch, sondern führen es nur durch Verschleierung des Rahmens des Cursors oder seiner Farbe aus. Für einen neuen Programmierer ist diese Oberfläche am einfachsten zu lesen und macht genau das, was von ihm erwartet wird.
Super-o