When I delete characters from a UITextField field one-by-one, the delegate method textField:shouldChangeCharactersInRange:replacementString: is called.
When I type a full line of characters into a textfield, and then hold down the delete key, however iOS at first calls the delegate for each character it deletes. But at some point (about half way through the line) it just deletes everything that remains. The strange thing is that textField:shouldChangeCharactersInRange:replacementString: is not called when this happens. Neither is textFieldShouldClear:.
How can I detect this event? I want to update UI when the textfield is empty. And if I empty it in this fashion, my code fails to detect.
You can register an object to observe UITextFieldDidChangeNotification on your text field.
For example:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldDidChange:) name:UITextFieldTextDidChangeNotification object:self.textField];
}
then
- (void)textFieldDidChange:(NSNotification *)notification
{
UITextField *aTextField = [notification object];
if ([aTextField.text length] == 0) {
aTextField.backgroundColor = [UIColor redColor];
}
}
if you set a breakpoint on
aTextField.backgroundColor = [UIColor redColor];
You will see that it is called right before the last deletion occurs that sets the text field's text property to nil.
You could also simply access your property self.textField, but I am demonstrating how to access the object the notification references. If you leave out the last parameter (object:) in -addObserver:selector:name:object: it will call the notification for all textFields in that object's instance.
Related
I have a routine in my iOS program that imports and manipulates a file from Dropbox. This can take some time (5-10 seconds) and it doesn't make sense to return the user to the normal UI while it's doing it, so I want to present a view letting the user know what the progress is.
From one VC, I use Dropbox's drop-in file picker, then load up a presented (modal VC) thus:
ZSImportVC *importVC = [[ZSImportVC alloc] init];
importVC.results = results;
[importVC setModalPresentationStyle:UIModalPresentationFormSheet];
[self presentViewController:importVC animated:YES completion:^{
[self performFetch];
}];
The VC (a bog-standard UIViewController), has a UILabel property:
#property (weak, nonatomic) IBOutlet UILabel *statusMessage;
In viewWillAppear: I can set the text of this label without any problem. The thing is, I want to keep changing this text as the process of manipulating the file continues.
The method that manipulates the file is called from viewDidAppear:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self processImport];
}
However, within the processImport method, the following has no effect:
self.statusMessage.text = #"Some text to update the user.";
So I created a method:
- (IBAction)updateStatus:(NSString *)message
{
[self.statusMessage setText:message];
NSLog(#"%#", message);
}
just to check what's going on. The NSLog shows that the method is being called okay, but the label text doesn't change. I tried adding:
[self.statusMessage setNeedsDisplay];
to the method, but that didn't help. I'm not using any private queues or background threads. I read somewhere that using NSNotification helps, so I tried adding this to viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateStatus:) name:#"updateStatus" object:nil];
Then changed the called method to:
- (void)updateStatus:(NSNotification *)message
{
[self.statusMessage setText:message.object];
NSLog(#"%#", message.object);
}
and called this from the main method with:
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateStatus" object:#"Retrieving file from Dropbox" userInfo:nil];
I could see from the console messages that the updateStatus method is getting called, but still the text doesn't change. Clearly I'm missing something here. Any thoughts?
Check your ZSImportVC instances on Debug perspective.
I think u are sending [self.statusMessage setText:message.object];
to another instance, that it is not shown on the screen.
I mean,
put a debug point here:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self processImport]; /* Debug point here*/
}
and check what´s the hexadecimal direction for self.
Now , do the same here:
- (void)updateStatus:(NSNotification *)message
{
[self.statusMessage setText:message.object]; /* Debug point here*/
NSLog(#"%#", message.object);
}
Btw, I had some problems with
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateStatus" object:#"Retrieving file from Dropbox" userInfo:nil];
because the instance that receive this notification was not the correct.
I fixed that calling a normal method, but u maybe could not do that.
Sorry for my english :S
I'm making a word game, and ive called my custom keyboards textfield _textbox
Ive put a x button that represents "clear written text" and I only need it to appear when the user types letters into the textfield!
Then disappear after the letters were cleared!
code:
- (IBAction)btnclear:(id)sender {
NSString *oldString = _textbox.text;
NSString *newString;
newString = [oldString substringFromIndex: _textbox.text.length];
[_textbox setText:newString];
}
The image is on the button!
If you're using a UITextField you can use the standard clear button with:
_textbox.clearButtonMode = UITextFieldViewModeWhileEditing;
If you're wanting a custom appearance to the button you can use rightView and rightViewMode to manage the state for you.
Use the following code, it uses UITextFieldTextDidChangeNotification notification,which is called every time you change text in your textfield, and hides or shows your button depending on input text.
- (void) viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textDidChange:) name:UITextFieldTextDidChangeNotification object: _textbox];
}
- (void) textDidChange:(NSNotification *)notification
{
UITextField *tf = (UITextField*)notification.object;
_button.hidden = (tf.text.length == 0);
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object: _textbox];
}
With the property "hidden" of the UIButton you can hide it
Check if there is text on your textView, and then hide your button
Use UITextFielDelegate method
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if(textField.text.length==0){
textXclear.hidden = NO;
}else{
textXclear.hidden = YES;
}
}
There are two ways, and by hidden do you mean not visible or just disabled?
For not visible, use the button.hidden property. For disabled (meaning it can't be touched), use the button.enabled property.
As for the textfield you could do something like this:
if ([textfield.text length] > 0) {...} else {...}
//extra stuff and suggestions
Also if you are using the text in the textfield to be added to some other view (say its an add item screen), you have to create a #property regarding the added item. And then you could, rather than the aforementioned mention write the code like in the .m:
if (self.aProperty != nil) {
button.hidden = NO;
} else {
button.hidden = YES;
And you'd have to declare the property in the .h file:
#property (nonatomic, strong) ObjectYouAreUsing *aProperty;
And this may be the reason it's not working but create a new file with the NSObject subclass. This will be the ObjectYouAreUsing.
This way you can access the pure object you are using and just import it where ever you need it. Also with this, if the user were to close the screen you could then write the initWithCoder method.
I was wondering if there is any way I can make the right view of an UITextField only be visible when there is at least one character inside, because by setting UITextFieldViewModeWhileEditing will show it once I focus on the field, not when I start typing.
I could only come up with implementing the UITextFieldDelegate and doing it on one of the methods that is triggered when the user is typing. The only issue here is that I change the delegate of the text field to something else once I create a text field and add it to the view. That is because I made a custom textfield by subclassing UITextField and I init it in various places, and in those various places I assign it's delegate to the current place it's initiated.
Here's a better idea. UITextField posts a change notification. Your subclass can observe itself this way:
// in RSSUITextField.m
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textFieldChanged:)
name:UITextFieldTextDidChangeNotification
object:self];
Then implement textFieldChanged: and change your rightView state in there. This answer is superior to the other I left, but I won't remove that one since that works too, and is a useful technique for cases where the control doesn't post notifications about a state change we care about.
Since each instance of the RSSUITextField will observe itself with the NSNotificationCenter, each is responsible to remove itself as an observer when it no longer matters. The latest possible time to do this is on dealloc...
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
This answer works, but there's a better answer for your particular case. See my other answer... text field posts NSNotification when the text changes....
KVO would be ideal, but the UIKit doesn't promise KVO compliance. The more abstract problem statement is that you want one object to know something about the text field's state that can only be known by the delegate, but you sometimes want some other object to be the delegate.
The only idea I have that doesn't break any rules is to have the subclass be a proxy for the real delegate, like so...
// in RSSUITextField.m
#interface RSSUITextField () <UITextFieldDelegate>
// I will be my own delegate, but I need to respect the real one
#property (weak, nonatomic) id<UITextFieldDelegate>proxyDelegate;
#end
In the designated init...
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.delegate = self;
}
return self;
}
I am my own delegate, so when the caller of this class wants to get/set the delegate, I need to fool him...
- (id<UITextFieldDelegate>)delegate {
return self.proxyDelegate;
}
- (void)setDelegate:(id<UITextFieldDelegate>)delegate {
self.proxyDelegate = delegate;
}
Now the important part (and then sad part). Important part: we're now in the position to know about our state as a delegate and still respect the caller's idea of the delegate...
- (void)textFieldDidBeginEditing:(UITextField *)textField {
// show or change my right view
if ([self.proxyDelegate respondsToSelector:#selector(textFieldDidBeginEditing:)]) {
[self.proxyDelegate textFieldDidBeginEditing:textField];
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
// hide or change my right view
if ([self.proxyDelegate respondsToSelector:#selector(textFieldDidBeginEditing:)]) {
[self.proxyDelegate textFieldDidBeginEditing:textField];
}
}
The sad part: We broke it, so we bought it. In order to fully respect the delegate, we have to pass on all delegate messages. This isn't a disaster, because there are only five more of them. They will all take exactly this form:
// I only added this one as an example, but do this with all five others
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if ([self.proxyDelegate respondsToSelector:#selector(textFieldShouldReturn:)]) {
return [self.proxyDelegate textFieldShouldReturn:textField];
} else {
return YES; // the default from the docs
}
}
Did anyone notice that UITextField calls textFieldDidEndEditing after clear button is pressed but text property still has old data ?
I'm not sure what code-sample I can provide here. I'm using storyboard if that matters.
For now I have to rely on taking data from all edit controls on main form's "Submit" button. But ideally I'd prefer to collect data in textFieldDidEndEditing handler.
Are there any better workarounds ?
I'm on iOS 6.
Update: Basically here is what I have on the form
UITextField and UiButton are on the form.
Keyboard dimissed by calling resignFirstResponder in handler of UITapGestureRecognizer
Steps to reproduce the issue:
Click on edit control. Enter some text.
Tap outside of text control.
textFieldDidEndEditing is called. Property .text has value I entered. All good.
Click on edit control again.
Click on clear button.
textFieldDidEndEditing is called again. But property .text still has value I just deleted !
Now as you see cursor blinking inside UITextField tap on Button on the form.
Keyboard is dismissed by textFieldDidEndEditing was never called.
I'll upload sample project on GitHub tomorrow.
I ran into the exact same problem. In my case, at least, it was due to having added a UITapGestureRecognizer to self.view (to allow for dismissing the keyboard if tapping outside of a UITextField) and setting cancelsTouchesInView=NO on the gesture recognizer. I had set that property in order to get hyperlinking working on a TTTAttributesLabel I have elsewhere in the View.
My workaround was to watch for keyboard show and hide notifications, and toggle that property accordingly:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShowNotification:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidHideNotification:) name:UIKeyboardDidHideNotification object:nil];
(sign up for notifications)
- (void)keyboardDidShowNotification:(NSNotification*)notification
{
tapGestureRecognizer.cancelsTouchesInView = YES;
}
- (void)keyboardDidHideNotification:(NSNotification *)notification
{
tapGestureRecognizer.cancelsTouchesInView = NO;
}
(handle notifications)
The only problem, behavior-wise, is that the hyperlink still doesn't work when the keyboard is displayed: touching it will simply dismiss the keyboard, not forward the touch to the link handler. But I can live with that. After the keyboard is dismissed, the link works fine.
First check UITextFieldDelegate is assigned or not, then
implement the textFieldShouldClear delegate and write the code here clear your textField
To do this you have to set the clearButtonMode property,
yourTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
yourTextField.delegate = self;
Then implement the textFieldShouldClear delegate
.h file
#interface myViewController: UIViewController <UITextFieldDelegate>{
}
.m file
-(BOOL)textFieldShouldClear:(UITextField *)textField
yourTextFeild.text = #"";
return YES;
}
try here:
-(BOOL) textFieldShouldReturn:(UITextField*) textField
I have a row of labels that have been programmatically instantiated, they are stored in an NSMutableArray. They don't currently contain any data. What I'm trying to do is make it so that when a user types in a character it is automatically displayed in the labels. I'm not sure how to do this. I know how to access the labels I have created [MyArray ObjectAtIndex:0] and so on, but how could I make it so that when a user types on the keyboard it formats the text (I have code for formatting) and then just appears on screen.
I need help putting each character on the screen as it is typed.
Any help would be greatly appreciated - I have a textfield (it's hidden) and the keyboard comes up by button. If that helps. :)
Thank you in advance :).
UITextField *tf;
[tf addTarget:self action:#selector(editingChanged:) forControlEvents:UIControlEventEditingChanged];
- (void)editingChanged:(UITextField *)textField {
_myHiddenLabel.text = textField.text;
}
You can add observer when Text inside UITextField changes and then access your labels and add text to it...
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(changeLabelsMethod:)
name:UITextFieldTextDidChangeNotification
object:myHiddenTextField];
}
-(void)changeLabelsMethod:(UITextField*)txtField
{
Static int i=0;
if(i<[MyArray count])
{
UILabel *lbl=[MyArray ObjectAtIndex:i];
lbl.text=txtField.text;
}
else
return
i++;
}
EDIT: Refer Eugene's answer for right approach
You can get notified every time a character is typed if you set your viewController as delegate to your hidden textField and implementing UITextFieldDelegate Protocol
- (IBAction)textFieldValueChanged {
NSString *strLastChar = [txtSearch.text substringFromIndex:txtSearch.text.length-1];
UILabel *lblCurrent = [arrSearch objectAtIndex:intCurrentLblNo];
[lblCurrent setText:strLastChar];
intCurrentLblNo++;
}
take intCurrentLblNo as global variable and set intCurrentLblNo = 0; in viewdidload method
and set it...
[txtSearch addTarget:self action:#selector(textFieldValueChanged) forControlEvents:UIControlEventValueChanged];
in viewdidload method