How to incorporate textfield change notification into textFieldDidEndEditing - ios

I have a UITableView with custom UITableViewCells and each tableview cell has a UITextField. By default the textfield has a title already in place that can be edited by the user. The default title in the textfield is associated with a file in NSFileManager and when the user finishes editing the text field and taps "return", a method that changes the file name to what the user enters gets called. This works fine, but when the user taps the textfield but doesn't do any editing and then taps "back" to go to the previous view controller, I get a warning from NSFileManager saying the file name already exists. This doesn't cause any problems, but its annoying. I know the method that calls NSFileManager to change the file name shouldn't get called unless the user edits the textfield, but I'm not sure of the best way to implement this.
I saw this post, but wasn't sure how to incorporate it into what I'm doing:
UITextField text change event
I was wondering if someone could give me some tips on how to make this work.
-(void) textFieldDidEndEditing:(UITextField *)textField
{
textField.delegate = self;
NSArray* cells = [self.audioTable visibleCells];
for (OSAudioTableCell* cell in cells)
{
if (textField == cell.textField)
{
NSInteger index = cell.tag;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
Recording * recording = [self.fetchCon objectAtIndexPath:indexPath];
NSString * previousPath = recording.audioURL;
//I left a lot out, but this is where I call the method to change the file name
NSString * returnedURL = [self.managedDocument changeFileName:previousPath withNewComponent:textField.text error:&aError];
}
}
}

I would just check if the textField's text changed. If it did then go through the block you pasted above. If not, then just do nothing. You can do this by holding a temporary reference to your textfield's value before any edits happen:
// At the top of your class
#property (strong, nonatomic) NSString *currentFileName;
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
_currentFileName = textField.text;
}
Then in your method above, I would check if the two strings are not equal:
-(void) textFieldDidEndEditing:(UITextField *)textField
{
if (![textField.text isEqualToString:currentFileName]) {
// continue with your logic
}
}

Try this. Add the delegate method -(void)textFieldDidBeginEditing:(UITextField *)textField. And in this method do something like:
-(void)textFieldDidBeginEditing:(UITextField *)textField {
self.textBeforeEditing = textField.text;
}
And then, do a compare when textFieldDidEndEditing is invoked:
-(void) textFieldDidEndEditing:(UITextField *)textField {
...
if(![self.textBeforeEditing isEqualToString:textField.text]) {
// Change the file name
}
...
}

You could implement textFieldDidBeginEditing:, in which you would store the unedited value of the UITextField in an instance variable. Then, in textFieldDidEndEditing: simple compare the before and after values, and if they're different call your NSFileManager method like you normally would.
Example
#interface MyClass () {
#property (strong, nonatomic) NSString *originalText;
}
#implementation MyClass
- (void)textFieldDidBeginEditing:(UITextField *)textField {
self.originalText = textField.text;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
if ([self.originalText isEqualToString:textField.text]) {
// Your original code here.
}
self.originalText = nil;
}
#end

Related

Clear the content of the UITextField's text property for tap

I have a UITextField called place, it's text property contains an NSString. I would like to clear the existing content from the text property when the user taps into the text field.
I tried the code above, but nothing happened. I also tried it with the placeholder property, but it was the same. Do you have any idea what could be the problem? I think it should work.
- (void)textFieldDidBeginEditing:(UITextField *)textField {
self.place.text = nil;
}
2, version - in this case nothing appears in the text field from the beginning
- (void)viewDidLoad
{
[super viewDidLoad];
...
[self textFieldDidBeginEditing:place];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
textField.text = nil;
}
There is a property clearsOnBeginEditing you can set to YES programmatically or there is a checkbox in Interface Builder.
If you also want to clear your placeholder
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
textField.placeholder = nil;
}
Double check that the UITextField self.place has it's delegate set to your view controller and validate that textFieldDidBeginEditing is being called.

How do I identify which textField triggered textFieldDidEndEditing on dynamically created UITableCell containing 2 uiTextFields?

I am on iOS 6 xcode 4.6.2 using storyboards.
I am using a dynamic UITableView which consists of a number of cells each of which have two UITextFields on them. The two fields are defined in a custom cell as
#property (strong, nonatomic) IBOutlet UITextField *lowRangeField;
#property (strong, nonatomic) IBOutlet UITextField *highRangeField;
I wish to use
-(void) textFieldDidEndEditing:(UITextField*) textfield
to get at the values and save it into a core data store.
Now, obviously, I can get at the value and assign it where I like, because I have the pointer to the textfield. My issue is I don't know how to identify which field on the cell this actually is. I know I can get the textfields superview to identify which cell its on , so I can work out which set of lowRangeField and highRangeField it is but then I get stuck.
My issue is I don't know how to identify which field on the cell this actually is.
Use Tag to Identify.
lowRangeField.tag = 1;
highRangeField.tag = 2;
-(void) textFieldDidEndEditing:(UITextField*) textfield
{
if (textField.tag == 1) {
NSLog(#" clicked in lowRangeField");
} else if (textField.tag ==2) {
NSLog(#" clicked in highRangeField");
}
}
Try this one
This is used to identify in which text field you have entered value .
- (void)viewDidLoad
{
lowRangeField.tag = 100;
highRangeField.tag = 200;
}
-(void) textFieldDidEndEditing:(UITextField*) textfield
{
if (textField.tag == 100)
{
NSLog(#" clicked On lowRangeField");
}
else if (textField.tag ==200)
{
NSLog(#" clicked On highRangeField");
}
}
If you have two (or more) field referred in the ViewController (as property) you can distinguish them following this way:
- (void) textFieldDidEndEditing:(UITextField *)textField {
if (textField==self.lowRangeField)
//do something
if (textField==self.highRangeField)
// do something else
}

clearing a UITextField when the user starts typing

short version: How can I make a UITextField box remove all content on the users first keypress? I don't want the info removed until the user starts typing something. ie, clearing it on begin edit is not good enough.
long version: I have three UITextField that loop around (using the return key and catching the press in the "shouldReturn" method. There is text already in the UITextField, and if the user doesn't type anything and just goes to the next UITextField, the value should stay (default behaviour).
But I want it that if the user starts typing, it automatically clears the text first. Something like having the whole field highlighted, and then typing anything deletes the fiels and then adds the user keypress.
"Clear when editing begins" is no good, because the text is immediately cleared on the cursor appearing in the field. That's not desired. I thought I could use the placeholder here, but that doesn't work as a default, and I can't find a default value property. The Highlighted and Selected properties don't do anything in this regard either.
There is a delegate method called
textFieldDidBeginEditing:(UITextField*) tf{
tf.startedEdinting = YES;
}
textFeildDidEndEditing: (UITextField*) tf {
tf.startedEditing = NO;
}
Add startEditing in a category to UITextField.
Then if value changes clear the field:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField.startEditing){
textField.text = string;
} else {
textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
}
}
You can add the property to the UITextField category in the following way:
.h
#property (nonatomic, assign) BOOL startEditing;
.m
#dynamic startEditing;
- (void) setStartEditing:(BOOL)startEditing_in{
NSNumber* num = [NSNumber numberWithBool:startEditing_in];
objc_setAssociatedObject(self, myConstant, num, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL) startEditing{
NSNumber* num = objc_getAssociatedObject(self, myConstant);
return [num boolValue];
}
Declare a BOOL variable in your .h file like.
BOOL clearField;
And implement the delegate methods like:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
clearField = YES;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
clearField = NO;
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
clearField = NO;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(clearField)
{
textField.text = #""
clearField = NO;
}
}
I want to thank people for their answers, I implemented both of the main methods described here and both worked flawlessly. But I have since come across a much simpler, nicer answer and involves only one line of code :)
In the textField's didBeginEditing method, place [self.textField selectAll:self]; or [self.textField selectAll:nil];
The original answer I found had selectAll:self but this shows the cut/copy/paste menu. If you send nil instead of self the menu doesn't appear.
Adding this one line of code highlights the text on entering the textField (so gives the user a visual cue), and only removes everything once a key is pressed.
Another solution that fulfils the same purpose is by simply using a text field placeholder which is defined as:
The string that is displayed when there is no other text in the text field.
So as soon as the user starts typing, the placeholder text disappears.
That's something you can set from the storyboard, or programmatically. (Yes it took me two hours trying to figure it the harder way.. when the solution was literally one line change of code).
If you want to clear the text one the user interacts with it, there is an option in interface builder to where you can set the text field to "Clear when editing begins."
Try to use the following method.
- (BOOL) textField: (UITextField *)theTextField shouldChangeCharactersInRange: (NSRange)range replacementString: (NSString *)string {
if(isFirsttime==YES)
{
textfield.text==#"";
isFirsttime=NO;
}
return YES;
}
Declare and initialize a NSString variable for your textField's initial text
NSString *initialText=#"initial text";
Then implement methods:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
if(textField.text isEqualToString:initialText)
{
textField.text=#"";
}
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
if(textField.text isEqualToString:#"")
{
textField.text=initialText;
}
}

UITextField clear when selected, but also restore data if nothing is input

I have a table with UITextFields, and I want to make it so that when a textfield is selected, the current info is cleared (to allow new input), but if the user decides (after selecting) that they don't want to change it, I want them to be able to click elsewhere and the data from before reappears in the textfield.
Is there an easy way to do this?
A good way to do this that's nice and user friendly is to use the placeholder property. Set up your viewController as the textfield's delegate (as described by Andeh) then add the following code:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
textField.placeholder = textField.text;
textField.text = #"";
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
if (textField.text.length == 0) {
textField.text = textField.placeholder;
}
textField.placeholder = #"";
}
And you're done. If you have multiple UITextFields on the page and you don't want this behaviour for all of them you can add a check to the methods above.
In the textFieldDidBeginEditing delegate method, save the current value to a persisting variable before clearing the UITextField. Then in the didFinishEditing delegate method, if the new length of the user input is 0 set the text back to the stored value!
UITextField Delegate docs for reference.
First I think you have two sets of behaviors here.
The text field must clear the value when you begin editing. This exists: -clearsOnBeginEditing.
The text field must restore the previous text if text is empty. Subclassing seems the better solution.
Here is a possible sample class:
// MyRestoringTextField.h
#interface MyRestoringTextField : UITextField
#end
// MyTextField.m
#interface MyRestoringTextField ()
#property (strong, nonatomic) NSString *previousText;
#end
#implementation MyRestoringTextField
- (BOOL)becomeFirstResponder
{
BOOL result = [super becomeFirstResponder];
self.previousText = self.text;
return result;
}
- (BOOL)resignFirstResponder
{
BOOL result = [super resignFirstResponder];
if (self.text.length == 0)
self.text = self.previousText;
return result;
}
#end
Hope that helps.
To clear and then restore a textField if you fail to make an entry, use the following delegates as such:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[[NSUserDefaults standardUserDefaults] setObject:textField.text forKey:kTextFieldIdentifier];
textField.text = #"";
}
-(BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
if ([textField.text isEqualToString:#""])
textField.text = [[NSUserDefaults standardUserDefaults]
stringForKey:kTextFieldIdentifier];
return YES;
}
As of iOS 8.1, textFieldDidBeginEditing is already receiving a cleared text. You should use
-(BOOL) textFieldShouldBeginEditing:(UITextField *)textField
to initialized the placeholder field.

how to get selected text from uitextfield in iphone?

I am working on Text to speech application in iPhone,
in which have a text field that takes input, i want user to select some portion of text from text field and my application will convert that selected text into speech.
my problem is how would i get the text that user has selected from text field?
-[UITextField selectedText]
Although UITextField doesn't have a selectedText method, it conforms to the UITextInput protocol. So, you can use the required properties & methods of the UITextInput protocol to determine the selectedText of a UITextField *textField (or any object that conforms to the UITextInput protocol, such as a UITextView).
NSString *selectedText = [textField textInRange:textField.selectedTextRange];
NSLog(#"selectedText: %#", selectedText);
As an aside, you can also use the required properties & methods of the UITextInput to calculate the selectedRange of a UITextField *textField.
UITextRange *selectedTextRange = textField.selectedTextRange;
NSUInteger location = [textField offsetFromPosition:textField.beginningOfDocument
toPosition:selectedTextRange.start];
NSUInteger length = [textField offsetFromPosition:selectedTextRange.start
toPosition:selectedTextRange.end];
NSRange selectedRange = NSMakeRange(location, length);
NSLog(#"selectedRange: %#", NSStringFromRange(selectedRange));
-[UITextFieldDelegate textFieldDidChangeSelection:]
Although, UITextFieldDelegate doesn't declare a textFieldDidChangeSelection: delegate method like -[UITextViewDelegate textViewDidChangeSelection:], you can still hook into when the selection of a UITextField has changed. To do so, subclass UITextField and use method swizzling to add your own code to the textField.inputDelegate's native implementation of -[UITextInputDelegate selectionDidChange:].
// MyTextField.h
#import <UIKit/UIKit.h>
#interface MyTextField : UITextField
#end
// MyTextField.m
#import <objc/runtime.h>
#import "MyTextField.h"
UIKIT_STATIC_INLINE void mySelectionDidChange(id self, SEL _cmd, id<UITextInput> textInput);
#implementation MyTextField {
BOOL swizzled;
}
#pragma mark - UIResponder
// Swizzle here because self.inputDelegate is set after becomeFirstResponder gets called.
- (BOOL)becomeFirstResponder {
if ([super becomeFirstResponder]) {
[self swizzleSelectionDidChange:YES];
return YES;
} else {
return NO;
}
}
// Unswizzle here because self.inputDelegate may become the inputDelegate for another UITextField.
- (BOOL)resignFirstResponder {
if ([super resignFirstResponder]) {
[self swizzleSelectionDidChange:NO];
return YES;
} else {
return NO;
}
}
#pragma mark - Swizzle -[UITextInput selectionDidChange:]
// Swizzle selectionDidChange: to "do whatever you want" when the text field's selection has changed.
// Only call this method on the main (UI) thread because it may not be thread safe.
- (void)swizzleSelectionDidChange:(BOOL)swizzle {
if (swizzle == swizzled || ![self respondsToSelector:#selector(inputDelegate)]) return; // 4.3
Class inputDelegateClass = object_getClass(self.inputDelegate);
SEL mySelector = #selector(mySelectionDidChange:);
class_addMethod(inputDelegateClass, mySelector, (IMP)mySelectionDidChange, "v#:#");
Method myMethod = class_getInstanceMethod(inputDelegateClass, mySelector);
Method uiKitMethod = class_getInstanceMethod(inputDelegateClass, #selector(selectionDidChange:));
method_exchangeImplementations(uiKitMethod, myMethod);
swizzled = swizzle;
// NSLog(#"swizzled? %i", method_getImplementation(uiKitMethod) == (IMP)venmo_selectionDidChange);
}
#end
UIKIT_STATIC_INLINE void mySelectionDidChange(id self, SEL _cmd, id<UITextInput> textInput) {
// Call the native implementation of selectionDidChange:.
[self performSelector:#selector(mySelectionDidChange:) withObject:textInput];
// "Do whatever you want" with the selectedText below.
NSString *selectedText = [textInput textInRange:textInput.selectedTextRange];
NSLog(#"selectedText: %#", selectedText);
}
I did solve my query as follow :
I implement UITextView's delegate and implement following method
- (void)textViewDidChangeSelection:(UITextView *)textView {
NSRange r = textView.selectedRange;
NSLog(#"Start from : %d",r.location); //starting selection in text selection
NSLog(#"To : %d",r.length); // end position in text selection
NSLog([tv.text substringWithRange:NSMakeRange(r.location, r.length)]); //tv is my text view
}
That's it!
Swift
In Swift, getting the selected text from a UITextField is done like this:
if let textRange = myTextField.selectedTextRange {
let selectedText = myTextField.textInRange(textRange)
}
where textRange is a UITextRange that is used to get the actual selected text.
A similar topic is discussed here: Can I select a specific block of text in a UITextField?
AFAIK there is no event if text is selected. However, you could setup an NSTimer to watch your textfield and check the _selectedRange. If it changes, go fire up your text-to-speech code.
EDIT: I was wrong about the selection. UITextField cannot do what you want to achieve. But if you use UITextView instead, you can implement its UITextViewDelegate and override
- (void)textViewDidChangeSelection:(UITextView *)textView
In there, you can use the selectedRange poperty to get the selection. See this reference for details:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextView_Class/Reference/UITextView.html#//apple_ref/doc/uid/TP40006898-CH3-SW13
UITextField don't have delegate to get the selection range change. We can use KVO to observe selectedTextRange property of UITextfield.
[textField addObserver:self forKeyPath:#"selectedTextRange" options:NSKeyValueObservingOptionNew context:NULL];
Or create UITextField subclass and override setSelectedTextRange method.

Resources