TextField: shouldChangeCharactersInRange: replacementString: in a subclass

I have subclassed UITextField, and I would like to use a method like textField:shouldChangeCharactersInRange:replacementString: inside the subclass to be notified when a character is entered and, if necessary, block this change, but do not delete the field delegation to yourself.

I found that if I redefine keyboardInput:shouldInsertText:isMarkedText: and keyboardInputShouldDelete: I can get the desired effect, unfortunately, these methods are private, and everything that uses this class will not go through the presentation process in the App Store.

Does anyone know an open method that achieves the same thing and does not require the field to be its own delegate?

UPDATE:

I went with the proposal to create a separate object only in order to be a delegate, who himself can have a delegate for sending messages.

+6
source share
4 answers

Having tried the UITextField subclass before, I have since learned to avoid this and go along the delegate route (it has a similar method called - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string , which should do what I think you described).

+2
source

One strategy to learn here is to override the setDelegate method: and then do some message forwarding. You can use [super setDelegate: self] to make sure your calls receive the first dibs in delegate messages. When overriding setDelegate: set the internal ivar, e.g.

  - (void) setDelegate: (id<UITextFieldDelegate>) internalDelegate; { [setInternalDelegate: internalDelegate]; } 

Then, for each of the UITextField delegate methods, do your thing before forwarding according to the delegate message, for example

 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; { // do your thing with range and replacement string // now forward message on the 'other' delegate [[self internalDelegate] textField: self shouldChangeCharactersInRange: range replacementString: string]; } 

Typically, you will want to override all delegate methods, even if for most of them all you do is straightforward.

Update . You note in the comments that the forwarding approach raises issues. If so, then traditional delegation is the way to go. (And, in general, this is the way to go - although I used the forwarding delegate once or twice, I'm not sure if it was absolutely important with retroactivity, and I did not check if I had it done with UITextField. @ Scott Korskadden neither recommends nor recommends.)

The most common pattern is to force the ViewController to keep track of the view in which the UITextField is a sub delegate. You do not say in your answer if there is any reason why you need to work with a subclass. If you are packing interesting things in a UITextField , then it can be, although you can always suggest a different poster and create a companion class for the UITextField that will work and use this as a delegate. In any case, if necessary, you can always force the delegate object to call additional methods in your UITextField subclass, for example.

 // in the delegate object class - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; { [delegate doSomeExtraThingInTheTextFieldSubclassThatItSeemsToMakeSenseToDoThereRatherThanHere]; // maybe that it, or maybe this object also wants to do something here... } 
+2
source

To achieve this, you can play with Objective-C runtime. The following code works for me. The idea is this:

  • in the subclass, we set the delegate property to ourselves:
  • we override the setDelegate: method in which we store the provided delegate variable ( forwardDelegate );
  • overriding respondsToSelector: forwardInvocation: and methodSignatureForSelector: we either call the method ourselves, or forward it to the stored forwardDelegate .

 // MyTextField.h @interface MyTextField : UITextField @end // MyTextField.m @interface MyTextField () <UITextFieldDelegate> @end @implementation MyTextField { id forwardDelegate; } - (void) myTextFieldCommonInit { [super setDelegate:self]; } - (id) initWithCoder:(NSCoder *)coder { if ((self = [super initWithCoder:coder])) { [self myTextFieldCommonInit]; } return self; } - (id) initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { [self myTextFieldCommonInit]; } return self; } - (void) setDelegate:(id)delegate { forwardDelegate = delegate; } - (BOOL) respondsToSelector:(SEL)selector { if ([super respondsToSelector:selector]) { return YES; } else { return [forwardDelegate respondsToSelector:selector]; } } - (void) forwardInvocation:(NSInvocation *)invocation { if ([super respondsToSelector:[invocation selector]]) { [super forwardInvocation:invocation]; } else if ([forwardDelegate respondsToSelector:[invocation selector]]) { [invocation invokeWithTarget:forwardDelegate]; } else { [self doesNotRecognizeSelector:[invocation selector]]; } } - (NSMethodSignature *) methodSignatureForSelector:(SEL)selector { NSMethodSignature *signature = [super methodSignatureForSelector:selector]; if (signature) { return signature; } else { return [forwardDelegate methodSignatureForSelector:selector]; } } #pragma mark - UITextFieldDelegate - (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { ... } @end 
+1
source

You can use the delegate method of the uitext field,

 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { } 

And don't forget to set textfield.delegate = self in ViewDidLoad.

0
source

Source: https://habr.com/ru/post/911610/


All Articles