텍스트 필드를 탐색하는 방법 (다음 / 완료 단추)


487

iPhone 키보드의 "다음"버튼으로 모든 텍스트 필드를 탐색하려면 어떻게해야합니까?

마지막 텍스트 필드는 키보드를 닫아야합니다.

IB 버튼 (다음 / 완료)을 설정했지만 이제 막혔습니다.

textFieldShouldReturn 액션을 구현했지만 이제 다음 및 완료 버튼으로 키보드가 닫힙니다.


아래 답변을 확인하십시오. 내 경험상 그것은 일반적으로 주어진 대답보다 더 좋고 더 완벽한 해결책입니다. 나는 왜 누군가가 그것을 부정적인 평가를 할 것인지 전혀 모른다.
— memmons

비슷한 문제가 있지만 Cordova, Phonegap에서 해결할 수있는 방법이 있습니까?

답변:


578

Mac OS X 용 Cocoa에는 다음 응답자 체인이 있으며, 다음 응답기에 텍스트 필드에 어떤 제어에 초점을 두어야하는지 물어볼 수 있습니다. 이것이 텍스트 필드 사이의 탭을 작동시키는 것입니다. 그러나 iOS 기기에는 키보드가없고 터치 만 있기 때문에이 개념은 Cocoa Touch로 전환해도 살아남지 못했습니다.

어쨌든 다음 두 가지 가정을 통해 쉽게 수행 할 수 있습니다.

  1. 모든 "tabbable" UITextField은 동일한 상위 뷰에 있습니다.
  2. "탭 순서"는 태그 속성으로 정의됩니다.

이것을 가정하면 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.
}

더 많은 코드를 추가하면 가정도 무시할 수 있습니다.

스위프트 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!

6
때때로 키보드 상단에 "다음"및 "이전"버튼이 있습니다. 적어도 브라우저에서.
— Tim Büthe

31
pedantic하기 위해서, 나는이 게시물에서 약간의 잘못된 정보를 정리하고 싶습니다. OS X에서 탭 순서는 NSView의 setNextKeyView : (setNextResponder : 아님) 메소드를 통해 설정됩니다. 응답기 체인은 이것과는 별개입니다. "대상화되지 않은"동작을 처리하는 응답자 객체의 계층 구조입니다. 응답자 체인은 일반적으로 창과 컨트롤러, 앱 위임까지의 뷰 계층 구조를 따릅니다. 많은 정보를위한 Google "응답자 체인"
— davehayden

텍스트 필드의 superview는 UITableViewCell입니다. 연결된 UITableView텍스트 에 액세스하여 찾고있는 텍스트 필드가 포함 된 셀을 가져와야합니다.
— Michael Mior

13
UITextFieldDelegate텍스트 필드 델리게이트 를 추가 하고 설정하는 것을 잊지 마십시오 .
— Sal

1
스토리 보드에서 TAG 옵션을 설정하라는 친숙한 알림 (제 경우). TextFields 태그를 오름차순으로 수동 설정해야합니다. 이 솔루션이 작동하려면 (첫 번째는 0, 두 번째는 1 등입니다.)
— Joakim

170

가 많이 나는 그것을 처음봤을 때 저를 멀리 날려 더 우아한 해결책은. 혜택:

  • 텍스트 필드가 포커스가 다음에 어디로 가야하는지 알고있는 OSX 텍스트 필드 구현에 더 가깝습니다.
  • 태그 사용 또는 태그 사용에 의존하지 않음-이 사용 사례에서 취약한 IMO
  • 컨트롤 UITextField과 UITextView키보드 입력 UI 컨트롤 모두에서 작동하도록 확장 가능
  • 상용구 UITextField 델리게이트 코드로 뷰 컨트롤러를 어지럽히 지 않습니다.
  • IB와 잘 통합되며 친숙한 옵션 끌어서 놓기를 통해 구성하여 콘센트를 연결할 수 있습니다.

IBOutletnextField라는 속성 이있는 UITextField 서브 클래스를 작성하십시오 . 헤더는 다음과 같습니다.

@interface SOTextField : UITextField

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

@end

구현은 다음과 같습니다.

@implementation SOTextField

@end

뷰 컨트롤러에서 -textFieldShouldReturn:대리자 메서드를 만듭니다 .

- (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에서 SOTextField클래스 를 사용하도록 UITextField를 변경하십시오 . 다음으로 IB에서도 각 'SOTextFields'에 대한 대리자를 'File 's Owner'(대리자 메서드-textFieldShouldReturn에 대한 코드를 넣는 위치)로 설정합니다. 이 디자인의 장점은 이제 모든 textField를 마우스 오른쪽 단추로 클릭하고 nextField 콘센트를 SOTextField다음 응답자가 될 다음 객체에 할당 할 수 있다는 것입니다.

IB에서 nextField 할당

또한 텍스트 필드 루프와 같은 멋진 작업을 수행하여 마지막 필드가 포커스를 잃은 후 첫 번째 필드가 다시 포커스를받을 수 있습니다.

nextField가 할당 된 경우 수동으로 구성하는 것 중 하나를 수동으로 구성 하기 위해 returnKeyType의 를 자동으로 할당하도록 쉽게 확장 할 수 있습니다 .SOTextFieldUIReturnKeyNext


3
태그를 사용하는 것보다 IB 통합, 특히 IB를 다른 IDE와 잘 통합하는 최신 버전의 Xcode와의 IB 통합을 정말 좋아합니다.
— memmons

4
태그는 더 쉬워 지지만이 방법은 더 좋으며 좋은 OO 디자인에 더 적합합니다.
— Brain2000

1
나는이 솔루션을 정말로 좋아하지만에서 각 텍스트 필드를 별도의 셀보기에서 사용하고 UITableView있습니다. IBOutlet각 텍스트 필드의 뷰 컨트롤러에 속성이 있지만 nextField속성을 파일 소유자의 해당 속성에 연결할 수는 없습니다 . 이 시나리오에서 어떻게 작동시킬 수 있습니까?
— Jay Imerman 2016 년

1
@JayImerman IB를 사용하면 셀 사이에 콘센트를 연결할 수 없습니다. 따라서 IB는이 시나리오에서 절대로 사용되지 않습니다. 그러나 코드를 사용하여 nextField 속성을 연결할 수 있습니다. textField1.nextField = textField2; textField2.nextField = textField3;등
— memmons

2
이 답변이 최신 버전의 xcode 및 ios에 대해 업데이트 될 수 있습니까?
— lostintranslation

82

대표단이없는 사람은 다음과 같습니다.

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에서 단일 액션 becomeFirstResponder에 연결하여 보낸 사람 을 기준으로 텍스트 필드를 결정할 수 있습니다 (그러나 위의 프로그래밍 솔루션만큼 우아하지 않으므로 IMO는 위의 코드로 수행합니다 viewDidLoad).


18
매우 간단하고 효과적인 솔루션입니다. 체인의 마지막 것은 예를 들어 트리거 할 수 있습니다 [tfN addTarget:self action:@selector(loginButtonTapped:) forControlEvents:UIControlEventEditingDidEndOnExit];. 감사합니다 @mxcl.
— msrdjan

스토리 보드에서 어떻게 하시겠습니까?
— Pedro Borges

이것은 아마도 가장 좋은 대답 일 것입니다.
— daspianist

이것은 스위프트 2에서도 완벽하게 작동했습니다. 이것은 델리게이트를 추가하는 것보다 훨씬 간단하고 빠른 솔루션입니다.
— ton

2
스위프트 4 : txtFirstname.addTarget (txtLastname, action : #selector (becomeFirstResponder), for : UIControlEvents.editingDidEndOnExit)
— realtimez

78

이 문제에 대한 해결책은 다음과 같습니다.

이 문제를 해결하기 위해 (그리고 태그를 사용하여 작업하는 것을 싫어하기 때문에) 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의 링크 된 목록이 있으며 각 줄은 누가 다음 줄인지 알고 있습니다.

도움이 되길 바랍니다.


11
이것이 최선의 해결책이며 답으로 선택해야합니다.
— Giovanni

4
이 솔루션은 저에게 효과적이었습니다. 변경 한 것은 nextTextField 및 IBOutlet을 만드는 것이므로 스토리 보드 내에서 체인을 설정할 수 있습니다.
— zachzurn

내가 좋아하는 답변을 저장할 수 있다면 이것 중 하나 일 것입니다. 깨끗한 솔루션에 감사드립니다!
— 매트 베커

1
IB가 원하는 경우 이러한 속성을 IBOutlets로 설정할 수도 있습니다. . 이름 충돌에주의하십시오-Apple은이를 UIResponder에 추가 할 수 있습니다.
— Jasper Blues

이 솔루션은 인터페이스 빌더와 호환됩니까? 프로그래밍 방식으로 nextTextField를 설정하는 대신 IBOutlets 및 스토리 보드 참조를 사용할 수 있습니까? IB에서는 UITextField 클래스를 범주로 설정할 수 없습니다.
— Pedro Borges

46

mxcl의 답변을 적용하여 특히 쉽게 만들 수있는 빠른 확장 기능 (Traveler의 신속한 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])

확장 기능은 마지막 필드를 제외한 모든 필드에 대해 리턴 버튼을 "다음"으로 설정하고 마지막 필드에 대해서는 "완료"로 설정하고 탭을 누르면 포커스를 이동 / 닫습니다.

스위프트 <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)
    }
}

스위프트 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)
        }
    }

5
BTW, 완료 작업을 수행하려는 경우 View Controller가을 준수하도록 UITextFieldDelegate하고 구현할 수 func textFieldDidEndEditing(textField: UITextField)있습니다. 경우 일찍 반환하십시오 textField != field3.
— Ryan

25

보다 일관되고 강력한 방법은 NextResponderTextField 를 사용 하는 것 view.tag입니다. 델리게이트를 설정하거나 사용할 필요없이 인터페이스 빌더에서 완전히 구성 할 수 있습니다 .

당신이해야 할 일은

  1. 당신의 클래스 유형을 설정 UITextField할NextResponderTextField 여기에 이미지 설명을 입력하십시오
  2. 그런 다음의 출구를 설정 nextResponderField그것이 무엇이든 할 수있는 다음 응답을 가리 키도록 UITextField또는 UIResponder서브 클래스입니다. 또한 UIButton 일 수 있으며 라이브러리는 TouchUpInside활성화 된 경우에만 버튼 의 이벤트 를 트리거 할 수있을 정도로 똑똑 합니다. 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

작동중인 라이브러리는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오


예. IB가 기본적으로 작동하는 방식 인 Swift 3.1, 매우 우아한 솔루션과 함께 잘 작동합니다.
— Richard

죄송합니다. 질문이 없습니다.
— mohamede1945

1
NextResponderTextField가 나를 위해 일했습니다. 하나의 신속한 파일로 빠르고 사용하기 쉬웠으며 소란없이 정확히 필요한 것을 수행했습니다.
— Pat McG

이것은 잘 작동합니다! 참고 : 예와 같이 Return 키를 수동으로 변경하여 "Next"와 "Done"을 표시해야했습니다.
— Mike Miller

14

나는 Anth0과 Answerbot이 이미 제안한 OO 솔루션을 좋아합니다. 그러나 나는 빠르고 작은 POC를 연구하고 있었기 때문에 하위 클래스와 범주로 물건을 어지럽히고 싶지 않았습니다.

또 다른 간단한 해결책은 NSArray 필드를 만들고 next를 누를 때 다음 필드를 찾는 것입니다. 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 이벤트가 시작됩니다.

좋은! 간단하고 코드는 모두 하나의 모델에 있습니다. AnthO도 매우 좋습니다.
— Seamus

이 코드를 사용하면 유지주기의 위험이 있습니다. fieldArray가 모든 IBOutlet에 사용 된 약한 참조를 배열 자체의 강한 참조로 변환합니다.
— Michael Long

11

다음은 UIControl에서 카테고리를 사용하여 탭하는 구현입니다. 이 솔루션은 Michael과 Anth0의 메소드의 모든 장점을 가지고 있지만 UITextFields 뿐만 아니라 모든 UIControl에 작동합니다 . 또한 Interface Builder 및 스토리 보드와 완벽하게 작동합니다.

소스 및 샘플 앱 : UIControlsWithTabbing 용 GitHub 저장소

용법:

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

인터페이스 빌더에서 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

1
때로는 다음 위젯이 컨트롤 (예 : UITextView)이 아니기 때문에 탭하기를 원할 수도 있습니다. UITextField에서 UIResponder로 유형을 변경하십시오.
— Pedro Borges

1
훌륭하고 훌륭한 문서로 작동합니다. 몇 시간 전에이 질문으로 스크롤하기를 바랍니다. :)
— Javier Cadiz

10

많은 코드를 시도해 보니 마지막으로 Swift 3.0 Latest [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키패드 반환 키를 표시 하는 위치는 어디 입니까?NextJoin/Go

이것은 textFieldShouldReturnUITextFieldDelegate 제어 방법이며 여기에서 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
 }

입력 필드가 인 경우를 제외하고 작동합니다 UITextView. 어떻게 조합 하여이 작업을 수행 할 수 있습니까?
— T9b

@ T9b 정확히 필요한 것은 무엇입니까? 앱에서 어떤 유형 제어를 사용하고 있습니까?
— 바네스 모한 쿠마르

두 가지 유형의 텍스트 입력이 있습니다. 첫 번째는 UITextField(한 줄로 입력), 다른 하나는 UITextView(여러 줄로 입력)입니다. UITextView분명히이 코드에는 응답하지 않지만 형태로 사용될 수 있습니다.
— T9b

예,이 부분은 UITextField 전용이며 UITextView의 이벤트를 수정 해 보셨습니까?
— 바네스 MOHANKUMAR

9

한 텍스트 필드를 종료 한 후 [otherTextField가 FirstResponder]를 호출하면 다음 필드에 포커스가 있습니다.

종종 화면을 스크롤하거나 텍스트 필드의 위치를 ​​조정하여 편집 할 때 쉽게 볼 수 있기 때문에 다루기가 까다로울 수 있습니다. 다른 방법으로 텍스트 필드에 들어오고 나 가면서 많은 테스트를 수행해야합니다. 초기에는 항상 떠나야합니다. 탐색 모음)


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

이 목적으로 태그를 사용해서는 안됩니다.
— Christian Schnorr

7

'완료'버튼을 눌렀을 때 키보드를 해제하는 가장 쉬운 방법은 다음과 같습니다.

헤더에 새로운 IBAction을 만듭니다.

- (IBAction)textFieldDoneEditing:(id)sender;

구현 파일 (.m 파일)에서 다음 메소드를 추가하십시오.

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

그런 다음 IBAction을 텍스트 필드에 연결하면 'Did End On Exit'이벤트에 연결하십시오.


1
텍스트 필드를 탐색하지는 않지만 키보드를 닫고 다른 필드를 선택할 수 있습니다.
— jcrowson

7

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

7

하나의 간단한 개념을 이해하지 못하는 몇 가지 답변에 놀랐습니다. 앱의 컨트롤을 탐색하는 것은 뷰 자체가 해야하는 것이 아닙니다. 다음 응답자를 만들 제어를 결정하는 것은 컨트롤러 의 작업입니다.

또한 대부분의 답변은 앞으로 탐색에만 적용되지만 사용자는 뒤로 이동하기를 원할 수도 있습니다.

그래서 여기에 내가 생각해 낸 것이 있습니다. 양식은 뷰 컨트롤러에서 관리해야하며 뷰 컨트롤러는 응답자 체인의 일부입니다. 따라서 다음 방법을 완벽하게 구현할 수 있습니다.

#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들) 대신, 정직하자 컨트롤러 수준에서 논리를 관리 할 수 있습니다 적절한 장소 그렇게 할 .


나는 가장 관련성이 높은 답변을 찾았지만 아마도 당신이 정리할 수있는 재미있는 것을 발견했습니다. UIViewController에 의해 관리되지 않는 UITextField를 사용하는 경우 탭 키를 누르면 기본적으로 다음 UITextField가 첫 번째 응답자입니다. 그러나 각각이 VC에 의해 관리 될 때 이것은 사실이 아닙니다. 내 VC는 아무것도하지 않지만 내장 된 탭 기능이 사라졌습니다. VC의 keyCommands가 뷰의 응답자 체인에 추가되는 것으로 나타났습니다. 이것은 VC를 사용하는 경우 keyCommands를 구현해야한다는 것을 믿게합니다.
— Joey Carson

4

이전 / 다음 버튼 기능을 구현하려는 경우 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

4

안녕하세요 모두들 이걸 봐주세요

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

대신에 UIView의 * 응답자는 UIRespoder * 응답자 사용하는 것이 더 정확한 것
— 페드로 보르헤스


4

이 물건 GNTextFieldsCollectionManager를 다룰 때 방금 새로운 포드를 만들었습니다 . 다음 / 마지막 텍스트 필드 문제를 자동으로 처리하며 사용하기 매우 쉽습니다.

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

보기 계층 구조 (또는 태그)로 표시하여 정렬 된 모든 텍스트 필드를 가져 오거나 고유 한 textField 배열을 지정할 수 있습니다.


4

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
        }

    }

4

누군가가 이걸 원한다면 나는 이것이 요구되는 요구 사항에 가장 가깝다고 생각합니다.

여기에 이미지 설명을 입력하십시오

여기 내가 이것을 구현 한 방법이 있습니다.

다음을 사용하여 설정하려는 각 텍스트 필드에 대한 액세서리보기를 추가하십시오.

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 태그를 순서대로 제공해야합니다.


3

차라리 선호합니다 :

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

3
if (셀 == 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]; // ViewDidLoad에서 가변 배열 초기화
}

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

    int tag = (int) textField.tag;
    UITextField * txt = [self.array_Textfields objectAtIndex : tag];
    [txt는 FirstResponder가 됨];
    YES를 반환;
}

3

스토리 보드에 약 10+ 이상의 UITextField가 있었고 다음 기능을 활성화하는 방법은 UITextField 배열을 만들고 다음 UITextField를 firstResponder로 만드는 것입니다. 구현 파일은 다음과 같습니다.

#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

3

이것은 Xamarin.iOS / Monotouch에서 나를 위해 일했습니다. 키보드 버튼을 다음으로 변경하고 컨트롤을 다음 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.

3

이것은 태그를 사용하지 않고 스토리 보드 트릭을 사용하지 않고 신속하게 간단한 솔루션입니다 ...

이 확장명을 사용하십시오.

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
    }

}

그리고 텍스트 필드 대리인과 함께 다음과 같이 호출하십시오.

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

이것에 대한 한 가지 의견은 귀하의 방법이 모든 필드가 단일 수퍼 뷰에 수직으로 쌓여 있다고 가정한다는 것입니다. 그룹화 된 필드를 허용하지 않으며 텍스트 필드가 나란히있을 때 작동하지 않습니다. 후자는 크기 클래스를 사용하여 가로보기를 위해 양식을 재 배열하거나 양식이 태블릿에 표시 될 때 특히 중요합니다.
— Michael Long

3

다음을 가정하면 더 안전하고 직접적인 방법입니다.

  • 텍스트 필드 델리게이트가 뷰 컨트롤러로 설정됩니다.
  • 모든 텍스트 필드는 동일한 뷰의 서브 뷰입니다
  • 텍스트 필드에는 진행하려는 순서대로 태그가 있습니다 (예 : textField2.tag = 2, textField3.tag = 3 등).
  • 키보드 의 리턴 버튼을 누르면 다음 텍스트 필드로 이동합니다 ( 다음 , 완료 등으로 변경할 수 있습니다 )
  • 마지막 텍스트 필드 다음에 키보드를 닫고 싶을 때

스위프트 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

    }
}

2

textFieldShouldReturn에서 현재 켜져있는 텍스트 필드가 다음을 클릭 할 때 마지막 텍스트 필드가 아니고 키보드를 닫지 않는지 확인해야합니다.


이봐,이 작동하지만, "다음"버튼으로 다음 텍스트 필드로 어떻게 이동할 수 있습니까?
— phx

2

이 게시물은 오래된 게시물이지만 페이지 순위가 높으므로 내 솔루션에 대해 설명하겠습니다.

비슷한 문제가 발생 UIToolbar하여 섹션이있는 동적 tableView에서 다음 / 이전 / 완료 기능을 관리 하는 하위 클래스를 만들었습니다 . https://github.com/jday001/DataEntryToolbar

툴바를 텍스트 필드의 inputAccessoryView로 설정하고 사전에 추가합니다. 이를 통해 동적 컨텐츠에서도 앞뒤로 순환 할 수 있습니다. textField 탐색이 발생할 때 고유 한 기능을 트리거하려는 경우 대리자 메소드가 있지만 태그 또는 첫 번째 응답자 상태를 처리 할 필요는 없습니다.

GitHub 링크에는 코드 스 니펫 및 예제 앱이 있으며 구현 세부 정보를 도와줍니다. 필드 내부의 값을 추적하려면 자체 데이터 모델이 필요합니다.


2

태그를 사용하지 않고 nextField / nextTextField에 대한 속성을 추가하지 않으면 "testInput"이 현재 활성 필드 인 TAB를 에뮬레이트하기 위해이를 시도 할 수 있습니다.

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

2

나는 약 1 년 동안 Michael G. Emmons의 대답을 사용 해 왔으며 훌륭하게 작동합니다. 최근 resignFirstResponder를 호출 한 다음 FirstFirstsResponder가되면 즉시 키보드가 "고정"되어 사라졌다가 즉시 나타날 수 있습니다. nextField를 사용할 수 있으면 resignFirstResponder를 건너 뛰기 위해 그의 버전을 약간 변경했습니다.

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

    if ([textField isKindOfClass : [NRTextField 클래스]])
    {
        NRTextField * nText = (NRTextField *) textField;
        if ([nText nextField]! = nil) {
            dispatch_async (dispatch_get_main_queue (),
                           ^ {[[nText nextField] willFirstResponder]; });

        }
        그밖에{
            [textField resignFirstResponder];
        }
    }
    그밖에{
        [textField resignFirstResponder];
    }

    true를 반환;

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.