Ocultar el cursor de un UITextField

137

Estoy usando a UITextFieldcon a UIPickerViewpara su inputView, de modo que cuando el usuario toca el campo de texto, se convoca a un selector para que seleccione una opción.

Casi todo funciona, pero tengo un problema: el cursor todavía parpadea en el campo de texto cuando está activo, lo cual es feo e inapropiado, ya que no se espera que el usuario escriba en el campo y no se le presente un teclado. Sé que hackily podía resolver esto mediante la creación editingde NOen el campo de texto y el seguimiento de toques en él, o sustituyéndolo por un botón personalizado de estilo, y llamando al selector a través de código. Sin embargo, quiero usar los UITextFieldDelegatemétodos para todo el manejo de eventos en el campo de texto y los hacks como reemplazar el campo de texto con un botón no permiten este enfoque.

¿Cómo puedo simplemente ocultar el cursor en el UITextFieldlugar?

emenegro
fuente

Respuestas:

277

Simplemente subclase UITextField y anule caretRectForPosition

- (CGRect)caretRectForPosition:(UITextPosition *)position
{
    return CGRectZero;
}
Joseph Chiu
fuente
2
¡Hermoso! Funciona como un encanto para mí.
Joe Strout
1
@Joseph Chiu Funciona muy bien. Pero no con iOS 4.3. ¿Podrías ayudarme con esto?
Dinesh Raja
el enfoque más limpio y corto que merece un doble voto positivo =)
Ilker Baltaci
1
Solo una nota. En mi subclase, agregué un bool hideCaret y luego en esta anulación. Si es cierto -> return CGRectZero else return el resultado de super.
WCByrne
66
Solo tenga en cuenta que los usuarios con un teclado externo pueden cambiar el valor del campo de texto incluso si el cursor está oculto y utiliza una vista de selector.
Mark
156

A partir de iOS 7, ahora puede configurarlo tintColor = [UIColor clearColor]en textField y el cursor desaparecerá.

jamone
fuente
1
Esto funciona en este momento, sin embargo, recomendaría no usarlo, ya que podría cambiar en el futuro. Más bien opte por la caretRectForPosition:solución de anulación.
lipka
@lipka Cierto, esa es probablemente una mejor manera.
jamone
2
Lo suficientemente bueno por ahora. A veces solo necesitas una solución rápida.
GoldenJoe
¡Esta es la solución más fácil con diferencia!
Jay Q.
1
Esta respuesta debe ser aprobada para iOS 7+
tryp
95

Simplemente puede borrar el tinteColor del campo de texto

self.textField.tintColor = [UIColor clearColor];

Swift 3.0

self.textField.tintColor = .clear

ingrese la descripción de la imagen aquí

anciano
fuente
La mejor respuesta más fácil.
Nik Kov
2
Como se mencionó anteriormente, borrar el color del tinte no impide que los usuarios con teclados externos (iPad Pro) cambien el texto.
Michael Long
@Michael A lo largo de esto no se trata de evitar que el usuario cambie el texto, es solo una elección de estilo.
JRam13
21

También es posible que desee evitar que el usuario seleccione, copie o pegue cualquier texto para que la única entrada de texto provenga de la vista del selector.

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

Consulte la propiedad selectedTextRange del protocolo UITextInput al que se ajusta la clase UITextField . ¡Pocos! Esa es una lección de programación orientada a objetos allí mismo.

Hide Caret

Para ocultar el cursor, nula el rango de texto seleccionado del campo de texto.

textField.selectedTextRange = nil; // hides caret

Mostrar Caret

Aquí hay dos formas de mostrar el caret.

  1. Establezca el rango de texto seleccionado del campo de texto al final del documento.

    UITextPosition *end = textField.endOfDocument;
    textField.selectedTextRange = [textField textRangeFromPosition:end
                                                        toPosition:end];
  2. Para mantener el cursor en el mismo lugar, primero, almacene el rango de texto seleccionado del campo de texto en una variable de instancia.

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

    Luego, cuando desee mostrar el cursor, simplemente configure el rango de texto seleccionado del campo de texto de nuevo a lo que era originalmente:

    textField.selectedTextRange     = _textFieldSelectedTextRange;
    _textFieldLastSelectedTextRange = nil;
ma11hew28
fuente
3
Esta solución particular no funcionó para mi implementación. El cursor todavía parpadea.
Art Geigel
Bueno, entonces, tal vez debería presentar un error en bugreport.apple.com porque los documentos de iOS dicen: "Si el rango de texto tiene una longitud, indica el texto seleccionado actualmente. Si tiene una longitud cero, indica el cursor (inserción punto). Si el objeto de rango de texto es nulo, indica que no hay una selección actual ".
ma11hew28
44
No me importa lo suficiente como para presentar un informe. Si otros usan su "solución" y no la ven funcionando, quiero que sepan que no están solos.
Art Geigel
A pesar de los comentarios de @ ArtGeigel, esto funciona perfectamente para mí. Sin embargo, prefiero la solución que implica anular caretRectForPosition. Es más explícito lo que está haciendo, y los documentos que ha citado no aclaran cuál debería ser el comportamiento del cursor cuando "no hay una selección actual". Si la afirmación de @ ArtGeigel de que esto no funciona fuera correcta (que no lo es, al menos por lo que puedo ver), no estaría claro que eso fuera un error.
Mark Amery
Tampoco funcionó para mí. Caret todavía está allí y parpadea.
CW0007007
11

Respuesta proporcionada por el OP, copiada del cuerpo de la pregunta para ayudar a limpiar la cola cada vez mayor de preguntas sin respuesta.

Encontré otra solución: subclase UIButtony anular estos métodos

- (UIView *)inputView {
    return inputView_;
}

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

- (BOOL)canBecomeFirstResponder {
    return YES;
}

Ahora el botón, como a UIResponder, tiene un comportamiento similar UITextFieldy una implementación bastante sencilla.

Robert Höglund
fuente
55
Esta no es realmente una gran solución. Mira esta respuesta a continuación: stackoverflow.com/a/13660503/1103584
DiscDev
2
¿Por qué no es esta una gran solución? Alcanza el efecto deseado y también proporciona funcionalidad a una clase que anteriormente no lo tenía. Tampoco es un truco. Pienso que es realmente genial. Entonces, ¿qué tiene de malo?
BreadicalMD
2
@BreadicalMD El mayor problema que puedo ver es que no puedes usar un UITextFieldDelegatecon esto para manejar los eventos de edición de inicio y finalización. En cambio, a menos que haya una manera de manejar esos eventos que no conozco, deberá anular becomeFirstRespondery resignFirstResponderen la subclase de botones, y posiblemente crear su propio protocolo de delegado, agregar una delegatepropiedad y llamar al delegado de lo mencionado anteriormente. métodos. Todo esto es mucho más trabajo que simplemente anular caretRectForPositionen una UITextFieldsubclase.
Mark Amery
1
@BreadicalMD Dicho esto, a pesar de que la respuesta de Joseph Chiu es superior desde cualquier punto de vista práctico, todavía estoy de acuerdo con usted en que esto es bastante sexy. Nunca antes había mirado cuidadosamente la UIResponderreferencia de clase, y no tenía idea de que un truco como este fuera posible.
Mark Amery
Tarde para la fiesta, pero creo que esta es una muy buena solución y se siente mucho más apropiado y menos hackeo que usar UITextFielday ocultar el cursor, que es básicamente un hack ya que estamos usando el campo de texto como etiqueta mientras sin utilizar ninguna de las funciones de UITextField.
Rupert
7

Versión Swift 5 de la publicación 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.
fuente
En mi caso funcionó. Se agregó una subclase de UITextField con estos métodos usando Swift 4. ¡Gracias!
J. Fdez
3

configura el tintColor en Clear Color

textfield.tintColor = [UIColor clearColor];

y también puedes configurarlo desde el generador de interfaces

Ahmad Al-Attal
fuente
2
Acabas de copiar una respuesta de hace más de 2 años.
Ashley Mills
1
lo siento mi amigo, pero no copié nada.
Ahmad Al-Attal
44
Eso es interesante, "mi amigo". Su respuesta se parece bastante a la respuesta de @ oldman del 1 de mayo de 2015. ¿Puedes decirme en qué se diferencia el tuyo?
Ashley Mills
1
Como se mencionó anteriormente, borrar el color del tinte no impide que los usuarios con teclados externos (iPad Pro) cambien el texto.
Michael Long
Los usuarios profesionales pueden hacer lo que quieran. Son profesionales: saben lo que están haciendo; ^)
Anton Tropashko
2

Si quieres ocultar el cursor, ¡puedes usar esto fácilmente! A mi me funciono ..

[[textField valueForKey:@"textInputTraits"] setValue:[UIColor clearColor] forKey:@"insertionPointColor"]
Göktuğ Aral
fuente
3
Esto es indocumentado, que yo sepa. Es posible que el uso de esto haga que su aplicación sea rechazada por llamar a API privadas si la envía a la tienda de aplicaciones, aunque no conozco ningún envío que use esto para probar esa especulación de una manera u otra. Es una pena, porque sería bueno poder resolver este problema sin subclasificar, y esta respuesta lo permite.
Mark Amery
1

Respuesta proporcionada por el OP, copiada del cuerpo de la pregunta para ayudar a limpiar la cola cada vez mayor de preguntas sin respuesta.

Creo que tengo la solución correcta, pero si se puede mejorar será bienvenido :) Bueno, hice una subclase de UITextField y anulé el método que devuelve el CGRect para los límites

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

¿El problema? El texto no se muestra porque el rect es cero. Pero agregué un UILabel como una subvista del control y anulé el método setText para que, al ingresar un texto como de costumbre, el texto del campo de texto sea nulo y sea la etiqueta que muestra el texto

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

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

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

Con esto, la cosa funciona como se esperaba. ¿Conoces alguna forma de mejorarlo?

Robert Höglund
fuente
Esta respuesta es básicamente inútil ahora dado que el enfoque alternativo de Joseph Chiu es muy similar pero mucho más simple. ¿Puedo sugerir simplemente eliminarlo?
Mark Amery
1

Para deshabilitar el cursor y el menú, uso la subclase con estos 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
fuente
0

Simplemente subclase UITextFieldy anulo de la layoutSubviewssiguiente manera:

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

Es un truco sucio, y puede fallar en el futuro (en ese momento el cursor será visible nuevamente; su aplicación no se bloqueará), pero funciona.

Mark Beaton
fuente
-1

Puede agregar una BOOL cursorlesspropiedad a UITextFielduna categoría a través de objetos asociados.

@interface UITextField (Cursorless)

@property (nonatomic, assign) BOOL cursorless;

@end

Luego, use el método swizzling para swizzle caretRectForPosition:con un método que alterna entre CGRectZeroy su valor predeterminado usando cursorless.

Esto lleva a una interfaz simple a través de una categoría desplegable. Esto se demuestra en los siguientes archivos.

Simplemente colóquelos y obtenga el beneficio de esta interfaz simple

UITextFieldcategoría: https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless.h https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless .metro

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
fuente
¡Una mala solución en tantos niveles! para empezar: nunca anule los métodos en categorías. Evite el método Swizziling a menos que realmente lo necesite (otros han encontrado buenas soluciones para esta pregunta). Esto hace que su código sea mucho más complicado.
EsbenB
Si realmente mirara el código, se daría cuenta de que ningún método se anula en la categoría y que el método swizzling se implementa correctamente. He estado usando esta implementación durante años sin falta.
Impresionante-o
Además, explique qué hay de complicado en agregar una base de código aislada de ~ 150 líneas para resolver permanentemente un problema recurrente continuo. Esto NO solo complicará su base de código, sino que proporciona la interfaz más simple posible; un solo booleano para dictar la funcionalidad.
Impresionante-o
Eche un vistazo a esta excelente discusión sobre el método swizzling stackoverflow.com/questions/5339276/... Especialmente sobre depuración. El método swizzling es una característica sorprendente, pero en mi humilde opinión, solo debe usarse cuando sea necesario. Este problema es fácil de resolver utilizando una de las sugerencias proporcionadas aquí, y proporcionará un código más fácil de mantener y leer para otros programadores.
EsbenB
Lo leí antes y seguí todas las pautas esbozadas en mi implementación para la corrección. Podría decirse que este es un ejemplo sólido de cuando el swizzling de métodos gana. Es un caso aislado, pero común, en el que las bibliotecas básicas no pueden implementar una característica ampliamente necesaria en las plataformas de una manera simple. Los otros ejemplos sugeridos no definen semánticamente un campo de texto sin cursor, solo lo realizan a través de la ofuscación del marco del cursor o de su color. Para un nuevo programador, esta interfaz es la más fácil de leer y hace exactamente lo que se espera de ella.
Impresionante-o