ios - sacar - iphone boton en pantalla




Cómo navegar a través de los campos de texto(botones Siguiente/Hecho) (20)

Acabo de crear un nuevo Pod cuando trato con estas cosas GNTextFieldsCollectionManager . Maneja automáticamente el problema del siguiente / último campo de texto y es muy fácil de usar:

[[GNTextFieldsCollectionManager alloc] initWithView:self.view];

Agarra todos los campos de texto ordenados por la jerarquía de vistas (o por etiquetas), o puede especificar su propia matriz de campos de texto.

¿Cómo puedo navegar por todos mis campos de texto con el botón "Siguiente" en el teclado del iPhone?

El último campo de texto debe cerrar el teclado.

He configurado el IB los Botones (Siguiente / Hecho) pero ahora estoy bloqueado.

Implementé la acción textFieldShouldReturn pero ahora los botones Siguiente y Hecho cierran el teclado.


Aquí está mi solución para este problema.

Para resolver esto (y porque odio confiar en las etiquetas para hacer cosas) decidí agregar una propiedad personalizada al objeto UITextField. En otras palabras, creé una categoría en UITextField como esta:

UITextField + Extended.h

@interface UITextField (Extended)

@property(retain, nonatomic)UITextField* nextTextField;

@end

UITextField + Extended.m

#import "UITextField+Extended.h"
#import <objc/runtime.h>

static char defaultHashKey;

@implementation UITextField (Extended)

- (UITextField*) nextTextField { 
    return objc_getAssociatedObject(self, &defaultHashKey); 
}

- (void) setNextTextField:(UITextField *)nextTextField{
    objc_setAssociatedObject(self, &defaultHashKey, nextTextField, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}

@end

Ahora, aquí está cómo lo uso:

UITextField *textField1 = ...init your textfield
UITextField *textField2 = ...init your textfield
UITextField *textField3 = ...init your textfield

textField1.nextTextField = textField2;
textField2.nextTextField = textField3;
textField3.nextTextField = nil;

E implementa el método textFieldShouldReturn:

- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {

    UITextField *next = theTextField.nextTextField;
    if (next) {
        [next becomeFirstResponder];
    } else {
        [theTextField resignFirstResponder];
    }

    return NO; 
}

Ahora tengo una especie de lista enlazada de UITextField, cada uno sabiendo quién es el siguiente en la línea.

Espero que te ayude.


Aquí hay uno sin delegación:

tf1.addTarget(tf2, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
tf2.addTarget(tf3, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)

ObjC:

[tf1 addTarget:tf2 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
[tf2 addTarget:tf3 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];

Funciona con la acción (en su mayoría desconocida) UIControlEventEditingDidEndOnExit UITextField .

También puede conectar esto fácilmente en el guión gráfico, por lo que no se requiere delegación o código.

Edición: en realidad no puedo entender cómo conectar esto en el guión gráfico. becomeFirstResponder no parece ser una acción ofrecida para este evento de control, que es una pena. Aún así, puede conectar todos sus campos de texto a una sola acción en su ViewController que luego determina qué campo de texto se becomeFirstResponder en el Primer becomeFirstResponder basado en el remitente (aunque no es tan elegante como la solución programática anterior, por lo que IMO lo hace con el código anterior en viewDidLoad ).


Después de salir de un campo de texto, llama a [otherTextField BecomeFirstResponder] y el siguiente campo se enfoca.

Esto puede ser realmente un problema difícil de tratar ya que a menudo también querrá desplazar la pantalla o ajustar la posición del campo de texto para que sea fácil de ver cuando se edita. Solo asegúrese de realizar muchas pruebas para entrar y salir de los campos de texto de diferentes maneras y también para salir temprano (siempre dé al usuario la opción de descartar el teclado en lugar de ir al siguiente campo, generalmente con "Hecho" en la barra de navegación)


Esto me funcionó en Xamarin.iOS / Monotouch. Cambie el botón del teclado a Siguiente, pase el control al siguiente UITextField y oculte el teclado después del último UITextField.

private void SetShouldReturnDelegates(IEnumerable<UIView> subViewsToScout )
{
  foreach (var item in subViewsToScout.Where(item => item.GetType() == typeof (UITextField)))
  {
    (item as UITextField).ReturnKeyType = UIReturnKeyType.Next;
    (item as UITextField).ShouldReturn += (textField) =>
    {
        nint nextTag = textField.Tag + 1;
        var nextResponder = textField.Superview.ViewWithTag(nextTag);
        if (null != nextResponder)
            nextResponder.BecomeFirstResponder();
        else
            textField.Superview.EndEditing(true); 
            //You could also use textField.ResignFirstResponder(); 

        return false; // We do not want UITextField to insert line-breaks.
    };
  }
}

Dentro de ViewDidLoad tendrás:

Si tus TextFields no tienen una etiqueta, configúralo ahora:

txtField1.Tag = 0;
txtField2.Tag = 1;
txtField3.Tag = 2;
//...

y solo la llamada

SetShouldReturnDelegates(yourViewWithTxtFields.Subviews.ToList());
//If you are not sure of which view contains your fields you can also call it in a safer way:
SetShouldReturnDelegates(txtField1.Superview.Subviews.ToList());
//You can also reuse the same method with different containerViews in case your UITextField are under different views.

Hay una solución mucho más elegante que me sorprendió la primera vez que la vi. Beneficios:

  • Más cerca de la implementación del campo de texto OSX donde un campo de texto sabe dónde debe ir el enfoque a continuación
  • No se basa en establecer o usar etiquetas, que son IMO frágiles para este caso de uso
  • Se puede extender para trabajar con los controles UITextField y UITextView , o con cualquier control de UI de entrada del teclado
  • No desordena el controlador de vista con el código delegado de UITextField repetitivo
  • Se integra muy bien con IB y se puede configurar a través de la opción familiar de arrastrar y soltar para conectar salidas.

Cree una subclase UITextField que tenga una propiedad IBOutlet llamada nextField. Aquí está el encabezado:

@interface SOTextField : UITextField

@property (weak, nonatomic) IBOutlet UITextField *nextField; 

@end

Y aquí está la implementación:

@implementation SOTextField

@end

En su controlador de vista, creará el método -textFieldShouldReturn: delegate:

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if ([textField isKindOfClass:[SOTextField class]]) {
        UITextField *nextField = [(SOTextField *)textField nextField];

        if (nextField) {
            dispatch_async(dispatch_get_current_queue(), ^{
                [nextField becomeFirstResponder];
            });
        }
        else {
            [textField resignFirstResponder];
        }
    }

    return YES;
}

En IB, cambie sus UITextFields para usar la clase SOTextField . A continuación, también en IB, configure el delegado para cada uno de los 'SOTextFields' a 'File's Owner' (que está justo donde colocó el código para el método del delegado - textFieldShouldReturn). La belleza de este diseño es que ahora puede simplemente hacer clic con el botón derecho en cualquier campo de texto y asignar la siguiente salida de campo al siguiente objeto SOTextField que desea que sea el siguiente respondedor.

Además, puedes hacer cosas geniales como hacer un loop en los campos de texto para que después de que el último pierda el enfoque, el primero vuelva a recibir el enfoque.

Esto se puede extender fácilmente para asignar automáticamente el returnKeyType de returnKeyType de returnKeyType de SOTextField a un UIReturnKeyNext si hay un nextField asignado, una cosa menos configurable manualmente.


He probado muchos códigos y, finalmente, esto funcionó para mí en Swift 3.0 Latest [Marzo 2017]

La clase ViewController debe heredarse del UITextFieldDelegate para hacer que este código funcione.

class ViewController: UIViewController,UITextFieldDelegate  

Agregue el campo Texto con el número de etiqueta adecuado y este número de etiqueta se usa para llevar el control al campo de texto apropiado en función del número de etiqueta incremental que se le asigna.

override func viewDidLoad() {
    userNameTextField.delegate = self
    userNameTextField.tag = 0
    userNameTextField.returnKeyType = UIReturnKeyType.next
    passwordTextField.delegate = self
    passwordTextField.tag = 1
    passwordTextField.returnKeyType = UIReturnKeyType.go
}

En el código anterior, returnKeyType = UIReturnKeyType.next donde hará que la tecla de retorno del teclado se muestre como Next , también tiene otras opciones como Join/Go , etc., según su aplicación, cambiar los valores.

Este textFieldShouldReturn es un método de UITextFieldDelegate controlado y aquí tenemos la siguiente selección de campo basada en el incremento del valor de la etiqueta

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
        nextField.becomeFirstResponder()
    } else {
        textField.resignFirstResponder()
        return true;
    }
    return false
 }

Hola a todos por favor ve este

- (void)nextPrevious:(id)sender
{

  UIView *responder = [self.view findFirstResponder];   

  if (nil == responder || ![responder isKindOfClass:[GroupTextField class]]) {
    return;
  }

  switch([(UISegmentedControl *)sender selectedSegmentIndex]) {
    case 0:
      // previous
      if (nil != ((GroupTextField *)responder).previousControl) {
        [((GroupTextField *)responder).previousControl becomeFirstResponder];
        DebugLog(@"currentControl: %i previousControl: %i",((GroupTextField *)responder).tag,((GroupTextField *)responder).previousControl.tag);
      }
      break;
    case 1:
      // next
      if (nil != ((GroupTextField *)responder).nextControl) {
        [((GroupTextField *)responder).nextControl becomeFirstResponder];
        DebugLog(@"currentControl: %i nextControl: %i",((GroupTextField *)responder).tag,((GroupTextField *)responder).nextControl.tag);
      }     
      break;    
  }
}

Me gustan las soluciones OO que Anth0 y Answerbot ya han sugerido. Sin embargo, estaba trabajando en un POC rápido y pequeño, por lo que no quería saturar las cosas con subclases y categorías.

Otra solución simple es crear una NSArray de campos y buscar el siguiente campo cuando presione siguiente. No es una solución OO, pero es rápida, simple y fácil de implementar. Además, puede ver y modificar el pedido de un vistazo.

Aquí está mi código (construido sobre otras respuestas en este hilo):

@property (nonatomic) NSArray *fieldArray;

- (void)viewDidLoad {
    [super viewDidLoad];

    fieldArray = [NSArray arrayWithObjects: firstField, secondField, thirdField, nil];
}

- (BOOL) textFieldShouldReturn:(UITextField *) textField {
    BOOL didResign = [textField resignFirstResponder];
    if (!didResign) return NO;

    NSUInteger index = [self.fieldArray indexOfObject:textField];
    if (index == NSNotFound || index + 1 == fieldArray.count) return NO;

    id nextField = [fieldArray objectAtIndex:index + 1];
    activeField = nextField;
    [nextField becomeFirstResponder];

    return NO;
}
  • Siempre devuelvo NO porque no quiero insertar un salto de línea. Pensé en señalarlo, ya que cuando respondiera SÍ, saldría automáticamente de los campos siguientes o insertaría un salto de línea en mi TextView. Me tomó un poco de tiempo para darme cuenta de eso.
  • activeField realiza un seguimiento del campo activo en caso de que sea necesario desplazarse para despejar el campo desde el teclado. Si tiene un código similar, asegúrese de asignar el campo activo antes de cambiar el primer respondedor. Cambiar el primer respondedor es inmediato y activará el evento KeyboardWasShown inmediatamente.

Me sorprende la cantidad de respuestas que aquí no entienden un concepto simple: navegar por los controles de su aplicación no es algo que las vistas deban hacer. Es el trabajo del controlador decidir qué control hará el siguiente primer respondedor.

Además, la mayoría de las respuestas solo se aplican a la navegación hacia adelante, pero los usuarios también pueden querer retroceder.

Así que esto es lo que he encontrado. Su formulario debe ser administrado por un controlador de vista, y los controladores de vista son parte de la cadena de respuesta. Entonces eres perfectamente libre de implementar los siguientes métodos:

#pragma mark - Key Commands

- (NSArray *)keyCommands
{
    static NSArray *commands;

    static dispatch_once_t once;
    dispatch_once(&once, ^{
        UIKeyCommand *const forward = [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(tabForward:)];
        UIKeyCommand *const backward = [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(tabBackward:)];

        commands = @[forward, backward];
    });

    return commands;
}

- (void)tabForward:(UIKeyCommand *)command
{
    NSArray *const controls = self.controls;
    UIResponder *firstResponder = nil;

    for (UIResponder *const responder in controls) {
        if (firstResponder != nil && responder.canBecomeFirstResponder) {
            [responder becomeFirstResponder]; return;
        }
        else if (responder.isFirstResponder) {
            firstResponder = responder;
        }
    }

    [controls.firstObject becomeFirstResponder];
}

- (void)tabBackward:(UIKeyCommand *)command
{
    NSArray *const controls = self.controls;
    UIResponder *firstResponder = nil;

    for (UIResponder *const responder in controls.reverseObjectEnumerator) {
        if (firstResponder != nil && responder.canBecomeFirstResponder) {
            [responder becomeFirstResponder]; return;
        }
        else if (responder.isFirstResponder) {
            firstResponder = responder;
        }
    }

    [controls.lastObject becomeFirstResponder];
}

Se puede aplicar una lógica adicional para desplazar a los respondedores fuera de la pantalla que están visibles.

Otra ventaja de este enfoque es que no necesita subclasificar todos los tipos de controles que desee mostrar (como UITextField s) sino que puede administrar la lógica en el nivel del controlador, donde, seamos honestos, es el lugar correcto para hacerlo asi que.


Primero configure la tecla de retorno del teclado en xib, de lo contrario puede escribir código en viewdidload :

passWord.returnKeyType = UIReturnKeyNext;

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
    if(textField == eMail) {
        [textField resignFirstResponder];
        [userName becomeFirstResponder];
    }
    if (textField==userName) {
        [textField resignFirstResponder];
        [passWord becomeFirstResponder];
    }
    if (textField==passWord) {
        [textField resignFirstResponder];
        [country becomeFirstResponder];
    }
    if (textField==country) {
        [textField resignFirstResponder];
    }
    return YES;
}

Tenía cerca de 10+ UITextField en mi panel de la historia y la forma en que habilité la siguiente funcionalidad fue crear una matriz de UITextField y hacer que el siguiente UITextField sea el primer Respuesta. Aquí está el archivo de implementación:

#import "RegistrationTableViewController.h"

@interface RegistrationTableViewController ()
@property (weak, nonatomic) IBOutlet UITextField *fullNameTextField;
@property (weak, nonatomic) IBOutlet UITextField *addressTextField;
@property (weak, nonatomic) IBOutlet UITextField *address2TextField;
@property (weak, nonatomic) IBOutlet UITextField *cityTextField;
@property (weak, nonatomic) IBOutlet UITextField *zipCodeTextField;
@property (weak, nonatomic) IBOutlet UITextField *urlTextField;
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;
@property (weak, nonatomic) IBOutlet UITextField *emailTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
@property (weak, nonatomic) IBOutlet UITextField *confirmPWTextField;

@end
NSArray *uiTextFieldArray;
@implementation RegistrationTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"view did load");
    uiTextFieldArray = @[self.fullNameTextField,self.addressTextField,self.address2TextField,self.cityTextField,self.zipCodeTextField,self.urlTextField,self.usernameTextField,self.emailTextField,self.passwordTextField,self.confirmPWTextField];
    for(UITextField *myField in uiTextFieldArray){
        myField.delegate = self;
    }


}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
    long index = [uiTextFieldArray indexOfObject:textField];
    NSLog(@"%ld",index);
    if(index < (uiTextFieldArray.count - 1)){
        [uiTextFieldArray[++index] becomeFirstResponder];
    }else{
        [uiTextFieldArray[index] resignFirstResponder];
    }
    return YES;
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

Una extensión rápida que aplica la respuesta de mxcl para hacer esto particularmente fácil (adaptado a swift 2.3 por Traveler):

extension UITextField {
    class func connectFields(fields:[UITextField]) -> Void {
        guard let last = fields.last else {
            return
        }
        for i in 0 ..< fields.count - 1 {
            fields[i].returnKeyType = .Next
            fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
        }
        last.returnKeyType = .Done
        last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), forControlEvents: .EditingDidEndOnExit)
    }
}

Es fácil de usar:

UITextField.connectFields([field1, field2, field3])

La extensión establecerá el botón de retorno en "Siguiente" para todos menos el último campo y en "Listo" para el último campo, y cambiará el enfoque / descartará el teclado cuando estos se toquen.

Rápido <2.3

extension UITextField {
    class func connectFields(fields:[UITextField]) -> Void {
        guard let last = fields.last else {
            return
        }
        for var i = 0; i < fields.count - 1; i += 1 {
            fields[i].returnKeyType = .Next
            fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
        }
        last.returnKeyType = .Done
        last.addTarget(last, action: "resignFirstResponder", forControlEvents: .EditingDidEndOnExit)
    }
}

SWIFT 3: uso así

UITextField.connectFields(fields: [field1, field2])

Extension:
    extension UITextField {
        class func connectFields(fields:[UITextField]) -> Void {
            guard let last = fields.last else {
                return
            }
            for i in 0 ..< fields.count - 1 {
                fields[i].returnKeyType = .next
                fields[i].addTarget(fields[i+1], action: #selector(UIResponder.becomeFirstResponder), for: .editingDidEndOnExit)
            }
            last.returnKeyType = .go
            last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), for: .editingDidEndOnExit)
        }
    }

Una forma más consistente y robusta es usar NextResponderTextField . Puede configurarlo totalmente desde el generador de interfaces sin necesidad de configurar el delegado o usar view.tag .

Todo lo que necesitas hacer es

  1. Configure el tipo de clase de su UITextField para que sea NextResponderTextField
  2. Luego, establezca la salida del nextResponderField para que apunte al siguiente respondedor, puede ser cualquier elemento UITextField o cualquier subclase UIResponder . También puede ser un UIButton y la biblioteca es lo suficientemente inteligente como para activar el evento TouchUpInside del botón solo si está habilitado.

Aquí está la biblioteca en acción:


Esta es una solución simple en Swift, sin etiqueta, sin trucos de guión gráfico ...

Solo usa esta extensión:

extension UITextField{

    func nextTextFieldField() -> UITextField?{
        //field to return
        var returnField : UITextField?
        if self.superview != nil{
            //for each view in superview
            for (_, view) in self.superview!.subviews.enumerate(){
                //if subview is a text's field
                if view.isKindOfClass(UITextField){
                    //cast curent view as text field
                    let currentTextField = view as! UITextField
                    //if text field is after the current one
                    if currentTextField.frame.origin.y > self.frame.origin.y{
                        //if there is no text field to return already
                        if returnField == nil {
                            //set as default return
                            returnField = currentTextField
                        }
                            //else if this this less far than the other
                        else if currentTextField.frame.origin.y < returnField!.frame.origin.y{
                            //this is the field to return
                            returnField = currentTextField
                        }
                    }
                }
            }
        }
        //end of the mdethod
        return returnField
    }

}

Y llámelo así (por ejemplo) con su delegado de campo de texto:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    textField.nextTextFieldField()?.becomeFirstResponder()
    return true
}

Sin etiquetas de uso y sin agregar una propiedad para nextField / nextTextField, puede intentar esto para emular TAB, donde "testInput" es su campo activo actual:

if ([textInput isFirstResponder])
    [textInput.superview.subviews enumerateObjectsAtIndexes:
     [NSIndexSet indexSetWithIndexesInRange:
      NSMakeRange([textInput.superview.subviews indexOfObject:textInput]+1,
                  [textInput.superview.subviews count]-[textInput.superview.subviews indexOfObject:textInput]-1)]
                                                    options:0 usingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) {
                                                        *stop = !obj.hidden && [obj becomeFirstResponder];
                                                    }];
if ([textInput isFirstResponder])
    [textInput.superview.subviews enumerateObjectsAtIndexes:
     [NSIndexSet indexSetWithIndexesInRange:
      NSMakeRange(0,
                  [textInput.superview.subviews indexOfObject:textInput])]
                                                    options:0 usingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) {
                                                        *stop = !obj.hidden && [obj becomeFirstResponder];
                                                    }];

en textFieldShouldReturn usted debe verificar que el campo de texto en el que se encuentra actualmente no sea el último cuando haga clic en siguiente y si no es así no descartar el teclado.


Aquí está una versión Swift 3 de la respuesta de Anth0 . ¡Lo estoy publicando aquí para ayudar a cualquier desarrollador veloz a querer aprovechar su gran respuesta! Me tomé la libertad de agregar un tipo de clave de retorno de "Siguiente" cuando configuró el objeto asociado.

extension UITextField {

  @nonobjc static var NextHashKey: UniChar = 0

  var nextTextField: UITextField? {
    get {
      return objc_getAssociatedObject(self, 
        &UITextField.NextHashKey) as? UITextField
    }
    set(next) {
     self.returnKeyType = UIReturnKeyType.next
     objc_setAssociatedObject(self,
      &UITextField.NextHashKey,next,.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
  }
}

Aquí hay otra extensión que muestra la posibilidad de usar el código anterior para recorrer una lista de UITextFields.

extension UIViewController: UITextFieldDelegate {
 public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
   guard let next = textField.nextTextField else {
     textField.resignFirstResponder()
     return true
   }

    next.becomeFirstResponder()
    return false
  }
}

Y luego, en su ViewController o donde sea, puede configurar sus campos de texto como ...

@IBOutlet fileprivate weak var textfield1: UITextField!
@IBOutlet fileprivate weak var textfield2: UITextField!
@IBOutlet fileprivate weak var textfield3: UITextField!

...

[textfield1, textfield2, textfield3].forEach{ $0?.delegate = self }

textfield1.nextTextField = textfield2
textfield2.nextTextField = textfield3
// We don't assign a nextTextField to textfield3 because we want 
// textfield3 to be the last one and resignFirstResponder when 
// the return button on the soft keyboard is tapped.

puede utilizar la biblioteca IQKeyboardManager para hacer esto. maneja cada cosa, no necesita ninguna configuración adicional. IQKeyboardManager está disponible a través de CocoaPods, para instalarlo simplemente agregue la siguiente línea a su Podfile:

pod 'IQKeyboardManager'

o Simplemente arrastre y suelte el directorio IQKeyBoardManager del proyecto de demostración a su proyecto. Eso es.puede encontrar el directorio IQKeyBoardManager en https://github.com/hackiftekhar/IQKeyboardManager


 -(BOOL)textFieldShouldReturn:(UITextField *)textField
{
   [[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
   return YES;
}




iphone