UITextField,在1个字符后自动移动到下一个

时间:2010-01-25 00:45:25

标签: delegates keyboard uitextfield

场景:我有4个UITextField只接受1个字符。容易。

问题:输入1个字符后,我希望下一个TextField自动变为活动而不必按下一个(即我使用的是UIKeyboardTypeNumberPad,并且没有NEXT按钮。(我知道我实际上可以创建下一个)按钮编程,但我不想走那么远,只需要输入1个字符后自动激活下一个字段。

#define MAX_LENGTH 1

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    NSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"];
    for (int i = 0; i < [string length]; i++) {
    unichar c = [string characterAtIndex:i];
        if (![myCharSet characterIsMember:c]) {
            return NO;
        }
    }
        NSUInteger newLength = [textField.text length] + [string length] - range.length;
        return (newLength > 1) ? NO : YES;
}


-(BOOL)textFieldShouldReturn:(UITextField *)textField {
    if (textField == pc1) {
        [pc2 becomeFirstResponder];
    }else if (textField == pc2) {
        [pc3 becomeFirstResponder];
    }else if (textField == pc3) {
        [pc4 becomeFirstResponder];
    }else if (textField == pc4) {
        [textField resignFirstResponder];
    }
    return YES;
}

6 个答案:

答案 0 :(得分:19)

我通过修改我在这里找到的一些代码来找到解决方案: http://www.thepensiveprogrammer.com/2010/03/customizing-uitextfield-formatting-for.html

首先将视图控制器设置为文本字段的委托。

然后做这样的事情:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{   
    BOOL shouldProcess = NO; //default to reject
    BOOL shouldMoveToNextField = NO; //default to remaining on the current field

    int insertStringLength = [string length];
    if(insertStringLength == 0){ //backspace
        shouldProcess = YES; //Process if the backspace character was pressed
    }
    else {
        if([[textField text] length] == 0) {
            shouldProcess = YES; //Process if there is only 1 character right now
        }
    }

    //here we deal with the UITextField on our own
    if(shouldProcess){
        //grab a mutable copy of what's currently in the UITextField
        NSMutableString* mstring = [[textField text] mutableCopy];
        if([mstring length] == 0){
            //nothing in the field yet so append the replacement string
            [mstring appendString:string];

            shouldMoveToNextField = YES;
        }
        else{
            //adding a char or deleting?
            if(insertStringLength > 0){
                [mstring insertString:string atIndex:range.location];
            }
            else {
                //delete case - the length of replacement string is zero for a delete
                [mstring deleteCharactersInRange:range];
            }
        }

        //set the text now
        [textField setText:mstring];

        [mstring release];

        if (shouldMoveToNextField) {
            //
            //MOVE TO NEXT INPUT FIELD HERE
            //
        }
    }

    //always return no since we are manually changing the text field
    return NO;
}

答案 1 :(得分:7)

我知道这是一个非常旧问题,但这是我的方法,只允许四个UITextField中的单个数值,并自动“标记”到下一个(pin1-pin4每个代表一个PIN号码数字lol,并保留为属性):

-(BOOL)textFieldShouldReturn:(UITextField*)textField;
{
    if (textField == pin1)
    {
        [pin2 becomeFirstResponder];
    }
    else if (textField == pin2)
    {
        [pin3 becomeFirstResponder];
    }
    else if (textField == pin3)
    {
        [pin4 becomeFirstResponder];
    }

    return NO;
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    // This allows numeric text only, but also backspace for deletes
    if (string.length > 0 && ![[NSScanner scannerWithString:string] scanInt:NULL])
        return NO;

    NSUInteger oldLength = [textField.text length];
    NSUInteger replacementLength = [string length];
    NSUInteger rangeLength = range.length;

    NSUInteger newLength = oldLength - rangeLength + replacementLength;

    // This 'tabs' to next field when entering digits
    if (newLength == 1) {
        if (textField == pin1)
        {
            [self performSelector:@selector(setNextResponder:) withObject:pin2 afterDelay:0.2];
        }
        else if (textField == pin2)
        {
            [self performSelector:@selector(setNextResponder:) withObject:pin3 afterDelay:0.2];
        }
        else if (textField == pin3)
        {
            [self performSelector:@selector(setNextResponder:) withObject:pin4 afterDelay:0.2];
        }
    }
    //this goes to previous field as you backspace through them, so you don't have to tap into them individually
    else if (oldLength > 0 && newLength == 0) {
        if (textField == pin4)
        {
            [self performSelector:@selector(setNextResponder:) withObject:pin3 afterDelay:0.1];
        }
        else if (textField == pin3)
        {
            [self performSelector:@selector(setNextResponder:) withObject:pin2 afterDelay:0.1];
        }
        else if (textField == pin2)
        {
            [self performSelector:@selector(setNextResponder:) withObject:pin1 afterDelay:0.1];
        }
    }

    return newLength <= 1;
}

- (void)setNextResponder:(UITextField *)nextResponder
{
    [nextResponder becomeFirstResponder];
}

答案 2 :(得分:7)

SWIFT 3更新代码

@IBOutlet weak var tf1: UITextField!
@IBOutlet weak var tf2: UITextField!
@IBOutlet weak var tf3: UITextField!
@IBOutlet weak var tf4: UITextField!
override func viewDidLoad() {
    super.viewDidLoad()
    tf1.delegate = self
    tf2.delegate = self
    tf3.delegate = self
    tf4.delegate = self


    tf1.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
    tf2.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
    tf3.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
    tf4.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)

}
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)
    tf1.becomeFirstResponder()

}
func textFieldDidChange(textField: UITextField){

    let text = textField.text

    if text?.utf16.count==1{
        switch textField{
        case tf1:
            tf2.becomeFirstResponder()
        case tf2:
            tf3.becomeFirstResponder()
        case tf3:
            tf4.becomeFirstResponder()
        case tf4:
            tf4.resignFirstResponder()
        default:
            break
        }
    }else{

    }
}

答案 3 :(得分:4)

以下内容是针对Swift 5的,它将文本字段作为数组而不是单个字段来处理。

import UIKit

class MyViewController: UIViewController, UITextFieldDelegate {

@IBOutlet var digitFields: [UITextField]!

override func viewDidLoad() {
    super.viewDidLoad()

    digitFields.forEach {
        configureDigitField($0)
    }
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    digitFields[0].becomeFirstResponder()
}

fileprivate func configureDigitField(_ digitField: UITextField) {
    digitField.delegate = (self as UITextFieldDelegate)
    digitField.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
}

// Move to next field in digit fields if the value is populated
@objc fileprivate func textFieldDidChange(textField: UITextField) {
    if textField.text?.count == 1 {
        let remaining = digitFields.filter { $0.text?.count == 0 }
        if remaining.count > 0 {
            remaining[0].becomeFirstResponder()
        } else {
            digitFields.forEach { $0.resignFirstResponder() }
        }
    }
}

结果:

enter image description here

这取决于在数组中分组的文本字段。这可以在界面构建器中通过在“出口配置”屏幕中配置字段集合来实现:

Outlet configuration as a collection

可以从最后一个选项卡项上的视图控制器属性访问 enter image description here

请注意,您需要手动添加

@IBOutlet var digitFields:[UITextField]!

添加到视图控制器,然后才能向其中添加文本字段。

代码行为摘要

  • 视图控制器必须是UITextFieldDelegate才能接收文本字段事件。
  • 在viewDidLoad函数中,使用configureDigitField方法初始化数组中的每个文本字段
  • 在viewWillAppear方法中,准备好数组中的第一个字段以处理输入(即第一个条目将在其中发生)
  • configureDigitalField函数将此视图控制器设置为从文本字段接收事件(每个文本字段都会调用每个事件)
  • 它还设置了一个选择器,以根据文本字段编辑更改事件的结果调用textFieldDidChange函数
  • textFieldDidChange方法检查字段中文本的长度是否为1,如果是,则为
  • 检查未输入任何值的其余文本字段
  • 获取剩下的第一个文本字段并将其设置为接收下一个输入
  • 如果没有剩余字段为空,请放弃其作为第一响应者的位置,以便在任何数字字段中都不会再进行按键操作

答案 4 :(得分:2)

Swift 4.x

textField.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: .editingChanged)
@objc func textFieldDidChange(textField: UITextField)
{
    let text = textField.text
    if text?.utf16.count == 1 {
        switch textField {
        case txtOtpNumber1:
            txtOtpNumber2.becomeFirstResponder()
        case txtOtpNumber2:
            txtOtpNumber3.becomeFirstResponder()
        case txtOtpNumber3:
            txtOtpNumber4.becomeFirstResponder()
        case txtOtpNumber4:
            txtOtpNumber4.resignFirstResponder()
        default:
            break
        }
    } else {
        switch textField {
        case txtOtpNumber4:
            txtOtpNumber3.becomeFirstResponder()
        case txtOtpNumber3:
            txtOtpNumber2.becomeFirstResponder()
        case txtOtpNumber2:
            txtOtpNumber1.becomeFirstResponder()
        case txtOtpNumber1:
            txtOtpNumber1.resignFirstResponder()
        default:
            break
        }
    }
}

<子> PS。 Chetan's answer针对当前的Swift进行了更新。

答案 5 :(得分:1)

虽然这是一个老问题,但我刚刚过来了,我提出了更简单的解决方案。假设我们为传递代码执行此操作,因此每个框(UITextField)最大长度为一个char。

- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
    if (![string isEqualToString:@""]) {
        textField.text = string;
        if ([textField isEqual:self.txtField1]) {
            [self.txtField2 becomeFirstResponder];
        }else if ([textField isEqual:self.txtField2]){
            [self.txtField3 becomeFirstResponder];
        }else if ([textField isEqual:self.txtField3]){
            [self.txtField4 becomeFirstResponder];
        }else{
            [textField resignFirstResponder];
        }
        return NO;
    }
    return YES;
}

-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
    if (textField.text.length > 0) {
        textField.text = @"";
    }
    return YES;
}