Ocultar o cursor de um UITextField

137

Estou usando um UITextFieldcom um UIPickerViewpara inputView, para que, quando o usuário tocar no campo de texto, um seletor seja convocado para que ele selecione uma opção.

Quase tudo funciona, mas eu tenho um problema: o cursor ainda pisca no campo de texto quando está ativo, o que é feio e inapropriado, pois o usuário não deve digitar no campo e não recebe um teclado. Eu sei que eu poderia resolver isso hackily definindo editingpara NOno campo de texto e acompanhando os toques nele, ou substituindo-o por um botão com estilo personalizado e convocando o selecionador por código. No entanto, quero usar os UITextFieldDelegatemétodos para toda a manipulação de eventos no campo de texto e hacks, como substituir o campo de texto por um botão, não permitem essa abordagem.

Como posso simplesmente ocultar o cursor no UITextFieldlugar?

Montenegro
fonte

Respostas:

277

Simplesmente subclasse UITextField e substitua caretRectForPosition

- (CGRect)caretRectForPosition:(UITextPosition *)position
{
    return CGRectZero;
}
Joseph Chiu
fonte
2
Lindo! Funciona como um encanto para mim.
21813 Joe Strout
1
@ Joseph Chiu Funciona muito bem. Mas não com o iOS 4.3. Você poderia me ajudar com isso?
Dinesh Raja
a abordagem mais limpo e mais curto que merece um duplo upvote =)
Ilker Baltaci
1
Apenas uma nota. Na minha subclasse, adicionei um hideCaret booleano e, nessa substituição, se for verdade -> return CGRectZero else retornará o resultado de super.
precisa saber é o seguinte
6
Lembre-se de que os usuários com um teclado externo podem alterar o valor do campo de texto, mesmo se o cursor estiver oculto e você usar uma exibição de selecionador.
Mark
156

A partir do iOS 7, agora você pode simplesmente definir o tintColor = [UIColor clearColor]campo de texto e o cursor desaparecerá.

jamone
fonte
1
Isso funciona no momento, no entanto, eu desaconselharia usá-lo, pois pode mudar no futuro. Em vez disso, opte pela caretRectForPosition:solução de substituição.
lipka
@ lipka True, que é provavelmente uma maneira melhor.
jamone
2
Bom o suficiente por enquanto. Às vezes você só precisa de uma solução rápida.
GoldenJoe
Esta é a solução mais fácil de longe!
Jay Q.
1
Esta resposta deve ser aprovada para iOS 7 ou superior
tryp
95

Você pode simplesmente limpar a tonalidade do campo de texto

self.textField.tintColor = [UIColor clearColor];

Swift 3.0

self.textField.tintColor = .clear

insira a descrição da imagem aqui

Velhote
fonte
Melhor resposta mais fácil.
Nik Kov
2
Como mencionado acima, a limpeza da cor da tonalidade não impede que os usuários com teclados externos (iPad Pro) alterem o texto.
Michael Long
Não se trata de impedir que o usuário altere o texto, é apenas uma opção de estilo.
JRam13 15/06
21

Você também pode impedir o usuário de selecionar, copiar ou colar qualquer texto, para que a única entrada de texto venha da visualização do seletor.

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

Confira a propriedade selectedTextRange do protocolo UITextInput , ao qual a classe UITextField está em conformidade. Poucos! Essa é uma lição de programação orientada a objetos.

Ocultar sinal de intercalação

Para ocultar o cursor, anule o intervalo de texto selecionado do campo de texto.

textField.selectedTextRange = nil; // hides caret

Mostrar o sinal de intercalação

Aqui estão duas maneiras de mostrar o sinal de intercalação.

  1. Defina o intervalo de texto selecionado do campo de texto para o final do documento.

    UITextPosition *end = textField.endOfDocument;
    textField.selectedTextRange = [textField textRangeFromPosition:end
                                                        toPosition:end];
  2. Para manter o cursor no mesmo local, primeiro armazene o intervalo de texto selecionado do campo de texto em uma variável de instância.

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

    Em seguida, quando desejar exibir o sinal de intercalação, basta definir o intervalo de texto selecionado do campo de texto de volta ao que era originalmente:

    textField.selectedTextRange     = _textFieldSelectedTextRange;
    _textFieldLastSelectedTextRange = nil;
ma11hew28
fonte
3
Esta solução específica não funcionou para minha implementação. O cursor ainda pisca.
Art Geigel
Bem, então, talvez você deva registrar um bug em bugreport.apple.com porque os documentos do iOS dizem: "Se o intervalo de texto tiver um comprimento, ele indica o texto selecionado no momento. Se tiver um comprimento zero, indica o sinal de intercalação (inserção ponto). Se o objeto do intervalo de texto for nulo, isso indica que não há uma seleção atual. "
ma11hew28
4
Não ligo o suficiente para registrar um relatório. Se outros usam sua "solução" e não a veem funcionando, eu queria que eles soubessem que não estão sozinhos.
Art Geigel
Apesar dos comentários de @ ArtGeigel, isso funciona perfeitamente para mim. No entanto, eu meio que prefiro a solução que envolve a substituição caretRectForPosition. É mais explícito o que está fazendo, e os documentos que você citou não deixam claro qual deve ser o comportamento do sinal de intercalação quando não há 'seleção atual'. Se a afirmação de @ ArtGeigel de que isso não funciona estava correta (o que não é, pelo menos até onde eu posso ver), não ficaria claro que isso foi um bug.
Mark Amery
Também não funcionou para mim. Caret ainda está lá e piscando.
CW0007007
11

A resposta fornecida pelo OP foi copiada do corpo da pergunta para ajudar a limpar a cauda cada vez maior das perguntas sem resposta.

Encontrei outra solução: subclasse UIButtone substitua esses métodos

- (UIView *)inputView {
    return inputView_;
}

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

- (BOOL)canBecomeFirstResponder {
    return YES;
}

Agora, o botão, como a UIResponder, tem um comportamento semelhante UITextFielde uma implementação bastante simples.

Robert Höglund
fonte
5
Esta não é realmente uma ótima solução. Verifique esta resposta abaixo: stackoverflow.com/a/13660503/1103584
DiscDev
2
Por que essa não é uma ótima solução? Consegue o efeito pretendido e também fornece funcionalidade a uma classe que anteriormente não o possuía. Também não é um hack. Eu acho muito legal. Então, o que há de errado nisso?
precisa saber é o seguinte
2
@BreadicalMD O maior problema que vejo é que você não pode usar um UITextFieldDelegatecom isso para manipular eventos de edição inicial e final. Em vez disso - a menos que haja uma maneira de lidar com os eventos que eu não conheço - você precisará substituir becomeFirstRespondere resignFirstResponderna subclasse de botão e possivelmente criar seu próprio protocolo de delegado, adicionar uma delegatepropriedade e chamar o delegado do mencionado métodos. Isso é muito mais trabalho do que apenas substituir caretRectForPositionuma UITextFieldsubclasse.
Mark Amery
1
@BreadicalMD Dito isto, apesar de a resposta de Joseph Chiu ser superior do ponto de vista prático, eu ainda concordo com você que isso é muito sexy. Eu nunca tinha olhado atentamente para a UIResponderreferência da turma antes e não tinha ideia de que um truque como esse fosse possível.
Mark Amery
Tarde para a festa, mas acho que essa é uma solução muito boa e parece muito mais apropriada e menos invasiva do que usar UITextFieldae ocultar o cursor, que é basicamente uma invasão, pois estamos usando o campo de texto como um rótulo enquanto não usando nenhuma das funcionalidades do UITextField.
Rupert
7

Versão Swift 5 da publicação 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.
fonte
No meu caso, funcionou. Adicionada uma subclasse de UITextField com esses métodos usando o Swift 4. Obrigado!
J. Fdez
3

defina a tintColor para Clear Color

textfield.tintColor = [UIColor clearColor];

e você também pode definir no construtor de interface

Ahmad Al-Attal
fonte
2
Você acabou de copiar uma resposta de mais de 2 anos atrás.
Ashley Mills
1
desculpe meu amigo, mas não copiei nada.
Ahmad Al-Attal 15/10
4
Isso é interessante, "meu amigo". Sua resposta parece bem próxima da resposta do @ oldman de 1º de maio de 15. Você pode me dizer como a sua é diferente?
Ashley Mills
1
Como mencionado acima, a limpeza da cor da tonalidade não impede que os usuários com teclados externos (iPad Pro) alterem o texto.
Michael Long
Os usuários profissionais podem fazer o que quiserem. Eles são profissionais: eles sabem o que estão fazendo; ^)
Anton Tropashko
2

Se você deseja ocultar o cursor, pode usá-lo facilmente! Funcionou para mim ..

[[textField valueForKey:@"textInputTraits"] setValue:[UIColor clearColor] forKey:@"insertionPointColor"]
Göktuğ Aral
fonte
3
Isso não está documentado, até onde eu sei. É possível que, usando isso, seu aplicativo seja rejeitado por chamar APIs privadas se você o enviar à loja de aplicativos, embora eu não conheça nenhum envio usando isso para testar essa especulação de uma maneira ou de outra. É uma pena, porque seria bom poder resolver esse problema sem subclassificar, e esta resposta permite isso.
Mark Amery
1

A resposta fornecida pelo OP foi copiada do corpo da pergunta para ajudar a limpar a cauda cada vez maior das perguntas sem resposta.

Acho que tenho a solução correta, mas se puder ser melhorada será bem-vinda :) Bem, fiz uma subclasse de UITextField e substituí o método que retorna o CGRect para os limites

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

O problema? O texto não aparece porque o ret é zero. Mas eu adicionei um UILabel como uma sub-visualização do controle e substituí o método setText. Assim, quando inserimos um texto normalmente, o texto do campo de texto é nulo e é o rótulo que mostra o texto

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

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

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

Com isso, a coisa funciona como esperado. Você conhece alguma maneira de melhorá-lo?

Robert Höglund
fonte
Essa resposta é basicamente inútil agora, já que a abordagem alternativa de Joseph Chiu é muito semelhante, mas muito mais simples. Posso sugerir apenas excluí-lo?
Mark Amery
1

Para desativar o cursor e o menu, uso a subclasse com estes 2 métodos:

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

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

    return NO;
}
Vive
fonte
0

Eu simplesmente subclasse UITextFielde substituo da layoutSubviewsseguinte forma:

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

É uma invasão suja e pode falhar no futuro (quando o cursor estiver visível novamente - seu aplicativo não trava), mas funciona.

Mark Beaton
fonte
-1

Você pode adicionar uma BOOL cursorlesspropriedade a UITextFielduma categoria por meio de objetos associados.

@interface UITextField (Cursorless)

@property (nonatomic, assign) BOOL cursorless;

@end

Em seguida, use swizzling de método para swizzle caretRectForPosition:com um método que alterna entre CGRectZeroe seu valor padrão usando cursorless.

Isso leva a uma interface simples por meio de uma categoria suspensa. Isso é demonstrado nos seguintes arquivos.

Basta soltá-los e obter o benefício dessa interface simples

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

Método 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

Awesome-o
fonte
uma má solução em tantos níveis! para iniciantes: nunca substitua métodos em categorias. Evite Swizziling de métodos, a menos que você realmente precise (outros criaram boas soluções para esta pergunta). Isso torna seu código muito mais complicado.
precisa saber é o seguinte
Se você realmente analisasse o código, perceberia que nenhum método é substituído na categoria e que o método swizzling foi implementado corretamente. Venho usando essa implementação há anos sem falhas.
Awesome-o
Além disso, explique o que é complicado em adicionar uma base de código isolada de ~ 150 linhas para resolver permanentemente um problema recorrente continuamente? Isso não apenas complicará sua base de código, mas também oferece a interface mais simples possível; um único booleano para ditar a funcionalidade.
Awesome-o
dê uma olhada nesta excelente discussão sobre o método swizzling stackoverflow.com/questions/5339276/… Especialmente na depuração. O método swizzling é um recurso incrível, mas o IMHO deve ser usado apenas quando necessário. Esse problema é fácil de resolver usando uma das sugestões fornecidas aqui e fornecerá código mais fácil de manter e ler para outros programadores.
precisa saber é o seguinte
Eu li isso antes e segui todas as diretrizes descritas em minha implementação para correção. Indiscutivelmente, este é um exemplo sólido de quando o método swizzling vence. É um caso isolado e comum de onde as bibliotecas básicas falham na implementação de um recurso amplamente necessário nas plataformas de uma maneira simples. Os outros exemplos sugeridos não definem semanticamente um campo de texto sem cursor, eles apenas o executam através da ofuscação do quadro do cursor ou de sua cor. Para um novo programador, essa interface é a mais fácil de ler e faz exatamente o que é esperado dela.
Awesome-o