Cocoa binding: NSTextField with empty string for zero value - objective-c

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];
}
}

Related

Checking whether or not a UITableView cell contains a keyword

I was trying to see if a UITableView cell contains a string.
I have tried using:
if ([cell.textlabel.text rangeOfString:#"Ok"].location !=NSNotFound)
{
}
But that was no use. It found the text every time no matter what.
You should test if the cell.textlabel.text string is not nil. I've tested your code with a nil value and always returns TRUE.
if(cell.textlabel.text)
{
if ([cell.textlabel.text rangeOfString:#"Ok"].location !=NSNotFound)
{
...
}
}
Try like this below:-
NSString *str=cell.textlabel.text;
if ([str rangeOfString:#"Ok"].location !=NSNotFound)
{
}

OBJ C - How to get the textfield name from a sender

I have several textfields, each with tags, that I want to individually add to an array. I need to figure out which field it is coming from before I add it. I would like to use the same method for all of them rather than have a method for each textfield.
Is it possible to get the variable name of the textfield from the sender? If they were button I could use [sender currentTitle], but I don't know how to get an identifier from the textfield.
I am thinking of something like this:
- (void)makeItSo:(id)sender
{
NSString * senderName = (UITextField*)[sender stringValue] ;
if ([senderName isEqual: #"name"] )
-- add name to array
else if ([senderName isEqual: #"address"] )
-- add address to array
}
If you give each text field a tag, then use the tag:
- (void)makeItSo:(UITextField *)sender {
if (sender.tag == 1) {
// the name text field
} else if (sender.tag == 2) {
// the address text field
}
}
This assumes you have set the tag property for each text field either in IB or in code.
It could be useful to define constants for each tag so you end up with something that is easier to read:
#define kNameTextField 1
#define kAddressTextField 2
- (void)makeItSo:(UITextField *)sender {
if (sender.tag == kNameTextField) {
// the name text field
} else if (sender.tag == kAddressTextField) {
// the address text field
}
}
If you have outlets or instance variables then you can do:
- (void)makeItSo:(UITextField *)sender {
if (sender == _nameTextField) {
// the name text field
} else if (sender == _addressTextField) {
// the address text field
}
}
where _nameTextField and _addressTextFields are the ivars for the text fields.
Is it possible to get the variable name of the textfield from the sender?
No, unless it's an instance variable, in which case you can, but you better don't.
I don't know how to get an identifier from the textfield
As always, it's enough to read the documentation as use the tag property of UIView:
if ([sender tag] == SOME_CUSTOM_PRESET_VALUE) {
// do stuff
}
For example you may have these text fields as ivars:
#property (weak) UITextField* textField1; // tag=1
#property (weak) UITextField* textField2; // tag=2
...
#property (weak) UITextField* textFieldN; // tag=N
When you receive an action you simply do:
- (void)makeItSo:(id)sender
{
// This is the searched text field
UITextField* textField= [self valueForKey: [NSString stringWithFormat: #"textField%d",sender.tag] ];
}
But at this point why not using a single property which is an array with N text fields, instead of N properties?

NSNumberformatter quasi Scientific notations

I need the NSNumberFormatter to conditionally display very huge or very small numbers as Scientific notation. But if the number can be presented without, i need it to be only decimal.
6.62e-34 is Ok, but 4,2E1 is not.
Elsewhere in the system, I have solved this using the following method:
- (NSString *) formatNumber: (double) d {
BOOL sci=NO;
double dd = d;
if (dd<0.0) dd=(-d);
if ((dd>0.0) && (dd<1e-3)) sci=YES;
if (dd>1e+8) sci=YES;
if (sci) [nf setNumberStyle:NSNumberFormatterScientificStyle];
else [nf setNumberStyle:NSNumberFormatterDecimalStyle];
return [nf stringFromNumber:[NSNumber numberWithDouble:d]];
}
But at this particular place, the whole thing is set up in the interface builder, with bindings to an managed object modell and using an array controller object. In the first text cell of the colum in the tableview, I have dragged in the numberformatter. I dont know if its possible to define some custom methods to be called to do the formatting, instead of using this NumberFormatter. If I take it away, I got errors when I run it.
anyone have some idea ?
What you want to do is write a subclass of NSValueTransformer. You can then provide the name of that transformer class in IB, in the bindings inspector. Here is an example:
#implementation RDTransformer
- (id)init {
if (self = [super init]) {
self.formatter = [[NSNumberFormatter alloc] init];
[self.formatter setNumberStyle:NSNumberFormatterScientificStyle];
[self.formatter setMaximumFractionDigits:3];
}
return self;
}
+(Class)transformedValueClass {
return [NSString class];
}
+(BOOL)allowsReverseTransformation {
return NO;
}
-(id)transformedValue:(NSNumber *)value {
if ([value compare:#1000] == NSOrderedAscending) {
return value.stringValue;
}else{
return [self.formatter stringFromNumber:value];
}
}
In IB, the column (in a single column table) has its value bound to Array Controller.arrangedObjects with a value transformer of RDTransformer. I don't have any formatters connected to the table cells.

How can I force NSTextField to only allow numbers?

In Interface Builder I’ve created a textfield and stuck an NSNumberFormatter into the cell. However, the user can still type text into the textfield. Is there any way to restrict the input to numbers only using interface builder? I thought that was the point of the NSNumberFormatter.
Every formatter has this method:
- (BOOL) getObjectValue: (id*) object forString: (NSString*) string
errorDescription: (NSError**) error;
This is valid for every formatter.
If the string is not valid it returns false and the object passed as argument (dereferenced) will be nil.
So instead of dropping a number formatter to the text field, use your own formatter as instance variable.Observe the text field implementing the delegate protocol so that at every change of the text you can be notified.
Then invoke this method:
NSNumber* number;
BOOL success=[formatter getObjectValue: &number forString: [myTextField stringValue] errorDescription:nil];
At this point if success is false (or check if number is nil), there is an invalid string in the text field, so do the action that is more appropriate for you (maybe delete the entire string, or display 0.0 as value).
There is also another method:
- (BOOL) isPartialStringValid : (NSString*) partialString: (NSString*) partialString
errorDescription: (NSString*) error;
This way you can know if a partial string is valid.For example with the scientific notation "1e" is not valid, but is partially valid because the user may add 1 and it will become "1e1".
Create an NSNumberFormatter subclass and put this in the implementation. In your code, set the YKKNumberFormatter as the formatter for the NSTextField/UITextField.
#implementation YKKNumberFormatter
- (BOOL)isPartialStringValid:(NSString *)partialString newEditingString:(NSString **)newString errorDescription:(NSString **)error {
// Make sure we clear newString and error to ensure old values aren't being used
if (newString) { *newString = nil;}
if (error) {*error = nil;}
static NSCharacterSet *nonDecimalCharacters = nil;
if (nonDecimalCharacters == nil) {
nonDecimalCharacters = [[[NSCharacterSet decimalDigitCharacterSet] invertedSet] retain];
}
if ([partialString length] == 0) {
return YES; // The empty string is okay (the user might just be deleting everything and starting over)
} else if ([partialString rangeOfCharacterFromSet:nonDecimalCharacters].location != NSNotFound) {
return NO; // Non-decimal characters aren't cool!
}
return YES;
}
#end

NSFormatter for BOOL

I have set up my simple Xcode project with a table that is binded to an array controller. It works fine if the array controller is full of entities with a string attribute. However I want to change the attribute to a BOOL and have the table show the string "true" or "false" based on the BOOL.
I have overrided the following two methods from NSFormatter:
-(NSString*) stringForObjectValue:(id)object {
//what is the object?
NSLog(#"object is: %#", object);
if(![object isKindOfClass: [ NSString class ] ] ) {
return nil;
}
//i'm tired....just output hello in the table!!
NSString *returnStr = [[NSString alloc] initWithFormat:#"hello"];
return returnStr;
}
-(BOOL)getObjectValue: (id*)object forString:string errorDescription:(NSString**)error {
if( object ) {
return YES;
}
return NO;
}
So the table gets populated with "hello" if the attribute is a string however if I switch it to a boolean, then the table gets populated with lots of blank spaces.
I don't know if this helps but on the line where I'm outputting the object, it outputs __NSCFString if the attribute is a string and "Text Cell" if I switch the attribute to a boolean. This is something else I don't understand.
Ok, it's not 100% clear what you're trying to do from the code, but first things first - BOOL is not an object, it's basically 0 or 1, so to place BOOL values into an array, you're probably best off using NSNumber:
NSNumber *boolValue = [NSNumber numberWithBool:YES];
and placing these into your array. Now you want to change your method:
-(NSString*) stringForObjectValue:(id)object {
NSNumber *number = (NSNumber *)object;
if ([number boolValue] == YES)
return #"true";
else
return #"false";
}
There's a few things here - for example, you want to avoid passing around id references if you can (if you know all your objects in the NSArray are NSNumber, you shouldn't need to).