UItextfield initialized with empty string? not null? nor nil? - ios

i have a uitextfield, when it is initialized and i didn't input any values into it, i found the value of the uitextfield is not null nor nil.
NSString *notes = (notesField.text)?(notesField.text):#"hello";
NSLog(#"notes: %#",notes);
it returns nothing for notes
NSString *notes1;
//or use legnth
if ([notesField.text isEqual:#""]) {
notes1=#"hello";
NSLog(#"empty textfield: %#",notes1);
//then it returns "hello"
}
else
{
notes1=notesField.text;
NSLog(#"not empty textfield: %#",notes1);
}
Why is that? Can I still user ternary operator ?
like this ?
NSString *notes = ([notesField.text length])?(notesField.text):#"hello";

You can use
NSString *notes = ([notesField.text length])?(notesField.text):#"hello";
OR
NSString *notes = ([notesField.text length]==0)?#"hello":(notesField.text);
OR
NSString *notes = ([notesField.text isEqualToString:#""])?#"hello":(notesField.text);
And for the case when your UITextField has no entry (initial case), use the second or third option , that will be beter. NSString *notes = ([notesField.text length])?#"hello":(notesField.text); won't work fine as you expect because notesField.text will be TRUE even if there is no text in textfield. So you should use notesField.text.length or [notesField.text isEqualToString:#""].
Hope its clear now.

The docs for UITextField state
text
The text displayed by the text field.
#property(nonatomic, copy) NSString *text
Discussion
This string is #"" by default.
Note: if compiling under Xcode 5.02 or 5.1 and running in iOS earlier than iOS7, UITextField.text is initialised to nil. If running in iOS7+ it is initialised to #"".
If compiling in Xcode 4.6.3 or earlier then UITextField.text has (always) been initialised to #"", as per the documentation.
Radar bug: 16336863

UITextField must be initializing itself with a non-nil empty string. In situations where you don't care if the string is empty or nil, you can simply check the length property:
if (!notesField.text.length) {
// text is nil or empty
}
Or using the ternary operator:
NSString *s = notesField.text.length ? notesField.text : #"Default";
This works because sending the -length selector to a nil object will return a default value of 0.

That approach will work just fine.
As for why it's an empty string instead of nil, it's generally bad practice to return nil for something unless you want to indicate an error or an uninitialized state. An empty text field has a value. It's just an empty string.

Related

ios magicalRecord not assigning string value to property

I have problem assigning a NSString value to a property of my model class. When I NSLog the string out, it is there, but when I try to assign the string to a property and then log out the property, its always null, could you please help me with that?
The property I am trying to assign a value to is defined like so:
#property (nonatomic, retain) NSString * text;
I am assigning the value like so:
NSString *categoryText = [self.pickerCategoryText objectAtIndex:[self.categoryPickerView selectedRowInComponent:0]];
newFilter.category.text = [NSString stringWithFormat:#"%#", categoryText];
I have also tried to alloc and init the property and also assign the value like so:
newFilter.category.text = categoryText;
but none of these solutions worked. I am now totally lost and without any other possible clues, could you please help me with that ?
Thx
At the time when you perform:
newFilter.category.text = [NSString stringWithFormat:#"%#", categoryText];
... either newFilter or category might be nil. If they are nil nothing gets changed.

Typecasting to UILabel in Objective-C

I have an NSArray made of UILabels that I initialized with this statement (the actual pointer is created in the .h file):
interLabels = [NSArray arrayWithObjects:inter1,inter2,inter3, nil];
Later on I have an IBAction method that responds and is supposed to update the array of labels when a button is clicked:
-(IBAction)intervalButton:(id)sender{
int count = 0;
double val[3];
if(count < 3){
val[count] = number;
[interLabels objectAtIndex:count].text = [NSString stringWithFormat:#"%.2f", val[count]];
count++;
}
}
However, [interLabels objectAtIndex:count] doesn't seem to be recognized as a UILabel object, so I get a compiler error that states that the property "text" cannot be found on object type "id." How can I get the compiler to recognize this as a UILabel? Is this an issue that can be solved by typecasting?
objectAtIndex returns you an reference of type 'id'. You need to cast it to UILabel before the compiler / IDE will recognise the text property.
E.g.
((UILabel*) [interLabels objectAtIndex:count]).text = ...
Type id does not need to be casted if you assign it to another variable. And in your case i think it would be nicer since you actually do two things, first get a object from the array and then changes state on that object. Consider this.
-(IBAction)intervalButton:(id)sender
{
for ( UILabel *label in interLabels )
{
label.text = [NSString stringWithFormat:#"%.2f", number];
}
}
I assumed you wanted a loop not a if statement since the if statement always evaluated to YES. Also i assumed number is a instance variable in your class.
Actually when i looked at the code again you can probably remove most of it.

Comparing NSString to UITextField text never gets called

I record the value of the text in my UITextField and I want to compare the text to the original text field value later. I try something like this, but I never get the NSLog to be displayed. Any ideas why?
defaultTopicText = topicTextField.text;
if ([topicTextField.text isEqualToString:defaultTopicText]){
NSLog(#"YES");
}else{
NSLog(topicTextField.text);
NSLog(defaultTopicText);
}
The code looks exactly like you see it. The first line I assign the value and the other - I compare with it. And it's not being called.
EDIT:
The code itself IS getting called and I also get the same values when I put them in NSLog. Might the problem be that the text field contains #"\n" characters?
NSLog gives me this:
2013-03-18 20:45:22.037 myapp[524:907]
Here comes the text
2013-03-18 20:45:22.039 myapp[524:907]
Here comes the text
Try to print out the value of the topicTextField.text and see what is shows. otherwise set the breakpoints to see if you are reaching to that particular line of code.
You coud also try comparing after removing the white spaces and new line, if there might be any
NSString *trimmmedText = [topicTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([trimmmedText isEqualToString:defaultTopicText]){
NSLog(#"YES");
}
Try changing to this:
NSString *newString = [defaultTopicText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([newString isEqualToString:defaultTopicText]){
NSLog(#"YES");
}
I typed the following the figured out the answer...
running this should give you your answer:
if(!defaultTopicText){
NSLog(#"defaultTopicText is nil");
}else{
NSLog(#"defaultTopicText is a: %#".[defaultTopicText classname]);
}
defaultTopicText = topicTextField.text;
if ([topicTextField.text localizedCaseInsensitiveCompare:defaultTopicText] == NSOrderedSame){
NSLog(#"YES");
}else{
NSLog(#"\"%#\" != \"%#\"",defaultTopicText, topicTextField.text);
}
Then I realized: topicTextField.text can only not be the same object as itself using this comparison method if it is nil.
topicTextField.text has to be nil... so it ends up executing:
id var = nil;
[var isEqual:nil];
and the runtime makes that return 0;
... so fix your outlet to topicTextField

UITableView titleForHeaderInSection does not returns correct stringWithFormat

I encounter a strange problem when attempting to return a composite string in the tableView's titleForHeaderIn section.
If I NSLog the string, it seems to be good, but when i return it, it crashes !
Here's my code :
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
NSString *title = NSLocalizedString(#"favorites",#"");
NSLog(#"%#", title); // this prints the correct title ("Items" for example...)
int number = (*_tabsections_especes)[0][0];
NSLog(#"%d", number); // this prints the correct number ( "5", for example...)
NSLog(#"%#", [NSString stringWithFormat:#"%# : %d", title, number ] );
// this prints the correct concatenated string ("Items : 5", for example);
return [NSString stringWithFormat:#"%# : %d)", title, number ];
// --> this either crashes the app, or returns anything in the title,
// for example the title of a resource image or another pointer...
}
If I replace "(*_tabsections_especes)[0][0]" by "5", for example, the problem persists.
So, it seems that the issue is about using NSLocalizedString in the stringWithFormat method, then returning it.
What am I doing wrong ?
Use this before
NSString *result = [NSString stringWithFormat:#"%# : %d)", title, number ];
return result;
or use this
NSString *result = [[NSString alloc]initStringWithFormat:#"%# : %d)", title, number ]]autorelease];
return result;
I tested your method and it works. Look for your bug elsewhere.
I have found where the problem was.
Actually it was not in tableView:titleForHeaderInSection, but rather in tableView:viewForHeaderInSection.
In fact, it is because I use a subclass of UIView for the viewForHeaderInSection.
In this subclass, I have an ivar named "title".
In the init method of this subclass, I set this ivar like this :
title = myTitle; // (myTitle is an argument of the custom init method)
And, just later, I use this title like this in the drawRect method :
[title drawAtPoint:CGPointMake(8, 9) withFont:[UIFont systemFontOfSize:19]];
This works fine if I pass a static string like #"example string" from titleForHeaderInSection, and via viewForHEaderInSection.
But not at all if I pass an autorelease object like stringWithFormat.
So, the solution is simply to retain my ivar in the subclass like this:
title = [myTitle retain];
and to release it in the dealloc method of my subclass:
[title dealloc];
Like this, it works and it doesn't crash. I hope this helps and that the explanations are clear.

Why do I have to retain/copy this NSString?

I recently rewrote some code in one of my classes, which gave me an error with an NSString. Here is what I have now:
My class header:
#interface MyViewController : UITableViewController {
NSString *myString;
}
#property (nonatomic, retain) NSString *myString; // Or copy instead of retain.
#end
And implemented some methods:
- (void)viewDidLoad {
myString = #"This is";
if (something) {
myString = [NSString stringWithFormat:#"%# a string.", myString]; // *1
}
[myString retain]; // <-- Why do I have to retain/copy here?
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
/* Some code for creating a UITextView called myTextView */
// ..and then setting the text property:
myTextView.text = myString; // <-- Crashes here if I don't retain/copy in viewDidLoad.
}
}
After some debugging I figured I had to retain/copy the NSString.
Why do I have to retain/copy the NSString in viewDidLoad if I want to use it later on?
Also, I noticed that if I remove the line marked *1, I don't have to retain/copy.
If you use the synthesized setter method to assign the variable, it will automatically retain the NSString for you:
if (something) {
self.myString = [NSString stringWithFormat:#"%# a string.", myString]; // *1
}
One way for you to better understand this would be to prefix your private attribute with an underscore _myString and keep your property as myString - this would make it easy to work out which is being used - the attribute or the property.
As you only seem to be using the value in your class I would question why you have a property at all as I think this is the source of the confusion.
If you have put the prefix in place then you will always know that _myString will need to have a retain when it is assigned to and myString will retain automatically.
The line at *1 is replacing the value of myString with a new string and for some reason this value is autoreleased before your code in the 2nd method is processed. I can't remember the exact reason, but I think this doesn't happen for the first assignment because you create a string literal with #"This is" which is not autoreleased.
I hope this helps.
You can use the retained property accessors which will release/retain automatically
- (void)viewDidLoad {
self.myString = #"This is";
if (something) {
self.myString = [NSString stringWithFormat:#"%# a string.", myString]; // *1
}
// no need to retain
}
The reason it does not crash when you remove the line marked *1 is because myString is still pointing at #"This is", which is a litteral, which lives in a corner of memory for the entire duration of the program and is never destroyed, hence that memory location remains valid.

Resources