¿Cómo deshabilitar la edición de UITextField pero aún aceptar el toque?

107

Estoy haciendo un UITextFieldque tiene un UIPickerViewas inputView. Está todo bien, excepto que puedo editar copiando, pegando, cortando y seleccionando texto, y no lo quiero. Solo el selector debe modificar el campo de texto.

Aprendí que puedo deshabilitar la edición configurando setEnabledo setUserInteractionEnabledpara NO. Ok, pero TextField deja de responder al tacto y el selector no aparece.

¿Qué puedo hacer para lograrlo?

Luiz de Prá
fuente

Respuestas:

131

Usando el delegado de campo de texto, hay un método

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

Devuelva NO de esto, y cualquier intento por parte del usuario de editar el texto será rechazado.

De esa manera, puede dejar el campo habilitado pero aún evitar que las personas peguen texto en él.

Nick Lockwood
fuente
4
Si aún desea responder al tacto, responda "tocar hacia abajo" y no "retocar por dentro". De lo contrario, su evento nunca se llamará.
radven
@NickLockwood, ¿hay alguna forma en que podamos permitir que textField se convierta en el primer respondedor, pero deshabilite la interacción del usuario y oculte el signo de intercalación?
onmyway133
1
@entropy Supongo que desea poder seleccionar el contenido para que el usuario pueda copiarlo. Si crea una subclase de UITextField, puede anular prácticamente cualquier comportamiento; por ejemplo, podría obligar al campo a seleccionar siempre todo cuando el usuario toque, lo que ocultaría efectivamente el símbolo de intercalación.
Nick Lockwood
1
@LoryLory es lo mismo, solo use la sintaxis Swift para el método delegado en su lugar.
Nick Lockwood
5
Más precisamente, podemos usar "textFieldShouldBeginEditing" si es necesario
Naveen Shan
22

Traduce la respuesta de Nick a rápido:

P / S: Devuelve falso => ​​los campos de texto no se pueden ingresar, editar con el teclado. Solo puede establecer texto por código.EX: textField.text = "My String Here"

override func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    return false
}
sotavento
fuente
3
Esto no me funciona. Simplemente haga clic prolongadamente en el campo de texto hasta que aparezca el zoom y aparezca el menú de cortar / copiar / pegar de iOS y pueda usarlo para modificar el texto.
RowanPD
2
swift 4: `func textField (_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {return false}`
lionello
16

Este sería el más simple de todos:

en viewDidLoad: (establezca el delegado solo para campos de texto que no deberían ser editables.

self.textfield.delegate=self;

e inserte esta función de delegado:

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
return NO;
}

¡Eso es!

Ankish Jain
fuente
27
Esto evita que se presente el selector y, por lo tanto, no soluciona el problema descrito
Martin Lockett
6
@MartinLockett Si activa el UIPickerViewcomportamiento de ese método delegado, funciona bien.
Albert Bori
9

En Swift 3+:

class MyViewController: UIViewController, UITextFieldDelegate {

   override func viewDidLoad() {
      self.myTextField.delegate     = self
   }

   func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
      if textField == myTextField {
         // code which you want to execute when the user touch myTextField
      }
      return false
   }
}
Nadeesha Lakmal
fuente
7

Sería más elegante crear una subclase personalizada UITextFieldque devuelva NOpara todas las llamadas a canPerformAction:withSender:(o al menos donde actionestá @selector(cut)o @selector(paste)), como se describe aquí .

Además, también implementaría - (BOOL) textField: (UITextField *) textField shouldChangeCharactersInRange: (NSRange) range replacementString: (NSString *) cadena según la sugerencia de Nick para deshabilitar la entrada de texto desde teclados Bluetooth.

MrMage
fuente
No puedo por mi vida averiguar cómo hacer eso. Los métodos canPerformActiony de la subclase shouldChangeCharactersInRangenunca se llaman.
Nestor
7

Simplemente coloque un UIButton exactamente sobre todo el UITextField sin etiqueta-texto que lo haga "invisible". Este botón puede recibir y delegar toques en lugar del campo de texto y el contenido del campo de texto sigue siendo visible.

Timm Kent
fuente
7

En Swift:

func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
    questionField.resignFirstResponder();
    // Additional code here
    return false
}
Saranya
fuente
3

Usé la solución proporcionada por MrMage. Lo único que agregaría es que debe renunciar a UITextView como primer respondedor, de lo contrario, se quedará atascado con el texto seleccionado.

Aquí está mi código rápido:

class TouchableTextView : UITextView {

    override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
        self.resignFirstResponder()
        return false
    }

    override func shouldChangeTextInRange(range: UITextRange, replacementText text: String) -> Bool {
        self.resignFirstResponder()
        return false
    }

}
Leedrick
fuente
1

Para una alternativa que maneja UIPickerView y Action Sheets, consulte ActionSheetPicker

https://github.com/TimCinel/ActionSheetPicker

Tiene cocoapods habilitados. Maneja todos los botones cancelar y listo en la hoja de acción. Los ejemplos dentro del proyecto de muestra son geniales. Elijo ActionSheetStringPicker, que maneja fácilmente solo las opciones basadas en cadenas, pero la API puede manejar casi cualquier cosa que se me ocurra.

Originalmente comencé una solución muy parecida a la respuesta marcada, pero tropecé con este proyecto y me tomó aproximadamente 20 minutos integrar las cosas en mi aplicación para su uso, incluido el uso de cocopods: ActionSheetPicker (~> 0.0)

Espero que esto ayude.

Descarga el proyecto git y mira las siguientes clases:

  • ActionSheetPickerViewController.m
  • ActionSheetPickerCustomPickerDelegate.h

Aquí está aproximadamente la mayor parte del código que agregué, más las importaciones * .h.

- (IBAction)gymTouched:(id)sender {
      NSLog(@"gym touched");

      [ActionSheetStringPicker showPickerWithTitle:@"Select a Gym" rows:self.locations initialSelection:self.selectedIndex target:self successAction:@selector(gymWasSelected:element:) cancelAction:@selector(actionPickerCancelled:) origin:sender];
 }


- (void)actionPickerCancelled:(id)sender {
    NSLog(@"Delegate has been informed that ActionSheetPicker was cancelled");
}


- (void)gymWasSelected:(NSNumber *)selectedIndex element:(id)element {
    self.selectedIndex = [selectedIndex intValue];

    //may have originated from textField or barButtonItem, use an IBOutlet instead of element
    self.txtGym.text = [self.locations objectAtIndex:self.selectedIndex];
}


-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
    return NO;  // Hide both keyboard and blinking cursor.
}
Nick N
fuente
Aquí hay una versión actualizada del pod. github.com/skywinder/ActionSheetPicker-3.0
Nick N
1

Para evitar la edición de UITextField mientras usa UIPickerView para seleccionar valores (en Swift):

self.txtTransDate = self.makeTextField(self.transDate, placeHolder: "Specify Date")
self.txtTransDate?.addTarget(self, action: "txtTransDateEditing:", forControlEvents: UIControlEvents.EditingDidBegin)

func makeTextField(text: String?, placeHolder: String) -> UITextField {
    var textField = UITextField(frame: CGRect(x: 140, y: 0, width: 220.00, height: 40.00));
    textField.placeholder = placeHolder
    textField.text = text
    textField.borderStyle = UITextBorderStyle.Line
    textField.secureTextEntry = false;
    textField.delegate = self
    return textField
}


func txtTransDateEditing(sender: UITextField) {
    var datePickerView:UIDatePicker = UIDatePicker()
    datePickerView.datePickerMode = UIDatePickerMode.Date
    sender.inputView = datePickerView
    datePickerView.addTarget(self, action: Selector("datePickerValueChanged:"), forControlEvents: UIControlEvents.ValueChanged)
}

func datePickerValueChanged(sender: UIDatePicker) {
    var dateformatter = NSDateFormatter()
    dateformatter.dateStyle = NSDateFormatterStyle.MediumStyle
    self.txtTransDate!.text = dateformatter.stringFromDate(sender.date)
}

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool  {
    self.resignFirstResponder()
    return false
}
Thiru
fuente
0

Esta solución funciona. Coloque una UIView transparente encima del campo de texto e implemente el siguiente código:

- (void)viewDidLoad
{
 [super viewDidLoad];

   UILongPressGestureRecognizer *press = [[UILongPressGestureRecognizer alloc]             initWithTarget:self action:@selector(longPress)];
[transparentView addGestureRecognizer:press];
 [press release];
  press = nil;
}

-(void)longPress
{
   txtField.userInteractionEnabled = NO;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
   txtField.userInteractionEnabled = YES;
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
  [txtField becomeFirstResponder];
}
cocoakomali
fuente
1
Estoy confundido de por qué no usa un simple UIButton + touchUpInside transparente y optó por UIView + gesto en su lugar.
Chen Li Yong
0

Haga que su inputView sea presentado por un campo de texto oculto que también cambie el texto del presentado y deshabilitado.

Sandro Vezzali
fuente
-5

Solía ​​:

[self.textField setEnabled:NO];

y funciona bien

Chris
fuente
-7

Esto funcionó para mí [textview setEditable:NO]; Las respuestas anteriores complican demasiado la situación.

Tolgab
fuente
1
El OP escribió "Aprendí que puedo deshabilitar la edición configurando setEnabled o setUserInteractionEnabled: NO a NO. Ok, pero TextField deja de responder al tacto y el selector no aparece". Su respuesta detendrá todos los eventos, lo que no fue el resultado deseado.
maninvan
¡@Tolgab ni siquiera es la solución relacionada para este OP!
Anurag Sharma