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


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
  • 컨트롤 UITextFieldUITextView키보드 입력 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. 당신의 클래스 유형을 설정 UITextFieldNextResponderTextField 여기에 이미지 설명을 입력하십시오
  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.