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

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

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

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

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



답변

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!


답변

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

  • 텍스트 필드가 포커스가 다음에 어디로 가야하는지 알고있는 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


답변

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

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


답변

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

이 문제를 해결하기 위해 (그리고 태그를 사용하여 작업하는 것을 싫어하기 때문에) 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의 답변을 적용하여 특히 쉽게 만들 수있는 빠른 확장 기능 (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)
        }
    }


답변

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

당신이해야 할 일은

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

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

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


답변

나는 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 이벤트가 시작됩니다.