UITextField 텍스트 변경 이벤트


563

textField에서 텍스트 변경 사항을 어떻게 감지합니까? 대리자 메서드가 shouldChangeCharactersInRange작동하지만 내 요구를 정확하게 충족시키지 못했습니다. YES를 반환 할 때까지 textField 텍스트는 다른 관찰자 메서드에서 사용할 수 없습니다.

예를 들어 내 코드 calculateAndUpdateTextFields에서 업데이트 된 텍스트를 얻지 못하면 사용자가 입력했습니다.

textChangedJava 이벤트 핸들러 와 같은 것을 얻을 수있는 방법 입니다.

- (BOOL)textField:(UITextField *)textField 
            shouldChangeCharactersInRange:(NSRange)range 
            replacementString:(NSString *)string 
{
    if (textField.tag == kTextFieldTagSubtotal 
        || textField.tag == kTextFieldTagSubtotalDecimal
        || textField.tag == kTextFieldTagShipping
        || textField.tag == kTextFieldTagShippingDecimal) 
    {
        [self calculateAndUpdateTextFields];

    }

    return YES;
}

3
이것은 완전히 잘못된 1000 표의 SO 답변의 좋은 예입니다 . iOS는 까다 롭고, 부끄러 우며, 고지로 가득합니다.
— Fattie

답변:


1056

에서 적절한 방법 텍스트 변경 콜 다시 UITextField에해야 할 일 :

UITextField 컨트롤에 보낸 문자를 다음과 같이 잡습니다.

// Add a "textFieldDidChange" notification method to the text field control.

Objective-C에서 :

[textField addTarget:self 
              action:@selector(textFieldDidChange:) 
    forControlEvents:UIControlEventEditingChanged];

스위프트에서 :

textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)

그런 다음 textFieldDidChange메소드에서 textField의 내용을 검사하고 필요에 따라 테이블 뷰를 다시로드 할 수 있습니다.

그것을 사용하고 calculateAndUpdateTextFields를 으로 넣을 수 있습니다 selector.


18
이것은 더 나은 솔루션입니다. Nibs 또는 Storyboards에서 이것을 설정할 수 있고 과도한 UITextFieldTextDidChangeNotification 코드를 작성할 필요가 없습니다
— PostCodeism

3
유일한 문제는. (의 UITextField *)에 textField shouldChangeCharactersInRange : (NSRange) 범위 replacementString (는 NSString *) 문자열 (BOOL)에 textField - 그것은과 함께 나를 위해 작동하지 않습니다
— iWheelBuy

4
@iWheelBuy 이 메소드에서 NO리턴 할 때 NO기본적으로 필드의 텍스트가 변경되지 않아야 하므로 논리적 인을 리턴 할 때만 리턴 됩니다.
— gitaarik

2
이로 인해 변경이 발생한 편집에 좋습니다. 다음을 통해 발생하는 프로그래밍 방식 변경 사항은 포착하지 않습니다 textField.text = "Some new value";. 이것을 잡는 영리한 방법이 있습니까?
— Benjohn

10
당신은 애플과 함께 UITextFieldDelegate무언가 func textField: UITextField, didChangeText text: String가 포함되었을 것이라고 생각했을 것입니다.하지만 ... (애플의 사람들에게 더러운 표정을
— Brandon

394

XenElement의 답변이 제자리에 있습니다.

UITextField를 마우스 오른쪽 버튼으로 클릭하고 "Editing Changed"send 이벤트를 서브 클래스 유닛으로 드래그하여 인터페이스 빌더에서도 위와 같은 작업을 수행 할 수 있습니다.

UITextField 변경 이벤트


2
쉽지는 않지만 10-15 분 동안 검색 한 후에 우연히 답변에 걸림돌이 될 때까지 이러한 가능성을 발견하지 못했습니다. 나에게서 또 다른 +1을 가져라. 코드 기반 솔루션과 IB 기반 솔루션 모두 이와 같은 질문에 대해 높은 투표를 한 것이 좋습니다.
— Mark Amery

방금 6.3에서 확인했는데 거기 있습니다. 이것은 UITextView이며 마우스 오른쪽 버튼으로 클릭하고 "변경된 편집"을 참조하십시오.
— William T.

4
왜 지구상에서 이름이 Editing Changed입니까? 미친! 솔루션에 감사드립니다 :-)
— malhal

3
변경 사항을 편집 할 때 이벤트가 호출되는 반면 텍스트 필드가 편집을 마치면 첫 번째 응답자를 사임 할 때 변경되는 값이 발생합니다. UITextFieldTextDidChangeNotification은 UITextField 알림이며 UIControlEventValueChanged는 UIButton 하위 클래스 인 UIControl에 의해 구현됩니다.
— Camsoft

2
나는이 텍스트 필드에서 사용자 탭과 같은 일을하지 않는주의 및 자동 고침 텍스트 콘텐츠 대체
— noobular

136

이벤트 리스너를 설정하려면 다음을 수행하십시오.

[self.textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];

실제로 들으려면 :

- (void)textFieldDidChange:(UITextField *)textField {
    NSLog(@"text changed: %@", textField.text);
}

멋있는. 이미 TextFieldDelegate 인 ViewController 기본 클래스에서 textField-change-listening을 구현하고 싶기 때문에이 방법이 마음에 듭니다. 이렇게하면 매력처럼 (하위 뷰의 textField)에 대해 Target : baseControllerMethod를 추가 할 수 있습니다. 고마워!
— HBublitz

87

빠른:

yourTextfield.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)

그런 다음 콜백 함수를 구현하십시오.

@objc func textFieldDidChange(textField: UITextField){

print("Text changed")

}

2
나를 위해 일한 나는 이미 이것을 시도했지만 함수가 textField 인수가 필요하다는 것을 깨닫지 못했습니다. 이 방법으로 문제가 해결되지 않으면 선택기에 textField를 전달할 인수가 있는지 확인하십시오 (사용하지 않더라도).
— werm098

textFieldDidChange에서 textField 앞에 _를 다음과 같이 추가하십시오. func textFieldDidChange (textField : UITextField) {...}
— Makalele

30

여기에 언급 된 바와 같이 : UITextField 텍스트 변경 이벤트 , iOS 6 (iOS 6.0 및 6.1 확인)에서는 UITextField을 관찰하는 것만으로 는 객체의 변경 사항을 완전히 감지 할 수없는 것으로 보입니다 UITextFieldTextDidChangeNotification.

내장 iOS 키보드로 직접 변경 한 내용 만 추적되는 것 같습니다. 즉 , UITextField다음과 같이 호출 하여 객체 를 변경하면 변경 myUITextField.text = @"any_text"사항에 대한 알림이 전혀 표시되지 않습니다.

이것이 버그인지 또는 의도 된 것인지 모르겠습니다. 문서에서 합리적인 설명을 찾지 못했기 때문에 버그처럼 보입니다. UITextField text change event 에도 설명 되어 있습니다 .

이것에 대한 나의 "솔루션"은 실제로 내 모든 변경 사항에 대한 알림을 실제로 게시하는 것입니다 UITextField(내장 iOS 키보드를 사용하지 않고 변경이 완료된 경우). 이 같은:

myUITextField.text = @"I'm_updating_my_UITextField_directly_in_code";

NSNotification *myTextFieldUpdateNotification  = 
  [NSNotification notificationWithName:UITextFieldTextDidChangeNotification
                  object:myUITextField];

[NSNotificationCenter.defaultCenter 
  postNotification:myTextFieldUpdateNotification];

이런 식 으로 코드에서 "수동으로"업데이트하거나 내장 iOS 키보드를 통해 객체 의 .text속성 을 변경할 때 동일한 알림을 받는다는 것을 100 % 확신합니다 UITextField.

문서화 된 동작이 아니기 때문에이 방법을 사용하면 UITextField객체 의 동일한 변경에 대해 2 개의 알림이 수신 될 수 있습니다 . 필요에 따라 ( UITextField.text변경할 때 실제로 수행하는 작업 ) 이는 불편할 수 있습니다.

알림이 UITextFieldTextDidChangeNotification자신의 알림인지 "iOS-made"인지 실제로 알아야하는 경우 약간 다른 방법으로 사용자 지정 알림 (이 아닌 사용자 지정 이름으로)을 게시하는 것입니다.

편집하다:

방금 더 나은 것으로 생각되는 다른 접근법을 찾았습니다.

여기에는 Objective-C의 KVO (Key-Value Observing) 기능 ( http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid / 10000177-BCICJDHA ).

기본적으로 귀하는 자신을 속성의 관찰자로 등록하고이 속성이 변경되면 이에 대한 알림을받습니다. "원칙"은 NSNotificationCenter작동 방식 과 매우 유사하며 ,이 접근 방식은 iOS 6에서 자동으로 작동한다는 주요 이점입니다 (수동으로 알림을 게시하는 것과 같은 특별한 조정없이).

우리에 대한 UITextField당신의 당신이 예를 들어,이 코드를 추가 할 경우이 잘 작동 -scenario, UIViewController텍스트 필드를 포함 :

static void *myContext = &myContext;

- (void)viewDidLoad {
  [super viewDidLoad];

  //Observing changes to myUITextField.text:
  [myUITextField addObserver:self forKeyPath:@"text"
    options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld 
    context:myContext];

}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
change:(NSDictionary *)change context:(void *)context {

  if(context == myContext) {
    //Here you get notified every time myUITextField's "text" property is updated
    NSLog(@"New value: %@ - Old value: %@",
      [change objectForKey:NSKeyValueChangeNewKey],
      [change objectForKey:NSKeyValueChangeOldKey]);
  }
  else 
    [super observeValueForKeyPath:keyPath ofObject:object 
      change:change context:context];

}

"컨텍스트"관리와 관련된이 답변의 출처 : https://stackoverflow.com/a/12097161/2078512

참고 : 내장 iOS 키보드 로 편집하는 UITextField동안 텍스트 필드의 "text"속성이 입력 / 제거 될 때마다 업데이트되지 않는 것 같습니다. 대신 텍스트 필드의 첫 번째 응답자 상태를 사임 한 후 텍스트 필드 개체가 "전체로"업데이트됩니다.


5
XenElement의 답변에서 target-action 메소드를 사용하십시오. 수십 줄의 코드가 필요한 경우 간단하게 "필요한"작업을 수행하면 잘못했을 수 있습니다.
— jrturton

6
그것은 의도 된 행동으로 보입니다. 코드에서 시작된 이벤트에 대해 다른 대리자 메서드 (예 : 스크롤, 선택)가 호출되지 않습니다.
— jrturton

1
참고 UIKit는 KVO를 준수하도록 보장되지 KVO 솔루션은하지 않습니다 반드시 작업 때문에.
— Pang

@jrturton,“왜”질문과 관련하여 다른 클래스 (어쩌면 장식, 애니메이션 등)에서 텍스트 필드에서 일어나는 일에 응답 해야하는 것은 일반적입니다.
— Fattie

27

StoryboardCTRL 에서 @IBAction다음과 같이 이벤트를 쉽게 변경하고 구성 할 수 있습니다 .

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


14

빠른 버전으로 여기에 동일합니다.

textField.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)

func textFieldDidChange(textField: UITextField) {

}

감사


12

shouldChangeChractersInRange의 동작을 변경하는 문제를 해결했습니다. NO를 반환하면 iOS에서 변경 사항을 내부적으로 적용하지 않고 대신 수동으로 변경하고 변경 후 작업을 수행 할 수 있습니다.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    //Replace the string manually in the textbox
    textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
    //perform any logic here now that you are sure the textbox text has changed
    [self didChangeTextInTextField:textField];
    return NO; //this make iOS not to perform any action
}

3
이 솔루션의 문제점은 다음과 같습니다. 커서 위치가 필드의 끝으로 설정됩니다 (textField.text = ...를 수행하자마자). 따라서 사용자가 필드 중간에있는 문자를 편집하려면 각 키를 누를 때마다 커서가 필드의 끝으로 이동합니다. 사용해보십시오.
— FranticRock

좋은 점 @ 알렉스,하지만 해결하기 간단합니다. 로컬 NSString에서 결과 값을 계산하고 didChangeTextInTextField : toText : 메서드에 전달한 다음 YES를 반환하여 iOS가 업데이트를 수행하도록합니다.
— jk7

11

스위프트 버전 테스트 :

//Somewhere in your UIViewController, like viewDidLoad(){ ... }
self.textField.addTarget(
        self, 
        action: #selector(SearchViewController.textFieldDidChange(_:)),
        forControlEvents: UIControlEvents.EditingChanged
)

설명 된 매개 변수 :

self.textField //-> A UITextField defined somewhere in your UIViewController
self //-> UIViewController
.textFieldDidChange(_:) //-> Can be named anyway you like, as long as it is defined in your UIViewController

그런 다음 위에서 만든 방법을 다음에 추가하십시오 UIViewController.

//Gets called everytime the text changes in the textfield.
func textFieldDidChange(textField: UITextField){

    print("Text changed: " + textField.text!)

}

7

스위프트 4

func addNotificationObservers() {

    NotificationCenter.default.addObserver(self, selector: #selector(textFieldDidChangeAction(_:)), name: .UITextFieldTextDidChange, object: nil)

}

@objc func textFieldDidChangeAction(_ notification: NSNotification) {

    let textField = notification.object as! UITextField
    print(textField.text!)

}

5

스위프트 3.0 :

let textField = UITextField()

textField.addTarget(
    nil,
    action: #selector(MyClass.textChanged(_:)),
    for: UIControlEvents.editingChanged
)

다음과 같은 클래스를 사용하십시오.

class MyClass {
    func textChanged(sender: Any!) {

    }
}

4

스위프트 3 버전

yourTextField.addTarget(self, action: #selector(YourControllerName.textChanges(_:)), for: UIControlEvents.editingChanged)

그리고 여기에서 변화를 얻으십시오

func textChanges(_ textField: UITextField) {
    let text = textField.text! // your desired text here
    // Now do whatever you want.
}

도움이 되길 바랍니다.


2

다른 방법은 실제로 중국어 입력 방법을 사용할 때 실제로 입력되지 않은 입력 상자를 수신하기 때문에이 문제를 해결하려면 알림을 사용해야합니다. viewDidload에서

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFiledEditChanged:)
                                                 name:@"UITextFieldTextDidChangeNotification"
                                               object:youTarget];

그때

- (void)textFiledEditChanged:(NSNotification *)obj {
UITextField *textField = (UITextField *)obj.object;
NSString *toBestring = textField.text;
NSArray *currentar = [UITextInputMode activeInputModes];
UITextInputMode *currentMode = [currentar firstObject];
if ([currentMode.primaryLanguage isEqualToString:@"zh-Hans"]) {
    UITextRange *selectedRange = [textField markedTextRange];
    UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
    if (!position) {
        if (toBestring.length > kMaxLength)
            textField.text =  toBestring;
} 

}

마지막으로, 당신은 실행됩니다.


2

스위프트 3.1 :

선택기 : ClassName.MethodName

  cell.lblItem.addTarget(self, action: #selector(NewListScreen.fieldChanged(textfieldChange:)), for: .editingChanged)

  func fieldChanged(textfieldChange: UITextField){

    }

2

스위프트 3 버전 :

class SomeClass: UIViewController, UITextFieldDelegate { 

   @IBOutlet weak var aTextField: UITextField!


    override func viewDidLoad() {
        super.viewDidLoad()

        aTextField.delegate = self
        aTextField.addTarget(self, action: #selector(SignUpVC.textFieldDidChange), for: UIControlEvents.editingChanged)        
    }

   func textFieldDidChange(_ textField: UITextField) {

       //TEXT FIELD CHANGED..SECRET STUFF

   }

}

델리게이트를 설정하는 것을 잊지 마십시오.


뷰 컨트롤러에 6 개의 텍스트 필드가 있는데 마지막 텍스트 필드가 변경되었는지 확인하고 싶습니다 ( "마지막 텍스트 필드가 변경되었습니다!")?
— Saeed Rahmatolahi

먼저 textField에 Iboutlet을 만드십시오. 그런 다음 답변에 표시된 것처럼 대리자와 addTarget을 설정하십시오. textFieldDidChange 대리자 함수에 다음을 추가하십시오. "if textField == yourLastTextField {print ("마지막 텍스트 필드가 변경되었습니다! ")} 지금은 컴퓨터에 액세스 할 수 없습니다. 내 컴퓨터에가요
— 조셉 프랜시스

2

폐쇄와 함께 :

   class TextFieldWithClosure: UITextField {
    var targetAction: (() -> Void)? {
        didSet {
            self.addTarget(self, action: #selector(self.targetSelector), for: .editingChanged)
        }
    }

    func targetSelector() {
        self.targetAction?()
    }
    }

그리고 사용

textField.targetAction? = {
 // will fire on text changed
 }

2
  1. KVO 솔루션 이 작동하지 않습니다

KVO가 컨트롤 아이폰 OS에서 작동하지 않습니다 http://stackoverflow.com/a/6352525/1402846 https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/KVO.html

  1. 관찰 수업에서 다음을 수행하십시오.

보고자하는 텍스트보기를 알고 있다면 :

var watchedTextView: UITextView!

이 작업을 수행:

NotificationCenter.default.addObserver(
    self,
    selector: #selector(changed),
    name: UITextView.textDidChangeNotification,
    object: watchedTextView)

그러나 다음 사항에주의하십시오.

  • 한 번만 호출하고 싶을 수 있으므로 예를 들어 호출하지 마십시오.layoutSubviews

  • 준비 과정에서 언제 가장 잘 전화해야하는지 알기가 매우 어렵습니다. 상황에 따라 다릅니다. 불행히도 표준 고정 솔루션은 없습니다

  • 예를 들어, 당신은 일반적으로 확실하게 할 수 없습니다 에서 호출 init물론 이후, 시간이 watchedTextView아직 존재하지 않을 수 있습니다

.

  1. 다음 문제는 ...

프로그래밍 방식으로 텍스트를 변경할 때 알림이 호출 되지 않습니다 .

이것은 iOS 엔지니어링에서 오래되고 어리 석고 성가신 것입니다.

.text 속성이 프로그래밍 방식으로 변경 될 때 컨트롤은 단순히 이야기를 끝내지 않습니다 .

물론 모든 앱은 사용자가 게시 한 후 필드를 지우는 것과 같이 프로그래밍 방식으로 텍스트를 설정하므로 이는 매우 성가신 일입니다.

다음과 같이 텍스트 뷰 (또는 유사한 컨트롤)를 서브 클래 싱해야합니다.

class NonIdioticTextView: UIITextView {

    override var text: String! {
        // boilerplate code needed to make watchers work properly:
        get {
            return super.text
        }
        set {
            super.text = newValue
            NotificationCenter.default.post(
                name: UITextView.textDidChangeNotification,
                object: self)
        }

    }

}

(팁 - 슈퍼 전화를 잊지 않는 것은 와야 전에 ! 포스트 전화를.)

이 아니오 , 당신은 단지 위와 같이 서브 클래스에 의한 제어를 해결하지 않는 한, 솔루션을 사용할 수 있습니다. 이것이 유일한 해결책입니다.

알림

UITextView.textDidChangeNotification

결과

func textViewDidChangeSelection(_ textView: UITextView) 

호출되고 있습니다.

( 아닙니다 textViewDidChange .)


1
[[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(didChangeTextViewText:) 
name:UITextFieldTextDidChangeNotification object:nil];



- (void) didChangeTextViewText {
 //do something
}

0

한 가지는 UITextField가 여러 개있을 수 있다는 것입니다. 따라서 태그를 제공하면 태그를 켤 수 있습니다. 모든 클래스에서 옵저버를 설정하는 방법은 다음과 같습니다.

private func setupTextFieldNotification() {
    NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification, object: nil, queue: OperationQueue.main) { (notification) in
        if let textField = notification.object as? UITextField, textField.tag == 100, let text = textField.text {
            print(#line, text)
        }
    }
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

-1

스위프트 4 버전

키-값 관찰 사용 다른 객체의 속성 변경에 대해 객체에 알립니다.

var textFieldObserver: NSKeyValueObservation?

textFieldObserver = yourTextField.observe(\.text, options: [.new, .old]) { [weak self] (object, changeValue) in
  guard let strongSelf = self else { return }
  print(changeValue)
}

좋은 생각. 이것을 문맥에 싸서 알려주십시오.
— Revanth Kausikan 19

1
불행히도 이것은 완전히 잘못된 것입니다. KVO는 iOS 용 컨트롤에서 작동하지 않습니다 : stackoverflow.com/a/6352525/1402846 developer.apple.com/library/archive/documentation/General/…
— Fattie
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.