Como desabilitar a edição UITextField, mas ainda aceitar o toque?

107

Estou fazendo um UITextFieldque tem um UIPickerViewas inputView. Tudo bem, exceto que eu posso editar copiando, colando, recortando e selecionando o texto, e eu não quero isso. Apenas o Seletor deve modificar o campo de texto.

Aprendi que posso desativar a edição configurando setEnabledou setUserInteractionEnabledpara NO. Ok, mas o TextField para de responder ao toque e o seletor não aparece.

O que posso fazer para o conseguir?

Luiz de prá
fonte

Respostas:

131

Usando o delegado textfield, há um método

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

Retorne NÃO com isso, e qualquer tentativa do usuário de editar o texto será rejeitada.

Dessa forma, você pode deixar o campo habilitado, mas ainda assim evitar que as pessoas colem texto nele.

Nick Lockwood
fonte
4
Se você ainda quiser responder ao toque, responda "tocar para baixo" e não "retocar por dentro". Caso contrário, seu evento nunca será chamado.
radven
@NickLockwood existe alguma maneira de permitir que o textField se torne o primeiro a responder, mas desabilitar a interação do usuário e ocultar o cursor?
onmyway133
1
@entropy Presumo que você queira selecionar o conteúdo para que o usuário possa copiá-lo? Se você criar uma subclasse de UITextField, poderá substituir praticamente qualquer comportamento - por exemplo, você pode forçar o campo a sempre selecionar todos sempre que o usuário tocar em, o que efetivamente ocultaria o circunflexo.
Nick Lockwood
1
@LoryLory é o mesmo, apenas use a sintaxe Swift para o método delegado.
Nick Lockwood,
5
Mais precisamente, podemos usar "textFieldShouldBeginEditing" se necessário
Naveen Shan
22

Traduza a resposta de Nick para rápida:

P / S: Retorno falso => ​​os campos de texto não podem inserir, editar pelo teclado. Ele apenas pode definir o texto por code.EX: textField.text = "My String Here"

override func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    return false
}
Lee
fonte
3
Isso não funciona para mim. Apenas dê um clique longo no campo de texto até que o zoom apareça e o menu recortar / copiar / colar do iOS apareça e eu posso usar isso para alterar o texto.
RowanPD de
2
swift 4: `func textField (_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {return false}`
lionello
16

Isso seria o mais simples de tudo:

em viewDidLoad: (defina o delegado apenas para campos de texto que não devem ser editáveis.

self.textfield.delegate=self;

e insira esta função de delegado:

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

É isso aí!

Jain Ankish
fonte
27
Isso evita que o seletor seja apresentado e não corrige o problema descrito
Martin Lockett
6
@MartinLockett Se você acionar o UIPickerViewcomportamento desse método delegado, ele funcionará bem.
Albert Bori
9

Em 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
fonte
7

Seria mais elegante criar uma subclasse personalizada de UITextFieldque retorna NOpara todas as chamadas para canPerformAction:withSender:(ou pelo menos onde actionestá @selector(cut)ou @selector(paste)), conforme descrito aqui .

Além disso, eu também implementaria - (BOOL) textField: (UITextField *) textField shouldChangeCharactersInRange: (NSRange) range replacementString: (NSString *) string de acordo com a sugestão de Nick para desativar a entrada de texto em teclados Bluetooth.

MrMage
fonte
Não consigo descobrir como fazer isso. Os métodos canPerformActione da subclasse shouldChangeCharactersInRangenunca são chamados.
Nestor
7

Simplesmente coloque um UIButton exatamente sobre todo o UITextField sem rótulo-texto que o torna "invisível". Este botão pode receber e delegar toques ao invés do Textfield e o conteúdo do TextField ainda estará visível.

Timm Kent
fonte
7

Em Swift:

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

Usei a solução fornecida pelo MrMage. A única coisa que eu acrescentaria é que você deve renunciar ao UITextView como primeiro a responder, caso contrário, você ficará preso com o texto selecionado.

Este é meu 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
fonte
1

Para obter uma alternativa que lida com UIPickerView e Action Sheets, confira ActionSheetPicker

https://github.com/TimCinel/ActionSheetPicker

São cocoapods habilitados. Ele lida com todos os botões de cancelar e concluir na Folha de Ação. Os exemplos dentro do projeto de amostra são ótimos. Eu escolhi o ActionSheetStringPicker, que lida facilmente apenas com opções baseadas em String, mas a API pode lidar com quase tudo que eu posso pensar.

Eu originalmente comecei uma solução muito parecida com a resposta marcada, mas tropecei neste projeto e levei cerca de 20 minutos para integrar as coisas ao meu aplicativo para uso, incluindo o uso de cocopods: ActionSheetPicker (~> 0.0)

Espero que isto ajude.

Baixe o projeto git e observe as seguintes classes:

  • ActionSheetPickerViewController.m
  • ActionSheetPickerCustomPickerDelegate.h

Aqui está aproximadamente a maior parte do código que adicionei, mais as importações * .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
fonte
Uma versão atualizada do pod está aqui. github.com/skywinder/ActionSheetPicker-3.0
Nick N
1

Para evitar a edição de UITextField ao usar UIPickerView para selecionar valores (em 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
fonte
0

Esta solução alternativa funciona. Coloque um UIView transparente acima do campo de texto e implemente o seguinte 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
fonte
1
Estou confuso sobre por que você não usa um UIButton + touchUpInside transparente e optou pelo gesto UIView +.
Chen Li Yong,
0

Faça sua inputView ser apresentada por um campo de texto oculto que também altera o texto do apresentado e desabilitado.

Sandro Vezzali
fonte
-5

Eu usei :

[self.textField setEnabled:NO];

e está funcionando bem

Chris
fonte
-7

Isso funcionou para mim. [textview setEditable:NO]; As respostas acima complicam a situação.

Tolgab
fonte
1
O OP escreveu "Aprendi que posso desativar a edição definindo setEnabled ou setUserInteractionEnabled: NO para NO. Ok, mas o TextField para de responder ao toque e o seletor não aparece." Sua resposta irá interromper todos os eventos, cujo resultado não era desejado.
maninvan
@Tolgab nem mesmo é a solução relacionada para este OP!
Anurag Sharma