Detect string or integer in Core Data - iphone

I have built in some Core Data support into my app from the Core Data Books example. The example uses Dates and Strings. However I have tried adding the ability to add and edit an Integer value.
//If the value is a string
if ([[editedObject valueForKey:editedFieldKey] isKindOfClass:[NSString class]]) {
textField.hidden = NO;
datePicker.hidden = YES;
textField.text = [editedObject valueForKey:editedFieldKey];
textField.placeholder = self.title;
[textField becomeFirstResponder];
}
//If the value is a number
else {
textField.hidden = NO;
datePicker.hidden = YES;
textField.text = [[editedObject valueForKey:editedFieldKey] stringValue];
textField.placeholder = self.title;
[textField becomeFirstResponder];
}
The first if statement is the in example code (without the check if its a string, I added that) and I added the else statement to run when its not a string but an integer. It works, however now when I edit a string it skips the if statement, so the line: if ([[editedObject valueForKey:editedFieldKey] isKindOfClass:[NSString class]]) isn't working somehow.
If you do look at the CoreDataBooks example from Apple, my code is the same, only I added a field which takes an Integer 16.
Edit
When putting a breakpoint on the first if statement and returning po [editedObject valueForKey:EditedFiledKey] in the console I get: Can't print the description of a NIL object.
I assume this is because it's before the object is made? This happens when the view appears (the view to enter a new string).
It's upon pressing the save button that this code is run:
- (IBAction)save {
// Set the action name for the undo operation.
NSUndoManager * undoManager = [[editedObject managedObjectContext] undoManager];
[undoManager setActionName:[NSString stringWithFormat:#"%#", editedFieldName]];
// Pass current value to the edited object, then pop.
if ([[editedObject valueForKey:editedFieldKey] isKindOfClass:[NSString class]]) {
[editedObject setValue:textField.text forKey:editedFieldKey];
}
else {
[editedObject setValue:[NSNumber numberWithInteger:[[textField text] integerValue]] forKey:editedFieldKey];
}
[self.navigationController popViewControllerAnimated:YES];
}
When this runs, its skips the first if statement and runes the else statement, then crashing and showing the error: Unacceptable type of value for attribute: property = "firstName"; desired type = NSString; given type = __NSCFNumber; value = 0.
firstName is the string attribute in my data model. Im guessing because that first if statement fails, its goes forward an expects an integer? Im really unsure.

OK, so based on the value being nil in the debugger, let me explain what's happening. In Objective-C, any message sent to nil object will simply do nothing, and then return nil (which happens to have exactly the same memory value as 0 and false and NO).
So you're doing this:
if ([[editedObject valueForKey:editedFieldKey] isKindOfClass:[NSString class]]) {
If editedObject is nil, then valueForKey will do nothing and return nil. Then you're sending isKindOfClass to nil which will also do nothing and return nil. Inside an if statement, nil will evaluate to NO, sending you to the else statement.
Where you do this:
textField.text = [[editedObject valueForKey:editedFieldKey] stringValue];
editedObject is nil, cascading to stringValue returning nil, and therefore you are trying to set the text field's value to nil, which is invalid and will crash your app.
The solution is to restructure your code to check for nil. Here's how I would write your code:
// don't do anything for a nil value note this will detect editedObject being nil, or the result of valueForKey: being nil.
if (![editedObject valueForKey:editedFieldKey]) {
return;
}
// figure out the string value
NSString *textFieldValue = [editedObject valueForKey:editedFieldKey];
if ([textFieldValue isKindOfClass:[NSNumber class]]) {
textFieldValue = [(NSNumber *)textFieldValue stringValue]
}
// update the text field
textField.hidden = NO;
datePicker.hidden = YES;
textField.text = textFieldValue;
textField.placeholder = self.title;
[textField becomeFirstResponder];

I tackled the same problem with more Core Data app. I also adapted the Core Data Books app. If you notice, in the original app, they use a BOOL variable (editingDate) to decide whether to show the date picker or not. I created a second BOOL variable, ('editingTextView`) and just change those BOOL variables depending on what needs to be edited. It may not be the most efficient way, but it is easy to program, and easy to follow what is already there in Core Data Books.

Related

UITextFeld Comparison Failing

I have a UITextField and I create it like so:
firstnameField1 = [[UITextField alloc] initWithFrame:CGRectMake(85+320*4, 80, 150, 30)];
[firstnameField1 setBorderStyle:UITextBorderStyleRoundedRect];
[firstnameField1 setPlaceholder:#"Firstname"];
[firstnameField1 setDelegate:self];
[firstnameField1 setReturnKeyType:UIReturnKeyNext];
[scrollViewController addSubview:firstnameField1];
When the user taps the return key I want to check if the text field has anything typed into it, if its empty, the user hasn't typed anything, I want to return and show a label telling the user that field is required to be filled before they can continue, just like you see all over the place.
I do the following to check:
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
//Check if the user has typed anything
if (textField.text == #"") {
//If not, show 'required' labels
[firstNameRequiredLbl1 setAlpha:1];
[surnameRequiredLbl1 setAlpha:1];
[firstNameRequiredLbl2 setAlpha:1];
[surnameRequiredLbl2 setAlpha:1];
return YES;
}
//Do all my other stuff, cut out for ease of reading, 100% doesn't affect this anyway
return YES;
}
I have set a breakpoint on that last method and it jumps right past that if statement whether I type into that textfield or not.
Any ideas? Thanks.
You'll want to change textField.text == #""
to: textField.text.length == 0 or textField.text isEqualToString:#""
What the == operator is actually doing in this case is checking if the strings are stored in the same area of memory rather than whether or not they contain the same characters.
You can't compare NSString text strings that way. Use this instead:
if ([textField.text isEqualToString: #""])

label not updating in setter

ok so this problem is kinda weird because the NSLog I have right in front of the line of code that should be printing out the text is returning the correct value.
Here's the code:
-(void)setCurrentDate:(UILabel *)currentDate
{
NSInteger onDay = 1; //because if it's today, you are on day one, not zero... no such thing as a day zero
//get the nubmer of days left
if( [[NSUserDefaults standardUserDefaults] objectForKey:#"StartDate"] ){ //if there is something at the userdefaults
onDay = [self daysToDate:[NSDate date]];
}//otherwise, onDay will just be one
self.theCurrentNumberOfDaysSinceStart = onDay;
NSLog(#"On day: %d", onDay); //this is returning the correct values....
//print it out on the label
[currentDate setText:[NSString stringWithFormat:#"On day: %d", onDay]];//echoes out the current day number
}
So when the app first launches, everything is fine. The label updates and everything. The problem arises when I hit a button that basically grabs a new date. In the process, it runs this:
//need to reload the "on day" label now
[self setCurrentDate:self.currentDate];
//and the "days left" label
[self setDaysLeft:self.daysLeft];
Again, I'm thinking this should all be correct because the NSLog is returning the correct stuff. I'm thinking that the problem is with the last line in the first block of code I showed... the line with the setText.
thanks for all your help!
cheers,
Matt
If you used a nib
When the nib loads and establishes all of it connections it... (From the Resource Programming guide)
looks for a method of the form set OutletName: and calls it if such a method is present
Therefore the nib will load and call setCurrentDate: passing in the unarchived UILabel as the parameter
In your method you configure the UILabel using the local reference passed into the method
[currentDate setText:[NSString stringWithFormat:#"On day: %d", onDay]];
You at no point actually store a reference to this UILabel in an ivar, so technically you have leaked the label and as you have not set the ivar currentDate it will be initialised to nil. This is the danger of overriding a setter with an incorrect implementation.
At some point in your method you should be setting your ivar to the passed in variable. A normal setter would look like this
- (void)setCurrentDate:(UILabel *)currentDate;
{
if (_currentDate != currentDate) {
[_currentDate release];
_currentDate = [currentDate retain];
}
}
But
In your example I would not worry about this at all I would instead change this
//need to reload the "on day" label now
[self setCurrentDate:self.currentDate];
to something like
[self updateCurrentDate];
The implementation would look something like:
- (void)updateCurrentDate;
{
NSInteger onDay = 1;
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"StartDate"]) {
onDay = [self daysToDate:[NSDate date]];
}
self.theCurrentNumberOfDaysSinceStart = onDay;
[self.currentDate setText:[NSString stringWithFormat:#"On day: %d", onDay]];
}

Issue with transferring and editing integer info stored in coredata

I'm looking for some help. I've tried searching this site and have tried amending my code but I'm stuck. My issue is with displaying and editing an integer attribute stored in coredata. I have a detailview which uses a UITableView. It seems to display the integer correctly in the cell, using the following code:
cell.textLabel.text = #"Set target";
cell.detailTextLabel.text = [match.set_target stringValue];
but, when I try and edit the value by passing it to a UITextField on an editing view, it displays the integer incorrectly (for example 3 is displayed as 53916). I'm passing the value to the UITextField with this code: (note, editedObject is NSManagedObject, numField is UITextField, editedFieldKey is NSString).
[numField setText:[NSString stringWithFormat:#"%d", editedFieldKey]];
The values are passed to the editing view from the detail view using this code:
controller.editedFieldKey = #"set_target";
controller.editedFieldName = NSLocalizedString(#"Number of sets to win", #"set_legs");
I can display, edit and save strings and dates but I can't figure out Integers. Any help would be appreciated.
EDIT 1
I have save and cancel buttons on my edit view. The save button invokes:
- (IBAction)save {
// Set the action name for the undo operation.
NSUndoManager * undoManager = [[editedObject managedObjectContext] undoManager];
[undoManager setActionName:[NSString stringWithFormat:#"%#", editedFieldName]];
if (editingDate) {
[editedObject setValue:dateField.date forKey:editedFieldKey];
}
else if (editingNum) {
[editedObject setValue: [NSNumber numberWithInteger: [numField.text integerValue]] forKey: editedFieldKey];
}
else {
[editedObject setValue: textField.text forKey:editedFieldKey];
}
[self.navigationController popViewControllerAnimated:YES];
}
With the code for displaying the integer set as:
[numField setText:[NSString stringWithFormat:"%d", [editedObject valueForKey:editedFieldKey]]];
I have a warning about the line: Passing argument 1 of 'StringwithFormat' from incompatible pointer type.
On running it crashes with: +[NSString WithFormat:]: unrecognized selector sent to class 0x211d60'
You are passing a string to numberWithInteger, when instead you want the integer value from the string. Try changing it to this:
[editedObject setValue: [NSNumber numberWithInteger: [numField.text integerValue]] forKey: editedFieldKey]; .
"editedFieldKey is NSString"
[numField setText:[NSString stringWithFormat:#"%d", editedFieldKey]];
replace the formatter with %#
[numField setText:[NSString stringWithFormat:#"%#", editedFieldKey]];
EDIT:
Since you are not adding anything to the string, the stringWithFormat: message is unnecessary.
The following will product the same result.
[numField setText:editedFieldKey];
And for those who prefer '.' syntax for property accessors
numField.text = editedFieldKey;
EDIT 2
I will assume that you are passing in your managed object and context to the editing controller...
To display the value of the "set_target" attribute of your managed object.
[numField setText:[[editedObject valueForKey:editedFieldKey] stringValue]]; // set_target is returned as an NSNumber

Accessing a UITextField from an array in Objective-C

I have 4 UITextFields that I'm dynamically creating, in the viewDidLoad, which works good. I want to reference those objects when the UISlider value changes. Right now I'm storing those objects in a NSMutableArray and accessing them like so from the sliderChanged method:
NSInteger labelIndex = [newText intValue];
labelIndex--;
NSUInteger firstValue = (int)0;
NSMutableArray *holeArray = [pointsArray objectAtIndex:labelIndex];
UITextField *textField = [textFieldArray objectAtIndex:firstValue];
NSString *newLabel1Text = [[NSString alloc] initWithString:[[holeArray objectAtIndex:firstValue] stringValue]];
[textField setText: newLabel1Text];
[newLabel1Text release];
Everything is working good, but the program crashes on the setText: method. The last message I get from the program is: [UILabel drawTextInRect:] and then I get a EXC_BAD_ACCESS failure.
I want to be able to acces that dynamically created UITextField, but I must be going about it the wrong way.
Thanks!
Uh, yea, you create a text field, but you aren't displaying the field itself, just creating it.
If you want to do what I think you want to do, I would just do if statements.
ex.
if (firstValue == 1)
{
fieldone.text = #"whatever";
}
else if (firstValue == 2)
{
fieldtwo.text = #"whatever";
}

UITextField text value returns garbage

I am trying to get a string value out of a textField when the user dismisses the keyboard. However, it seems that whenever I try to get the value, I get garbage (attempting to print out textField.text gives out garbage). What could I be doing wrong?
(The control displays fine, and I can put text values into it even).
Here's my code:
-(BOOL)textFieldShouldReturn:(UITextField *)textField {
NSInteger currenttag = textField.tag;
NSLog(#"%d",textField.tag);
if (currenttag == 0) {
NSLog(#"%x %s",(unsigned int)textField.text,textField.text);
username = textField.text;
} else if (currenttag == 1) {
password = textField.text;
}
[textField resignFirstResponder];
return YES;
}
The fields username and passwords are nil NSString*'s, but since I will merely hold on to the NSStrings held by textField.text, it should be fine.
NSLog(#"text field text:%#",textField.text);
Have you tried using breakpoints? Have you tried NSLog(#"%#", textField.text); ?
Have you tried rewriting the function so it only displays the text?
Is the textField a valid object?
Inserting [textField retain]; as the 1st line will probably fix the problem. Just remember to add a [textField release]; at the end of the method.