簡體   English   中英

如何瀏覽文本字段(下一步/完成按鈕)

[英]How to navigate through textfields (Next / Done Buttons)

如何使用 iPhone 鍵盤上的“下一步”按鈕瀏覽所有文本字段?

最后一個文本字段應關閉鍵盤。

我已經設置了 IB 按鈕(下一步/完成),但現在我卡住了。

我實現了 textFieldShouldReturn 操作,但現在 Next 和 Done 按鈕關閉了鍵盤。

在Cocoa for Mac OS X中,您有下一個響應者鏈,您可以在其中詢問文本字段接下來應該關注哪個控件。 這就是使文本字段之間的標簽工作的原因。 但由於iOS設備沒有鍵盤,只有觸摸,這個概念還沒有幸免於過渡到Cocoa Touch。

無論如何,這可以輕松完成,有兩個假設:

  1. 所有“tabbable” UITextField都在同一父視圖上。
  2. 他們的“tab-order”由tag屬性定義。

假設你可以覆蓋textFieldShouldReturn:如下:

-(BOOL)textFieldShouldReturn:(UITextField*)textField
{
  NSInteger nextTag = textField.tag + 1;
  // Try to find next responder
  UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
  if (nextResponder) {
    // Found next responder, so set it.
    [nextResponder becomeFirstResponder];
  } else {
    // Not found, so remove keyboard.
    [textField resignFirstResponder];
  }
  return NO; // We do not want UITextField to insert line-breaks.
}

添加更多代碼,也可以忽略這些假設。

Swift 4.0

 func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    let nextTag = textField.tag + 1
    // Try to find next responder
    let nextResponder = textField.superview?.viewWithTag(nextTag) as UIResponder!

    if nextResponder != nil {
        // Found next responder, so set it
        nextResponder?.becomeFirstResponder()
    } else {
        // Not found, so remove keyboard
        textField.resignFirstResponder()
    }

    return false
}

如果文本字段的超級視圖將是UITableViewCell,則下一個響應者將是

let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!

還有一個優雅解決方案,它把我嚇倒了,我第一次看到它。 優點:

  • 更接近OSX文本字段實現,其中文本字段知道下一個焦點應該去哪里
  • 不依賴於設置或使用標簽 - 這對於這個用例來說是IMO脆弱的
  • 可以擴展為使用UITextFieldUITextView控件 - 或任何鍵盤輸入UI控件
  • 不會使用樣板文件UITextField委托代碼來混淆視圖控制器
  • 與IB完美集成,可以通過熟悉的選項 - 拖放來配置連接插座。

創建一個UITextField子類,它具有一個名為nextField的IBOutlet屬性。 這是標題:

@interface SOTextField : UITextField

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

@end

這是實施:

@implementation SOTextField

@end

在視圖控制器中,您將創建-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;
}

在IB中,更改您的UITextField以使用SOTextField類。 接下來,同樣在IB中,將每個'SOTextFields'的委托設置為'文件所有者'(這是您放置委托方法的代碼的位置 - textFieldShouldReturn)。 這種設計的優點在於,現在您只需右鍵單擊任何textField,並將nextField出口分配給您希望成為下一個響應者的下一個SOTextField對象。

在IB中分配nextField

此外,你可以做很酷的事情,比如循環textFields,這樣在最后一個失去焦點后,第一個會再次獲得焦點。

這可以很容易地擴展到自動分配returnKeyType的的SOTextFieldUIReturnKeyNext如果有nextField分配-少了一個東西手動配置。

這是一個沒有代表團的人:

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];

使用(大多數未知的) UIControlEventEditingDidEndOnExit UITextField操作。

您還可以輕松地將其連接到故事板中,因此不需要委托代碼。

編輯:實際上我無法弄清楚如何在故事板中將其連接起來。 對於這個控件事件,似乎並不是一個有效的第一個becomeFirstResponder者,這很可惜。 仍然,您可以將所有文本字段掛鈎到ViewController中的單個操作,然后根據發件人確定哪個textField成為becomeFirstResponder (盡管它不像上面的編程解決方案那樣優雅,所以IMO使用viewDidLoad的上述代碼執行它)。

這是我解決這個問題的方法。

為了解決這個問題(因為我討厭依賴標簽來做東西),我決定在UITextField對象中添加一個自定義屬性。 換句話說,我在UITextField上創建了一個類別,如下所示:

的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

現在,這是我如何使用它:

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

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

並實現textFieldShouldReturn方法:

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

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

    return NO; 
}

我現在有一個UITextField的鏈接列表,每個人都知道誰在下一行。

希望它會有所幫助。

一個快速擴展,應用mxcl的答案,使這特別容易(適應旅行者swift 2.3):

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)
    }
}

它易於使用:

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

對於除最后一個字段之外的所有字段,擴展名將返回按鈕設置為“Next”,對於最后一個字段,將“返回”按鈕設置為“完成”,並在點擊這些按鈕時移動焦點/關閉鍵盤。

斯威夫特<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:像這樣使用 -

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)
        }
    }

更一致和更健壯的方法是使用NextResponderTextField您可以完全從界面構建器配置它,而無需設置委托或使用view.tag

你需要做的就是

  1. UITextField的類類型設置為NextResponderTextField 在此輸入圖像描述
  2. 然后設置nextResponderField的出口指向下一個響應者,它可以是任何UITextField或任何UIResponder子類。 它也可以是一個UIButton,並且該庫足夠聰明,只有在啟用時才能觸發按鈕的TouchUpInside事件。 在此輸入圖像描述在此輸入圖像描述

這是圖書館的行動:

在此輸入圖像描述

我喜歡Anth0和Answerbot已經提出過的OO解決方案。 但是,我正在開發一個快速小巧的POC,所以我不想讓子類和類別混亂。

另一個簡單的解決方案是創建一個NSArray字段,並在按下下一個字段時查找下一個字段。 不是OO解決方案,但快速,簡單且易於實施。 此外,您可以一目了然地查看和修改訂購。

這是我的代碼(基於此線程中的其他答案):

@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;
}
  • 我總是返回NO,因為我不想插入換行符。 我以為我會指出這一點,因為當我返回YES時它將自動退出后續字段或在我的TextView中插入換行符。 我花了一點時間才弄明白。
  • activeField會跟蹤活動字段,以防需要滾動來從鍵盤中取消顯示字段。 如果您有類似的代碼,請確保在更改第一個響應者之前分配activeField。 立即更改第一響應者並立即觸發KeyboardWasShown事件。

這是使用UIControl上的類別進行Tab鍵的實現。 這個解決方案具有Michael和Anth0方法的所有優點,但適用於所有UIControl,而不僅僅是UITextField 它還可以與Interface Builder和故事板無縫協作。

源和示例應用程序: UIControlsWithTabbing的GitHub存儲庫

用法:

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField transferFirstResponderToNextControl];
    return NO;
}

在Interface Builder中分配nextControl

標題:

//
// UIControl+NextControl.h
// UIControlsWithTabbing
//

#import <UIKit/UIKit.h>

@interface UIControl (NextControl)

@property (nonatomic, weak) IBOutlet UIControl *nextControl;

- (BOOL)transferFirstResponderToNextControl;

@end

執行:

#import "UIControl+NextControl.h"
#import <objc/runtime.h>

static char defaultHashKey;

@implementation UIControl (NextControl)

- (UIControl *)nextControl
{
    return objc_getAssociatedObject(self, &defaultHashKey);
}

- (void)setNextControl:(UIControl *)nextControl
{
    objc_setAssociatedObject(self, &defaultHashKey, nextControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)transferFirstResponderToNextControl
{
    if (self.nextControl)
    {
        [self.nextControl becomeFirstResponder];

        return YES;
    }

    [self resignFirstResponder];

    return NO;
}

@end

我嘗試了很多代碼,最后,這在Swift 3.0中為我工作最新[2017年3月]

ViewController類應繼承UITextFieldDelegate以使此代碼正常工作。

class ViewController: UIViewController,UITextFieldDelegate  

添加帶有正確標記號的文本字段,此標記號用於根據分配給它的增量標記號將控件帶到適當的文本字段。

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

在上面的代碼中, returnKeyType = UIReturnKeyType.next將使Key pad返回鍵顯示為Next你還有其他選項如Join/Go等,根據你的應用程序更改值。

這個textFieldShouldReturn是一個UITextFieldDelegate控制的方法,這里我們有基於Tag值增量的下一個字段選擇

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
 }
 -(BOOL)textFieldShouldReturn:(UITextField *)textField
{
   [[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
   return YES;
}

退出一個文本字段后,調用[otherTextField becomeFirstResponder],下一個字段獲得焦點。

這實際上可能是一個棘手的問題,因為您通常還需要滾動屏幕或以其他方式調整文本字段的位置,以便在編輯時很容易看到。 只需確保以不同的方式進出文本字段並進行大量測試,並提前離開(總是讓用戶選擇關閉鍵盤而不是去下一個字段,通常使用“完成”導航欄)

我很驚訝這里有多少答案無法理解一個簡單的概念:瀏覽應用程序中的控件並不是視圖本身應該做的事情。 控制器的工作是決定使哪個控制器成為下一個第一響應者。

此外,大多數答案僅適用於向前導航,但用戶可能也想要倒退。

所以這就是我想出來的。 您的表單應由視圖控制器管理,視圖控制器是響應程序鏈的一部分。 所以你可以完全自由地實現以下方法:

#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];
}

可以應用用於預先滾動屏幕外響應者的附加邏輯。

這種方法的另一個優點是你不需要子類化你可能想要顯示的各種控件(比如UITextField ),而是可以在控制器級別管理邏輯,說實話,這是正確的地方 。所以。

按下“完成”按鈕時,解除鍵盤的一種非常簡單的方法是:

在標題中創建一個新的IBAction

- (IBAction)textFieldDoneEditing:(id)sender;

在實現文件(.m文件)中添加以下方法:

- (IBAction)textFieldDoneEditing:(id)sender 
{ 
  [sender resignFirstResponder];
}

然后,當您將IBAction鏈接到文本字段時 - 鏈接到“退出時結束”事件。

首先在xib中設置鍵盤返回鍵,否則你可以在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;
}

我已經添加了PeyloW的答案,以防你想要實現上一個/下一個按鈕功能:

- (IBAction)moveThroughTextFields:(UIBarButtonItem *)sender 
{
    NSInteger nextTag;
    UITextView *currentTextField = [self.view findFirstResponderAndReturn];

    if (currentTextField != nil) {
        // I assigned tags to the buttons.  0 represent prev & 1 represents next
        if (sender.tag == 0) {
            nextTag = currentTextField.tag - 1;

        } else if (sender.tag == 1) {
            nextTag = currentTextField.tag + 1;
        }
    }
    // Try to find next responder
    UIResponder* nextResponder = [self.view viewWithTag:nextTag];
    if (nextResponder) {
        // Found next responder, so set it.
        // I added the resign here in case there's different keyboards in place.
        [currentTextField resignFirstResponder];
        [nextResponder becomeFirstResponder];
    } else {
        // Not found, so remove keyboard.
        [currentTextField resignFirstResponder];

    }
}

你把UIView子類化的地方是這樣的:

@implementation UIView (FindAndReturnFirstResponder)
- (UITextView *)findFirstResponderAndReturn
{
    for (UITextView *subView in self.subviews) {
        if (subView.isFirstResponder){
            return subView;
        }
    }
    return nil;
}
@end

我嘗試使用更復雜的方法來解決這個問題,該方法基於在UITableView為每個單元格(或UITextField )分配一個可以在以后檢索的唯一標記值: activate-next-uitextfield-in-uitableview-ios

我希望這有幫助!

我剛剛在處理GNTextFieldsCollectionManager這個東西時創建了新的Pod。 它會自動處理下一個/最后一個textField問題,並且非常易於使用:

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

抓取按視圖層次結構(或標記)顯示的所有文本字段,或者您可以指定自己的textFields數組。

Swift 3.1中的解決方案,在連接文本字段后,IBOutlets在viewDidLoad中設置文本字段委托,然后在textFieldShouldReturn中導航您的操作

class YourViewController: UIViewController,UITextFieldDelegate {

        @IBOutlet weak var passwordTextField: UITextField!
        @IBOutlet weak var phoneTextField: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()
            self.passwordTextField.delegate = self
            self.phoneTextField.delegate = self
            // Set your return type
            self.phoneTextField.returnKeyType = .next
            self.passwordTextField.returnKeyType = .done
        }

        func textFieldShouldReturn(_ textField: UITextField) -> Bool{
            if textField == self.phoneTextField {
                self.passwordTextField.becomeFirstResponder()
            }else if textField == self.passwordTextField{
                // Call login api
                self.login()
            }
            return true
        }

    }

大家好,請看這個

- (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;    
  }
}

我更喜歡:

@interface MyViewController : UIViewController
@property (nonatomic, retain) IBOutletCollection(UIView) NSArray *inputFields;
@end

在NIB文件中,我將textFields以所需順序掛鈎到此inputFields數組中。 之后,我對UITextField的索引進行了簡單測試,該索引報告用戶點擊了返回:

// for UITextField
-(BOOL)textFieldShouldReturn:(UITextField*)textField {
    NSUInteger index = [_inputFields indexOfObject:textField];
    index++;
    if (index < _inputFields.count) {
        UIView *v = [_inputFields objectAtIndex:index];
        [v becomeFirstResponder];
    }
    return NO;
}

// for UITextView
-(BOOL)textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text {
    if ([@"\n" isEqualToString:text]) {
        NSUInteger index = [_inputFields indexOfObject:textView];
        index++;
        if (index < _inputFields.count) {
            UIView *v = [_inputFields objectAtIndex:index];
            [v becomeFirstResponder];
        } else {
            [self.view endEditing:YES];
        }
        return NO;
    }
    return YES;
}
if (cell == nil)
{
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    txt_Input = [[ UITextField alloc] initWithFrame:CGRectMake(0, 10, 150, 30)];
    txt_Input.tag = indexPath.row+1;
    [self.array_Textfields addObject:txt_Input]; // Initialize mutable array in ViewDidLoad
}

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

    int tag = ( int) textField.tag ;
    UITextField * txt = [  self.array_Textfields objectAtIndex:tag ] ;
    [ txt becomeFirstResponder] ;
    return YES ;
}

我的故事板中有大約10多個UITextField,我啟用下一個功能的方式是創建一個UITextField數組,並使下一個UITextField成為第一個響應者。 這是實現文件:

#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

這在Xamarin.iOS / Monotouch中適用於我。 將鍵盤按鈕更改為Next,將控件傳遞給下一個UITextField,並在最后一個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.
    };
  }
}

ViewDidLoad中你將擁有:

如果您的TextFields現在沒有設置標簽:

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

只是電話

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.

這是一個簡單的快速解決方案,沒有標簽使用,沒有故事板技巧......

只需使用此擴展程序:

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
    }

}

並使用您的textfield委托調用它(例如):

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

如果有人想這樣。 我認為這是最接近有關要求的要求

在此輸入圖像描述

以下是我實現這一點的方法

使用為每個要進行設置的文本字段添加附件視圖

func setAccessoryViewFor(textField : UITextField)    {
    let toolBar = UIToolbar()
    toolBar.barStyle = .default
    toolBar.isTranslucent = true
    toolBar.sizeToFit()

    // Adds the buttons

    // Add previousButton
    let prevButton = UIBarButtonItem(title: "<", style: .plain, target: self, action: #selector(previousPressed(sender:)))
    prevButton.tag = textField.tag
    if getPreviousResponderFor(tag: textField.tag) == nil {
        prevButton.isEnabled = false
    }

    // Add nextButton
    let nextButton = UIBarButtonItem(title: ">", style: .plain, target: self, action: #selector(nextPressed(sender:)))
    nextButton.tag = textField.tag
    if getNextResponderFor(tag: textField.tag) == nil {
        nextButton.title = "Done"
    }

    let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    toolBar.setItems([prevButton,spaceButton,nextButton], animated: false)
    toolBar.isUserInteractionEnabled = true
    textField.inputAccessoryView = toolBar
}

使用以下功能來處理水龍頭

func nextPressed(sender : UIBarButtonItem) {
    if let nextResponder = getNextResponderFor(tag: sender.tag) {
        nextResponder.becomeFirstResponder()
    } else {
        self.view.endEditing(true)
    }

}

func previousPressed(sender : UIBarButtonItem) {
    if let previousResponder = getPreviousResponderFor(tag : sender.tag)  {
        previousResponder.becomeFirstResponder()
    }
}

func getNextResponderFor(tag : Int) -> UITextField? {
    return self.view.viewWithTag(tag + 1) as? UITextField
}

func getPreviousResponderFor(tag : Int) -> UITextField? {
    return self.view.viewWithTag(tag - 1) as? UITextField
}

您需要按順序給出textFields標簽,您希望下一個/ prev按鈕響應。

一種更安全、更直接的方法,假設:

  • 文本字段委托設置為您的視圖控制器
  • 所有文本字段都是同一視圖的子視圖
  • 文本字段按您要進行的順序具有標簽(例如,textField2.tag = 2、textField3.tag = 3 等)
  • 當您點擊鍵盤上的返回按鈕時,將移動到下一個文本字段(您可以將其更改為nextdone等)
  • 您希望鍵盤在最后一個文本字段后關閉

斯威夫特 4.1:

extension ViewController: UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        let nextTag = textField.tag + 1
        guard let nextTextField = textField.superview?.viewWithTag(nextTag) else {
            textField.resignFirstResponder()
            return false
        }

    nextTextField.becomeFirstResponder()

    return false

    }
}

在textFieldShouldReturn中,你應該檢查你當前所在的文本字段是不是最后一個,當他們點擊下一個,如果它不要關閉鍵盤..

這是一個老帖子,但頁面排名很高,所以我會使用我的解決方案。

我遇到了類似的問題,最后創建了UIToolbar的子類來管理動態tableView中的下一個/上一個/完成的功能,其中包含以下部分: https//github.com/jday001/DataEntryToolbar

將工具欄設置為文本字段的inputAccessoryView並將其添加到其字典中。 這使您可以向前和向后循環,即使是動態內容也是如此。 如果要在textField導航發生時觸發自己的功能,則有委托方法,但您不必處理管理任何標記或第一響應者狀態。

GitHub鏈接中有代碼片段和示例應用程序,以幫助實現詳細信息。 您將需要自己的數據模型來跟蹤字段內的值。

我一直在使用Michael G. Emmons的答案大約一年,效果很好。 我最近注意到,調用resignFirstResponder然后立即調用FirstFirstResponder會導致鍵盤“出現故障”,消失然后立即出現。 如果nextField可用,我稍微更改了他的版本以跳過resignFirstResponder。

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

    if ([textField isKindOfClass:[NRTextField class]])
    {
        NRTextField *nText = (NRTextField*)textField;
        if ([nText nextField] != nil){
            dispatch_async(dispatch_get_main_queue(),
                           ^ { [[nText nextField] becomeFirstResponder]; });

        }
        else{
            [textField resignFirstResponder];
        }
    }
    else{
        [textField resignFirstResponder];
    }

    return true;

}

您可以使用IQKeyboardManager庫來執行此操作。 它處理所有事情,你不需要任何額外的setup.IQKeyboardManager可以通過CocoaPods,安裝它只需將以下行添加到你的Podfile:

pod 'IQKeyboardManager'

或者只是將IQKeyBoardManager目錄從演示項目拖放到您的項目中。 而已。 你可以從https://github.com/hackiftekhar/IQKeyboardManager找到IQKeyBoardManager目錄

這是Anth0答案的 Swift 3 版本。 我將其發布在這里是為了幫助任何想要利用他出色回答的快速開發人員! 當您設置關聯對象時,我冒昧地添加了“Next”的返回鍵類型。

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)
    }
  }
}

這是另一個擴展,展示了使用上述代碼循環瀏覽 UITextFields 列表的可能性。

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

    next.becomeFirstResponder()
    return false
  }
}

然后在你的 ViewController 或任何地方,你可以像這樣設置你的文本字段......

@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.

如果沒有使用標簽並且沒有為nextField / nextTextField添加屬性,您可以嘗試使用它來模擬TAB,其中“testInput”是您當前的活動字段:

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];
                                                    }];

Swift 3 解決方案,使用UITextField's有序數組

func nextTextField() {
    let textFields = // Your textfields array

    for i in 0 ..< textFields.count{
        if let textfield = textFields[i], textfield.isFirstResponder{
            textfield.resignFirstResponder()
            if i+1 < textFields.count, let nextextfield = textFields[i+1]{
                nextextfield.becomeFirstResponder()
                return
            }
        }
    }
}

我已經實現了一個動態解決方案,用於在視圖層次結構中導航響應者。 這個想法是,根據需要向前或向后搜索整個層次結構以尋找下一個可能的響應者。

用於搜索的 UIView 擴展:

UIView+Cat.h

- (UIResponder *)getNextFirstResponder;
- (UIResponder *)getPreviousFirstResponder;

UIView+Cat.m

- (UIResponder *)getPreviousFirstResponder {
    if (!self.superview) {
        return nil;
    }
    
    BOOL hasreachedself = NO;
    for (NSInteger i = self.superview.subviews.count-1; i >= 0; i--) {
        UIView *v = [self.superview.subviews objectAtIndex:i];
        if (v == self) {
            hasreachedself = YES;
            continue;
        }
        if (!hasreachedself) continue;
        
        if ([v canBecomeFirstResponder] && !v.hidden) {
            return v;
        }
        UIResponder *subResponder = [self getNextFirstResponderInView:v];
        if (subResponder) {
            return subResponder;
        }
    }
    
    //search hierachicaly in superviews
    return [self.superview getPreviousFirstResponder];
}

- (UIResponder *)getNextFirstResponder {
    if (!self.superview) {
        return nil;
    }
    
    BOOL hasreachedself = NO;
    for (UIView *v in self.superview.subviews) {
        if (v == self) {
            hasreachedself = YES;
            continue;
        }
        if (!hasreachedself) continue;
        
        if ([v canBecomeFirstResponder] && !v.hidden) {
            return v;
        }
        UIResponder *subResponder = [self getNextFirstResponderInView:v];
        if (subResponder) {
            return subResponder;
        }
    }
    
    //search hierachicaly in superviews
    return [self.superview getNextFirstResponder];
}

- (UIResponder *)getNextFirstResponderInView:(UIView *)view {
    if ([view canBecomeFirstResponder] && !view.hidden) {
        return view;
    }
    for (UIView *v in view.subviews) {
        UIResponder *subResponder = [self getNextFirstResponderInView:v];
        if (subResponder) {
            return subResponder;
        }
    }
    return nil;
}

用法:將 UIToolbar 添加到 UITextField / UITextView(在類別或子類中):

- (void)addToolbarInputAccessoryView {
    UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectZero];
    UIBarButtonItem *prev = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"chevron.up"] style:UIBarButtonItemStylePlain target:self action:@selector(moveToPreviousFirstResponder)];
    UIBarButtonItem *next = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"chevron.down"] style:UIBarButtonItemStylePlain target:self action:@selector(moveToNextFirstResponder)];
    UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
    UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(hideKeyboard)];
    toolbar.items = @[prev, next, space, done];
    
    if ([[UITextField appearance] keyboardAppearance] == UIKeyboardAppearanceDark) {
        toolbar.barStyle = UIBarStyleBlack;
        toolbar.translucent = YES;
        [toolbar setBarTintColor:[UIColor blackColor]];
        [toolbar setTintColor:[UIColor whiteColor]];
    }
    
    [toolbar sizeToFit];
    
    self.inputAccessoryView = toolbar;
}

- (void)hideKeyboard {
    [self resignFirstResponder];
}

- (void)moveToNextFirstResponder {
    UIResponder *next = [self getNextFirstResponder];
    if (next) {
        [next becomeFirstResponder];
    }
    else {
        [self resignFirstResponder];
    }
}

- (void)moveToPreviousFirstResponder {
    UIResponder *prev = [self getPreviousFirstResponder];
    if (prev) {
        [prev becomeFirstResponder];
    }
    else {
        [self resignFirstResponder];
    }
}

Swift 4 for mxcl 答案:

txtFirstname.addTarget(txtLastname, action: 
#selector(becomeFirstResponder), for: UIControlEvents.editingDidEndOnExit)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM