Ukryj kursor UITextField

137

Używam a UITextFieldz a UIPickerViewjako jego inputView, więc gdy użytkownik dotknie pola tekstowego, zostanie przywołany selektor, aby wybrać opcję.

Prawie wszystko działa, ale mam jeden problem: kursor nadal miga w polu tekstowym, gdy jest aktywny, co jest brzydkie i niewłaściwe, ponieważ użytkownik nie powinien pisać w polu i nie jest wyświetlany z klawiaturą. Wiem, że mógłbym hackily rozwiązać ten problem przez ustawienie editingsię NOna polu tekstowym i śledzenia akcenty na nim, lub poprzez zastąpienie go za pomocą przycisku niestandardowego stylu i wzywanie próbnika poprzez kod. Jednak chcę użyć UITextFieldDelegatemetod do obsługi wszystkich zdarzeń w polu tekstowym, a hacki, takie jak zamiana pola tekstowego na przycisk, nie pozwalają na to podejście.

Jak mogę po prostu ukryć kursor UITextFieldzamiast tego?

czarnogóra
źródło

Odpowiedzi:

277

Po prostu podklasuj UITextField i nadpisz caretRectForPosition

- (CGRect)caretRectForPosition:(UITextPosition *)position
{
    return CGRectZero;
}
Joseph Chiu
źródło
2
Piękny! U mnie działa jak urok.
Joe Strout
1
@Joseph Chiu To działa świetnie. Ale nie z iOS 4.3. Czy mógłbyś mi z tym pomóc?
Dinesh Raja
najczystsze i najkrótsze podejście, które zasługuje na podwójne uznanie =)
Ilker Baltaci
1
Tylko uwaga. W mojej podklasie dodałem bool hideCaret, a następnie w tym nadpisaniu Jeśli to prawda -> return CGRectZero w przeciwnym razie zwróć wynik super.
WCByrne,
6
Pamiętaj tylko, że użytkownicy z klawiaturą zewnętrzną mogą zmieniać wartość pola tekstowego, nawet jeśli kursor jest ukryty i używasz widoku selektora.
Mark
156

Od iOS 7 możesz teraz po prostu ustawić tintColor = [UIColor clearColor]na textField, a daszek zniknie.

jamone
źródło
1
W tej chwili to działa, jednak odradzałbym jego używanie, ponieważ może się to zmienić w przyszłości. Zamiast tego zdecyduj się na caretRectForPosition:rozwiązanie zastępcze.
lipka
@lipka Prawda, to prawdopodobnie lepszy sposób.
jamone
2
Na razie wystarczy. Czasami potrzebujesz tylko szybkiego rozwiązania.
GoldenJoe
To zdecydowanie najłatwiejsze rozwiązanie!
Jay Q.
1
Ta odpowiedź powinna zostać zatwierdzona dla iOS 7+
tryp
95

Możesz po prostu wyczyścić tintColor pola tekstowego

self.textField.tintColor = [UIColor clearColor];

Swift 3.0

self.textField.tintColor = .clear

wprowadź opis obrazu tutaj

starzec
źródło
Najlepsza, najłatwiejsza odpowiedź.
Nik Kov,
2
Jak wspomniano powyżej, wyczyszczenie koloru odcienia nie powstrzymuje użytkowników korzystających z zewnętrznych klawiatur (iPad Pro) przed zmianą tekstu.
Michael Long
@MichaelLong nie chodzi o powstrzymanie użytkownika przed zmianą tekstu, to tylko wybór stylu.
JRam13
21

Możesz również chcieć uniemożliwić użytkownikowi wybieranie, kopiowanie lub wklejanie dowolnego tekstu, tak aby jedyne dane wejściowe pochodziły z widoku selektora.

- (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/disiring-the-caret-and-text-entry-in-uitextfields/

Nat
źródło
15

Sprawdź właściwość selectedTextRange tego protokołu UITextInput , do której klasy UITextField są zgodne. Mało! To lekcja programowania obiektowego.

Ukryj Caret

Aby ukryć daszek, zeruj zaznaczony zakres tekstu pola tekstowego.

textField.selectedTextRange = nil; // hides caret

Odkryj Caret

Oto dwa sposoby odkrywania daszka.

  1. Ustaw zakres tekstu zaznaczonego pola tekstowego na koniec dokumentu.

    UITextPosition *end = textField.endOfDocument;
    textField.selectedTextRange = [textField textRangeFromPosition:end
                                                        toPosition:end];
  2. Aby zachować daszek w tym samym miejscu, najpierw zapisz zakres tekstu zaznaczonego pola tekstowego w zmiennej instancji.

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

    Następnie, gdy chcesz odkryć daszek, po prostu ustaw zaznaczony zakres tekstu pola tekstowego z powrotem na pierwotny:

    textField.selectedTextRange     = _textFieldSelectedTextRange;
    _textFieldLastSelectedTextRange = nil;
ma11hew28
źródło
3
To konkretne rozwiązanie nie zadziałało w mojej implementacji. Kursor nadal miga.
Art Geigel
Cóż, więc może powinieneś zgłosić błąd na bugreport.apple.com, ponieważ dokumentacja iOS mówi: „Jeśli zakres tekstu ma długość, wskazuje aktualnie zaznaczony tekst. Jeśli ma zerową długość, wskazuje daszek (wstawienie punkt). Jeśli obiekt zakresu tekstu ma wartość zero, oznacza to, że nie ma bieżącego zaznaczenia. "
ma11hew 28
4
Nie obchodzi mnie na tyle, żeby złożyć raport. Jeśli inni używają twojego „rozwiązania” i nie widzą, że działa, chcę, żeby wiedzieli, że nie są sami.
Art Geigel
Pomimo komentarzy @ ArtGeigel, to działa dla mnie idealnie. Jednak wolę rozwiązanie polegające na nadpisywaniu caretRectForPosition. Jest bardziej jasne, co robi, a dokumenty, które zacytowałeś, nie wyjaśniają, jakie powinno być zachowanie karetki, gdy nie ma aktualnego wyboru. Gdyby twierdzenie @ ArtGeigel, że to nie działa, było poprawne (co nie jest, przynajmniej o ile widzę), nie byłoby jasne, że to był błąd.
Mark Amery,
Dla mnie też nie działało. Caret wciąż tam jest i mruga.
CW0007007
11

Odpowiedź udzielona przez PO, skopiowana z części pytań, aby pomóc uporać się z coraz większą liczbą pytań bez odpowiedzi.

Znalazłem inne rozwiązanie: podklasę UIButtoni zastąpienie tych metod

- (UIView *)inputView {
    return inputView_;
}

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

- (BOOL)canBecomeFirstResponder {
    return YES;
}

Teraz przycisk jako a UIResponderzachowuje się podobnie, UITextFielda implementacja jest całkiem prosta.

Robert Höglund
źródło
5
To naprawdę nie jest świetne rozwiązanie. Sprawdź tę odpowiedź poniżej: stackoverflow.com/a/13660503/1103584
DiscDev
2
Dlaczego nie jest to świetne rozwiązanie? Osiąga zamierzony efekt, a także zapewnia funkcjonalność klasie, która wcześniej jej nie miała. To też nie jest hack. Myślę, że to naprawdę fajne. Więc co w tym złego?
BreadicalMD
2
@BreadicalMD Największym problemem, jaki widzę, jest to, że nie można użyć UITextFieldDelegatez tym do obsługi zdarzeń rozpoczęcia i zakończenia edycji. Zamiast tego - chyba że istnieje sposób na obsłużenie tych zdarzeń, o których nie wiem - musisz nadpisać becomeFirstResponderi resignFirstResponderw podklasie przycisku i ewentualnie utworzyć własny protokół delegata, dodać delegatewłaściwość i wywołać delegata z wyżej wymienionej metody. To o wiele więcej pracy niż tylko nadpisywanie caretRectForPositionw UITextFieldpodklasie.
Mark Amery,
1
@BreadicalMD To powiedziawszy, mimo że odpowiedź Josepha Chiu jest lepsza z praktycznego punktu widzenia, nadal zgadzam się z tobą, że jest to całkiem sexy. Nigdy wcześniej nie przyjrzałem się uważnie UIResponderodwołaniom do klasy i nie miałem pojęcia, że ​​taka sztuczka jest możliwa.
Mark Amery,
Spóźniłem się na imprezę, ale myślę, że jest to bardzo dobre rozwiązanie i wydaje się o wiele bardziej odpowiednie i mniej hakerskie niż użycie UITextFieldi ukrycie kursora, co jest w zasadzie hackowaniem, ponieważ używamy wtedy pola tekstowego jako etykiety podczas nieużywanie żadnej z funkcji UITextField.
Rupert
7

Swift 5 wersja posta 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.
źródło
W moim przypadku zadziałało. Dodano podklasę UITextField z tymi metodami przy użyciu języka Swift 4. Dzięki!
J. Fdez
3

ustaw tintColor na Clear Color

textfield.tintColor = [UIColor clearColor];

i możesz również ustawić w konstruktorze interfejsu

Ahmad Al-Attal
źródło
2
Właśnie skopiowałeś odpowiedź sprzed ponad 2 lat.
Ashley Mills
1
przepraszam przyjacielu, ale niczego nie skopiowałem.
Ahmad Al-Attal
4
To ciekawe, „mój przyjacielu”. Twoja odpowiedź jest bardzo zbliżona do odpowiedzi @ oldman z 1 maja 2015 roku. Czy możesz mi powiedzieć, czym się różni?
Ashley Mills,
1
Jak wspomniano powyżej, wyczyszczenie koloru odcienia nie powstrzymuje użytkowników korzystających z zewnętrznych klawiatur (iPad Pro) przed zmianą tekstu.
Michael Long
Profesjonalni użytkownicy mogą robić, co im się podoba. Są profesjonalistami: wiedzą, co robią; ^)
Anton Tropashko
2

Jeśli chcesz ukryć kursor, możesz z łatwością tego użyć! U mnie zadziałało ...

[[textField valueForKey:@"textInputTraits"] setValue:[UIColor clearColor] forKey:@"insertionPointColor"]
Göktuğ Aral
źródło
3
O ile wiem, to nie jest udokumentowane. Możliwe, że użycie tej opcji spowoduje odrzucenie aplikacji za wywoływanie prywatnych interfejsów API, jeśli prześlesz ją do sklepu z aplikacjami, chociaż nie znam żadnych zgłoszeń wykorzystujących to do przetestowania tej spekulacji w taki czy inny sposób. Szkoda, bo fajnie byłoby móc rozwiązać ten problem bez podklas, a ta odpowiedź na to pozwala.
Mark Amery,
1

Odpowiedź udzielona przez PO, skopiowana z części pytań, aby pomóc uporać się z coraz większą liczbą pytań bez odpowiedzi.

Myślę, że mam poprawne rozwiązanie, ale jeśli da się je ulepszyć, będę mile widziany :) No cóż, zrobiłem podklasę UITextField i nadpisałem metodę, która zwraca CGRect dla granic

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

Problem? Tekst nie jest wyświetlany, ponieważ prostokąt ma wartość zero. Ale dodałem UILabel jako podwidok kontrolki i nadpisałem metodę setText, więc kiedy wpisujemy tekst jak zwykle, tekst pola tekstowego jest zerowy i jest etykietą, która pokazuje tekst

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

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

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

Dzięki temu rzecz działa zgodnie z oczekiwaniami. Czy znasz jakiś sposób, aby to poprawić?

Robert Höglund
źródło
Ta odpowiedź jest teraz w zasadzie bezwartościowa, biorąc pod uwagę, że alternatywne podejście Josepha Chiu jest bardzo podobne, ale znacznie prostsze. Czy mogę zasugerować usunięcie go?
Mark Amery,
1

Aby wyłączyć kursor i menu, używam podklasy z tymi 2 metodami:

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

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

    return NO;
}
Vive
źródło
0

Po prostu podklasuję UITextFieldi nadpisuję layoutSubviewsw następujący sposób:

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

To brudny hack i może się nie powieść w przyszłości (w tym momencie kursor będzie ponownie widoczny - Twoja aplikacja nie ulegnie awarii), ale działa.

Mark Beaton
źródło
-1

Możesz dodać BOOL cursorlesswłaściwość do UITextFieldkategorii poprzez skojarzone obiekty.

@interface UITextField (Cursorless)

@property (nonatomic, assign) BOOL cursorless;

@end

Następnie użyj swizzling metody, aby przesunąć się caretRectForPosition:za pomocą metody, która przełącza się między CGRectZeroi jej wartością domyślną za pomocą cursorless.

Prowadzi to do prostego interfejsu za pośrednictwem rozwijanej kategorii. Pokazują to poniższe pliki.

Po prostu wrzuć je i skorzystaj z tego prostego interfejsu

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

Metoda 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

Niesamowite
źródło
złe rozwiązanie na TAK wielu poziomach! na początek: Nigdy nie nadpisuj metod w kategoriach. Unikaj Swizziling Method, chyba że naprawdę tego potrzebujesz (inni wymyślili dobre rozwiązania na to pytanie). To sprawia, że ​​twój kod jest o wiele bardziej skomplikowany.
EsbenB
Jeśli faktycznie spojrzałeś na kod, zdałeś sobie sprawę, że żadna metoda nie jest nadpisywana w kategorii, a swizzling metod jest poprawnie zaimplementowany. Korzystam z tej implementacji od lat bezbłędnie.
Awesome-o
Wyjaśnij również, co jest skomplikowane w dodawaniu izolowanej bazy kodu zawierającej ~ 150 linii, aby trwale rozwiązać powtarzający się problem? Nie tylko NIE komplikuje to twojej bazy kodu, ale także zapewnia najprostszy możliwy interfejs; pojedyncza wartość logiczna, która dyktuje funkcjonalność.
Awesome-o
spójrz na tę doskonałą dyskusję na temat zamiany metod stackoverflow.com/questions/5339276/ ... Zwłaszcza o debugowaniu. Swizzling metod to niesamowita funkcja, ale IMHO powinno być używane tylko w razie potrzeby. Ten problem można łatwo rozwiązać za pomocą jednej z podanych tutaj sugestii i zapewni kod łatwiejszy do utrzymania i czytania dla innych programistów.
EsbenB
Przeczytałem to już wcześniej i postępowałem zgodnie ze wszystkimi wytycznymi podanymi w mojej implementacji pod kątem poprawności. Zapewne jest to solidny przykład tego, kiedy wygrywa zawijanie metod. Jest to odizolowany, ale powszechny przypadek, w którym podstawowe biblioteki nie potrafią w prosty sposób zaimplementować szeroko potrzebnej funkcji na różnych platformach. Inne sugerowane przykłady nie definiują semantycznie pola tekstowego bez kursora, wykonują je jedynie poprzez zaciemnianie ramki kursora lub jej koloru. Dla nowego programisty ten interfejs jest najłatwiejszy do odczytania i robi dokładnie to, czego się od niego oczekuje.
Awesome-o