Saving data entered in textfield of custom cells - objective-c

I have a custom cell.In that custom cell,there is a textfield and a label.I actually have made a form using custom cell.The user will enter its name,city,state,country,dob detail in that textfield.Now ,on a click of a button I want to save all this data together in a dictionary.But I am not able to understand that how can I save data for different keys using the same textfield as it is being reused.Please help with some code in objective c.Thanks in advance!

I am giving you idea, so that you can implement it
Just take a Macro, like this #define textFieldTag 1000
You are re-using the textfField right. So, in cellForRowAtIndexPath (if u r using table view) set the tag of the textField like this: cell.textType.tag = indexPath.row + textFieldTag;
then
access the textField using delegate
- (void)textFieldDidEndEditing:(UITextField *)textField {
switch (textField.tag - textFieldTag) {
case 0:{
NSString *name = textField.text;
//Take a NSMutableDictionary and add the values over here
break;
}
case 1:
{
NSString *city = textField.text;
break;
}
case 2:
{
NSString *state = textField.text;
break;
}
case 3:
{
NSString *country = textField.text;
break;
}
case 4:
{
NSString *dob = textField.text;
break;
}
default:
break;
}
}
And one thing, when you click the save or what ever button u r using to submit the form remember to dismiss the keyboard [textField resignFirstResponder];, otherwise the last value can be nil.
Thank you

Related

Enable button only after text fields are entered

Apple started rejecting my app (after yrs of approvals) - I have 2 text fields for a few char of last, first name, then press Go. They claim - I can't reproduce that the app crashes if you just press Go - when I run it it tries to send the request but kicks it back with an error 'No name - please reenter'. Whatever. To appease Apple I edited my text field entry method to now (see below) - but still not working. Please advise.
// Text Field methods
-(void)textFieldDidBeginEditing:(UITextField *)textField {
UIFont* boldFont = [UIFont fontWithName:#"Helvetica-Bold" size: 24];
if (textField == _lastName) {
[_lastName becomeFirstResponder];
[_lastName setFont:boldFont];
_lastName.autocorrectionType = UITextAutocorrectionTypeNo;;
_lastName.autocapitalizationType = UITextAutocapitalizationTypeNone;
_lastName.text = #"";
NSLog(#"user editing last...");
// Enable button once fields have values
if ([_lastName.text isEqual: #""] && [_firstName.text isEqual: #""])
{
_fwdButton.enabled = YES;
}
}
else {
[_firstName becomeFirstResponder];
[_firstName setFont:boldFont];
_firstName.autocorrectionType = UITextAutocorrectionTypeNo;;
_firstName.autocapitalizationType = UITextAutocapitalizationTypeNone;
_firstName.text = #"";
NSLog(#"user editing first");
// Enable button once fields have values
if ([_lastName.text isEqual: #""] && [_firstName.text isEqual: #""])
{
_fwdButton.enabled = YES;
}
}
}
I was able to add code similar to[https://stackoverflow.com/questions/2859821/disable-button-until-text-fields-have-been-entered][1]
to deal with the situation.

disabling rows on condition on xlform

I have a section set up using xlform where there is a bool field and then a text field under it. I want to disable the text field if the bool is chosen. I tried with the following, but it did not work
if ([[self.form valueForKey:#"pay"] isEqualToValue:#(1)]){
row.disabled = YES;
} else {
row.required = YES;
}
[section addFormRow:row];
Any suggestions? Here is the documentation, spent a bunch of time search without being able to find the answer.
Edit: I'm starting to think that the values of the dictionary don't get updated dynamically. Which is odd because the dictionary can be accessed in other parts of the view controller at any time.
Edit
- (void)formRowDescriptorValueHasChangedTwo:(XLFormRowDescriptor *)formRow value:(id)value
{
[value isEqual:[self.formValues valueForKey:#"pay"]];
if([formRow.tag isEqualToString:#"pay"] && [value isEqual:[NSNumber numberWithBool:YES]])
{
self.price.disabled = NO;
self.price.required = YES;
[self.tableView reloadRowsAtIndexPaths:#[[self.form indexPathOfFormRow:self.price]]
withRowAnimation:UITableViewRowAnimationNone];
}
}
I got it working, but I have a new problem.
Here is the code
- (void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)formRow oldValue:(id)oldValue newValue:(id)newValue
{
if([formRow.tag isEqualToString:#"now"] && [newValue isEqual:[NSNumber numberWithBool:YES]])
{
self.time.disabled = YES;
[self.tableView reloadRowsAtIndexPaths:#[[self.form indexPathOfFormRow:self.time]]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
if([formRow.tag isEqualToString:#"pay"] && [newValue isEqual:[NSNumber numberWithBool:YES]])
{
self.price.disabled = NO;
self.price.required = YES;
[self.tableView reloadRowsAtIndexPaths:#[[self.form indexPathOfFormRow:self.price]]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
When I click on the first row (now), it disables the corresponding row fine, but when I click on the second row (pay), it makes the row that now disables reappear AND it makes the row that I want to appear under pay to disappear.
Edit Got it to work by changing the animation to UITableViewRowAnimationNone
Here's how you can do it using XLFormDescriptorDelegate. This will change the state as the field is toggled on the form.
#interface SomeClass() <XLFormDescriptorDelegate>
#end
#implementation SomeClass
- (void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)formRow oldValue:(id)oldValue newValue:(id)newValue
{
if([formRow.tag isEqualToString:#"boolFieldTag"] && [newValue boolValue])
{
self.otherRow.disabled = YES;
[self.tableView reloadRowsAtIndexPaths:#[[self.form indexPathOfFormRow:self.otherRow]]
withRowAnimation:UITableViewRowAnimationNone];
}
}
#end
In this example I set the row as a property when I originally added it to the form. Alternatively you can just use: [self.form formRowWithTag:#"someTag"]

iOS 6 UITextField Secure - How to detect backspace clearing all characters?

In iOS 6 if you type text into a secure text field, change to another text field, then come back to the secure text field and hit backspace, all of the characters are removed. I am fine with this happening, however, I am trying to enable/disable a button based on if this secure text field has characters in it or not. I know how to determine what characters are in the fields and if a backspace is hit but I am having trouble determining how to detect if clearing of all the characters is happening.
This is the delegate method I'm using to get the new text of a field, but, I can't seem to figure out how to get the new text (assuming the new text would just be a blank string) if a backspace is hit that clears all the characters.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
//returns the "new text" of the field
NSString * text = [textField.text stringByReplacingCharactersInRange:range withString:string];
}
Any help is much appreciated.
Thanks!
I use this solution. It does not need local variables and sets the cursor position correctly, after deleting the char.
It's a mashup of this solutions:
Backspace functionality in iOS 6 & iOS 5 for UITextfield with 'secure' attribute
how to move cursor in UITextField after setting its value
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (range.location > 0 && range.length == 1 && string.length == 0)
{
// Stores cursor position
UITextPosition *beginning = textField.beginningOfDocument;
UITextPosition *start = [textField positionFromPosition:beginning offset:range.location];
NSInteger cursorOffset = [textField offsetFromPosition:beginning toPosition:start] + string.length;
// Save the current text, in case iOS deletes the whole text
NSString *text = textField.text;
// Trigger deletion
[textField deleteBackward];
// iOS deleted the entire string
if (textField.text.length != text.length - 1)
{
textField.text = [text stringByReplacingCharactersInRange:range withString:string];
// Update cursor position
UITextPosition *newCursorPosition = [textField positionFromPosition:textField.beginningOfDocument offset:cursorOffset];
UITextRange *newSelectedRange = [textField textRangeFromPosition:newCursorPosition toPosition:newCursorPosition];
[textField setSelectedTextRange:newSelectedRange];
}
return NO;
}
return YES;
}
Finally figured it out for anyone looking to see how to determine when a backspace is going to clear all the characters of a secure UITextField:
UITextField:
self.passwordTextField
Private property (initialized to NO in init - probably not needed):
self.passwordFirstCharacterAfterDidBeginEditing
UITextFieldDelegate Methods:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
//if text is blank when first editing, then first delete is just a single space delete
if([textField.text length] == 0 && self.passwordFirstCharacterAfterDidBeginEditing)
self.passwordFirstCharacterAfterDidBeginEditing = NO;
//if text is present when first editing, the first delete will result in clearing the entire password, even after typing text
if([textField.text length] > 0 && self.passwordFirstCharacterAfterDidBeginEditing && [string length] == 0 && textField == self.passwordTextField)
{
NSLog(#"Deleting all characters");
self.passwordFirstCharacterAfterDidBeginEditing = NO;
}
return YES;
}
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
if(textField == self.passwordTextField)
{
self.passwordFirstCharacterAfterDidBeginEditing = YES;
}
}
I hope this helps someone and I also hope Apple just creates a delegate method that is called when a secure text field is cleared by a delete - this seems a big cumbersome, but it works.
Secure text fields clear on begin editing on iOS6+, regardless of whether you're deleting or entering text. Checking if the length of the replacement string is 0 works if the text field is cleared from a backspace, but annoyingly, the range and replacement string parameters to textField:shouldChangeCharactersInRange:replacementString: are incorrect if the text is cleared when entering text. This was my solution:
1) Register for textFieldTextDidChange notifications.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textFieldTextDidChange:)
name:UITextFieldTextDidChangeNotification
object:nil];
2) Re-invoke the textField:shouldChangeCharactersInRange:replacementString: delegate method if the text field is secure and may have been cleared.
- (void)textFieldTextDidChange:(NSNotification *)notification
{
if (textField.secureTextEntry && textField.text.length <= 1) {
[self.textField.delegate textField:self.textField
shouldChangeCharactersInRange:NSMakeRange(0, textField.text.length)
replacementString:textField.text];
}
}
The accepted solution does not enable a button. It actually changes backspace behaviour on a secure textfield.
This Swift solution does solve the question by correctly informing a delegate about the actual string that will be set in the textField, including when pressing backspace. A decision can then be made by that delegate.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
var shouldChange = true
if let text = textField.text {
let oldText = text.copy()
var resultString = (text as NSString).stringByReplacingCharactersInRange(range, withString: string) as NSString
let inputString = string as NSString
if range.location > 0 && range.length == 1 && inputString.length == 0 {//Backspace pressed
textField.deleteBackward()
shouldChange = false
if let updatedText = textField.text as NSString? {
if updatedText.length != oldText.length - 1 {
resultString = ""
}
}
}
self.delegate?.willChangeTextForCell(self, string: resultString as String)
}
return shouldChange
}
I was facing the same issue, I wanted to detect when the backspace is going to clear all characters so that I can enabled disable some other buttons on screen. So this is how I achieved it.
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
_checkForDelete = NO;
if (textField.isSecureTextEntry && textField.text.length > 0) {
_checkForDelete = YES;
}
return YES;
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (_checkForDelete && string.length == 0) {
_checkForDelete = NO;
//Do whatever you want to do in this case,
//because all the text is going to be cleared.
}
return YES
}
This solution is different because it lets you take action when a secure field already has some text in it and you are trying to edit it, rather in the above accepted answer, you will go in the block even if you hit backspace while already editing a field, that will not clear the whole text but only deletes one character. Using my method you have an option to take an action in this special case, but it will not impact the native flow either.
Posting my alternate solution as I had the exact problem as OP and found I did not have to do any cursor position alteration detailed in the chosen answer.
Below is the UITextFieldDelegate method, I call a custom delegate method didChangeValueForTextField as my button I want to enable is outside of this class.
Class implementing UITextFieldDelegate
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newValue = [textField.text stringByReplacingCharactersInRange:range withString:string];
BOOL shouldReturn = YES;
if (range.location > 0 && range.length == 1 && string.length == 0){
[textField deleteBackward];
if ([textField.text isEmptyCheck]) { // Check if textField is empty
newValue = #"";
}
shouldReturn = NO;
}
if(self.delegate && [self.delegate respondsToSelector:#selector(didChangeValueForField:withNewValue:)]) {
[self.delegate didChangeValueForField:textField withNewValue:newValue];
}
return shouldReturn;
}
The key was detecting the difference between secure field single character deletion and secure field single character deletion that triggers field clear. Detecting this within the above delegate method was necessary.
Class containing button to enable
- (void)didChangeValueForField:(UITextField *)textField withNewValue:(NSString *)value {
// Update values used to validate/invalidate the button.
// I updated a separate model here.
// Enable button based on validity
BOOL isEnabled = [self allFieldsValid]; // Check all field values that contribute to the button being enabled.
self.myButton.enabled = isEnabled;
}
swift3.0 - it works.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == passwordTextfield {
if range.location > 0 && range.length == 1 && string.characters.count == 0 {
if let textString = textField.text {
// iOS is trying to delete the entire string
let index = textString.index(textString.endIndex, offsetBy: -1)
textField.text = textString.substring(to: index)
return false
}
}else if range.location == 0 {
return true
}else {
if let textString = textField.text {
textField.text = textString + string
return false
}
}
}
return true
}

Cocoa binding: NSTextField with empty string for zero value

I have NSTextField with placeholder. And it's binded to some integer property. So I want to display empty text in the field (with placeholder shown) when binded integer is zero.
Is it possible to do it?
(Update)
I discovered that this can be done through NSNumberFormatter - it has —(void) setZeroSymbol: (NSString*) string method. Not tried yet this in practice...
You could use an NSValueTransformer.
(Just in case)Create a new class, subclass from NSValueTransformer. In the implementation, add something like this:
+(Class)transformedValueClass {
return [NSString class];
}
-(id)transformedValue:(id)value {
if (value == nil) {
return nil;
} else {
if ([value integerValue] == 0) {
return #"";
} else {
return [NSString stringWithFormat:#"%d", [value stringValue]];
}
}
}
In Interface Builder, select your field, go to the bindings tab, and in the Value Transformer drop down, either select or type in your class name you made. This should prevent you from having to worry about modifying it elsewhere. I'm not 100% positive about it showing the placeholder (I don't have a Mac available right now).
EDIT:
I can confirm that this does indeed work. Here is a link to a github project I made to show how to use it: https://github.com/macandyp/ZeroTransformer
check the integer value before binding, if you are binding at runtime. Try
int i;
if (i == 0)
{
txt.placeholder = #"text";
}
else
{
[txt setStringValue:[NSString stringWithFormat:#"%d",i]];
}
You can not do conditional binding.
You need to create another property that will hold the value based on condition and use that property and bind to textfield.
I am using bindedString and bindedInteger. bindedString is bound to text field.
Whenever some action is performed it is updated.
- (id)init{
self = [super init];
if (self) {
self.bindedString=#"place holder string";
}
return self;
}
- (IBAction)button:(id)sender {
if (self.bindedInteger==0) {
self.bindedString=#"place holder string";
}
else{
self.bindedString=[NSString stringWithFormat:#"%ld",self.bindedInteger];
}
}

NSTokenField does not check token on blur

I have an NSTokenField which allows the user to select contacts (Just like in Mail.app). So the NSTextField is bound to an array in my model.recipient instance variable.
The user can now select an entry from the auto completion list e.g. Joe Bloggs: joe#blogs.com and as soon as he hits Enter the token (Joe Bloggs) is displayed and model.recipients now contains a BBContact entry.
Now if the user starts to type some keys (so the suggestions are shown) and then hits Tab instead of Enter the token with the value of the completion text (Joe Bloggs: joe#bloggs.com) is created and the NSTokenFieldDelegate methods did not get called, so that I could respond to this event. The model.recipient entry now contains an NSString instead of a BBContact entry.
Curiously the delegate method tokenField:shouldAddObjects:atIndex: does not get called, which is what I would expect when the user tabs out of the token field.
Pressing tab calls isValidObject on the delegate so return NO for NSTokenField in it however you want to return YES if there are no alphanumeric characters in it otherwise the user won't be able to focus away from the field (the string contains invisible unicode characters based on how many tokens exist)
The less fragile implementation i could come up with is:
- (BOOL)control:(NSControl *)control isValidObject:(id)token
{
if ([control isKindOfClass:[NSTokenField class]] && [token isKindOfClass:[NSString class]])
{
if ([token rangeOfCharacterFromSet:[NSCharacterSet alphanumericCharacterSet]].location == NSNotFound) return YES;
return NO;
}
return YES;
}
I was able to solve the problem using #valexa's suggestions. In case of a blur with TAB I have to go through all entries and look up my contact-objects for any strings.
- (BOOL)control:(NSControl *)control isValidObject:(id)token{
if ([control isKindOfClass:[NSTokenField class]] && [token isKindOfClass:[NSString class]])
{
NSTokenField *tf = (NSTokenField *)control;
if ([token rangeOfCharacterFromSet:[NSCharacterSet alphanumericCharacterSet]].location == NSNotFound){
return YES;
} else {
// We get here if the user Tabs away with an entry "pre-selected"
NSMutableArray *set = #[].mutableCopy;
for(NSObject *entry in tf.objectValue){
GSContact *c;
if([entry isKindOfClass:GSContact.class]){
c = (GSContact *)entry;
}
if([entry isKindOfClass:NSString.class]){
NSString *number = [[(NSString *)entry stringByReplacingOccurrencesOfString:#">" withString:#""]
componentsSeparatedByString:#"<"][1];
c = [self findContactByNumber:number];
}
if(c) [set addObject:c];
}
[control setObjectValue:set];
}
}
return YES;
}
This could be because the "enter" key might send the event of the token field to it's action where the "tab" key just adds text to it. You could try to set the -isContinuous property to YES and see if you get the desired results.