Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

textField:shouldChangeCharactersInRange:replacementString: in subclass

I've subclassed a UITextField and I'd like to use a method similar to textField:shouldChangeCharactersInRange:replacementString: inside the subclass to get notified when a character is typed and if necessary block the change, but avoid setting the fields delegate to itself.

I've found if I override keyboardInput:shouldInsertText:isMarkedText: and keyboardInputShouldDelete: I can get the desired effect, unfortunately these methods are private and anything using the class won't make it through the App Store submission process.

Anyone know of a public method that achieves the same thing and doesn't require the field being its own delegate?

UPDATE:

I went with the suggestion of creating a separate object just to be the delegate, which itself can have a delegate to forward messages to.

like image 766
Tom Irving Avatar asked Mar 25 '12 17:03

Tom Irving


2 Answers

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

like image 105
Scott Corscadden Avatar answered Oct 23 '22 04:10

Scott Corscadden


One strategy to explore here is to override the setDelegate: method and then do some message forwarding. You can use [super setDelegate: self] to make sure your calls gets first dibs at the delegate messages. In your override of setDelegate: set an internal ivar, e.g.

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

Then for each of the the UITextField delegate methods do your thing before forwarding on the delegate message, e.g.

- (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'll want to override all of the delegate methods, even if for most of them all you do is a straight forward.

Update You note in comments that the forwarding approach is raising issues. If so, then traditional delegation is the way to go. (And, in generally, it is the way to go - although I've used forwarding delegate once or twice, I'm not sure if, with hindsight, it was absolutely essential and I've not checked to see if I've done it with UITextField. @Scott Corscadden has and does not recommend it.)

The most common pattern is to make the ViewController that looks after the view in which the UITextField is a subview the delegate. You don't say in your answer if there's particular reason why you need to work with the subclass. If you're packing interesting stuff into the UITextField then it might be, though you could always another poster suggests and create a companion class for UITextField that does that work and use that as the delegate. In any case, if need be, you can always get the delegate object to call extra methods on your UITextField subclass, e.g.

// in the delegate object class

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
{
      [delegate doSomeExtraThingInTheTextFieldSubclassThatItSeemsToMakeSenseToDoThereRatherThanHere];

      // maybe that's it, or maybe this object also wants to do something here...
}
like image 43
Obliquely Avatar answered Oct 23 '22 05:10

Obliquely