I'm just beginning with XCode and coding for iOS, have the following problem. I want to add a function that sets UITextField values in the ViewController based around the value from a UIStepper. The code actually handles formatting three UITextFields, cut it to one to shorten the example. This code works fine:
- (IBAction)Temp_Stepper_Changed:(UIStepper *)sender {
integer_t stepperValue = (integer_t) sender.value;
NSString *temp_format = [[NSString alloc]
initWithFormat: #"%%.%df",stepperValue];
double fahrenheit = [_TempF_Text.text doubleValue];
NSString *FresultString = [[NSString alloc]
initWithFormat: temp_format,fahrenheit];
_TempF_Text.text = FresultString;
}
I have several places I want to do this, so want to create a function to call, and so I put this function into the view controller's .m file:
void Temp_Text_Update (double F_Temp){
NSString *FresultString = [[NSString alloc]
initWithFormat: #"%.2f",F_Temp];
_TempF_Text.text = FresultString;
}
The function won't compile, results in error:
use of undeclared identifier '_TempF_Text'
Without the line, it compiles fine, can call the function, pass values, etc. I had assumed (remember, beginning at this) as the UIStepper had _TempF_Text in it's scope, the function being in the same .m file would as well. Is there some magic happening behind the scenes that allows the IBAction type calls to access any value from the ViewController items, but my function is missing said magic? I'll also need the UIStepper value to complete the function. This was built using Storyboard, control-drag for outlets and actions, header file is:
#interface TemperatureViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *TempF_Text;
#property (weak, nonatomic) IBOutlet UIStepper *Temp_Stepper;
- (IBAction)TempF_CnvButton:(id)sender;
- (IBAction)Temp_Stepper_Changed:(UIStepper *)sender;
I've spent a few hours searching, including this site, found references from one ViewController to another and so forth, but doesn't really match; tried a few things anyway, but nothing worked (though some yielded extra errors). I suspect it is so obvious and simple as to not be asked, but I've run out of ideas and any help would be appreciated.
To answer the specific question, the correct syntax for the signature you're trying to write would look like this:
- (void)Temp_Text_Update:(double)F_Temp {
This says we have a method named Temp_Text_Update: that takes a double parameter called F_Temp and doesn't return anything.
A more general solution would be to use NSNumberFormatter to go between strings and doubles in a locale sensitive way, something like this:
// get a double from a string
- (double)doubleFromString:(NSString *)string {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *number = [formatter numberFromString:string];
return [number doubleValue];
}
// get a string from a double:
- (NSString *)stringFromDouble:(double)aDouble {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
return [formatter stringFromNumber:[NSNumber numberWithDouble:aDouble]];
}
Then your computing functions would look like this:
NSString *text = self.someControlView.text;
double aDouble = [self doubleFromString:text];
// do computation on aDouble, resulting in
double result = // whatever
[self setOutputTextWithResult:result];
Finally, your method (better named):
- (void) setOutputTextWithResult:(double)result {
self.someControlView.text = [self stringFromDouble:result];
}
It's so short now, it almost doesn't need it's own method.
Related
I need to encode additional data to a NSString (Long story, please don't ask why...)
I've subclassed NSString using the method outlined here:
When I assign one of these subclasses as a UILabel's text I would expect to get it back when asking the labels text. But this isn't the case. (I get an NSString cluster instance instead)
MyString *string = [[MyString alloc] initWithString:#"Some string"];
UILabel *l = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
l.text = string;
NSString *t = l.text; // not getting the "MyString" object
Is there a work around for this?
The label copies the string:
#property (nonatomic, copy) NSString *text
so you at least need to implement copy to return your subclass type and copy your other data.
(not that subclassing is the best idea)
If you subclass NSString, you are braver than I would ever be, and you are totally on your own. NSString is a class cluster. You have basically not a chance in hell to subclass it and get it to work. It starts with the initialisation where [super init] which would be the NSString init method might return any kind of object.
How come this code isn't working to set a character limit in XCode?
I wrote it in my viewDidLoad() under ViewController.m
NSNumberFormatter* formatter= [[NSNumberFormatter alloc]init];
NSFormatter.numberStyle= NSNumberFormatterDecimalStyle;
formatter.allowsFloats= NO;
formatter.minimum= #5;
formatter.maximum= #15;
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
return [formatter numberFromString: textField.text]!= nil;
}
Errors:
http://imgur.com/h83hzsw
From the errors, it looks like you're not using this code inside an actual method (such as viewDidLoad) Since you need it in multiple methods, you'll need your formatter to be accessible throughout the class. Try moving things around a bit:
#implementation ViewController
{
NSNumberFormatter *formatter;
}
...
- (void)viewDidLoad
{
...
formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle= NSNumberFormatterDecimalStyle;
formatter.allowsFloats= NO;
formatter.minimum= #5;
formatter.maximum= #15;
}
You are getting the error 'Initializer element is not a compile-time constant' because you are initialising the variable NSNumberFormatter* formatter not in a method or function and the compiler therefor expects a constant value.
See SO answer here for more detail - Initializer element is not a compile-time constant
I have an object: indivOrder:
#interface indivOrderDetails : NSObject{
NSNumber* shirtNumber;
NSNumber* pantsNumber;
NSNumber* jacketNumber;
NSNumber* laundryNumber;
NSNumber* blouseNumber;
NSNumber* blazerNumber;
NSNumber* skirtNumber;
NSNumber* suitNumber;
NSString* pickUpOrDropOff;
NSString* pickUpFrom;
NSNumber* totalOrderPrice;
}
They're all given the interface of
#property (nonatomic, retain) NSNumber* propertyName
I have three steps.
First I retrieve the data from a text field:
shirtNumber = [self convertStringToNumber:shirtField.text];
Second, I use this convertStringToNumber method.
-(NSNumber*) convertStringToNumber:(NSString*)stringToConvert
{
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *myNumber = [f numberFromString:stringToConvert];
return myNumber;
}
Then I assign that value to my object variable.
orderDetails.shirtNumber = shirtNumber;
But the only value I'm coming back with when I try to access the orderDetails.shirtNumber variable is zero. The shirtNumber is coming back with the correct value from the ViewController.
Your problem likely lies here:
shirtNumber = [self convertStringToNumber:shirtField.text];
Look at that to which you're assigning. It's one of the ivars you declared. That ivar, despite what you may think, is not the backing for your property of the same name. The backing instead would be
_shirtNumber
since you don't appear to have synthesized (i.e., used the #synthesize directive) any accessors.
As a result, _shirtNumber and shirtNumber are two distinct entities, which means that when you attempt to access
orderDetails.shirtNumber
of course it's going to be nil. You never put anything in it.
So you have two choices: Either use the property name that's prefixed with an underscore
_shirtNumber = [self convertStringToNumber:shirtField.text];
or use #synthesize to set up some accessors, in which case you'd do this
self.shirtNumber = [self convertStringToNumber:shirtField.text];
or this (if you like the old form)
[self setShirtNumber:[self convertStringToNumber:shirtField.text]];
Guess that's three choices. Anyway, it's a subtle, classic 'gotcha.' If you want further background for this, you can find it in this very excellent explanation elsewhere on Stack Overflow.
Good luck to you in your endeavors.
Hi a very simple app it takes in 2 arguments via 2 text boxes, and then totals them and displays them in a label called result. The idea is to have it handled via an object called brain, for which in the later part i have given the code. problem is foo is zero and when you click the button the result goes to nothing.
The plan is to use this to build a better model view architecture for a bigger app i have completed.
#import "calbrain.h"
#import "ImmyViewController.h"
#interface ImmyViewController ()
#property (nonatomic, strong) calbrain *brain;
#end
#implementation ImmyViewController
#synthesize brain;
#synthesize num1;
#synthesize num2;
#synthesize result;
-(calbrain *) setBrain
{
if (!brain) {
brain = [[calbrain alloc] init];
}
return brain;
}
- (IBAction)kickit:(UIButton *)sender {
NSString *number1 = self.num1.text;
NSString *number2 = self.num2.text;
NSString *foo;
foo = [brain calculating:number1 anddouble:number2];
self.result.text = foo;
// self.result.text = [brain calculating:self.num1.text anddouble:self.num2.text];
}
#end
#implementation calbrain
-(NSString *) calculating:(NSString *)number1 anddouble:(NSString *)number2
{
double numb1 = [number1 doubleValue];
double numb2 = [number2 doubleValue];
double newresult = (numb1 + numb2);
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSString *numberAsString = [numberFormatter stringFromNumber:[NSNumber n numberWithFloat:newresult]];
return numberAsString;}
Check your brain using NSLog in the (IBAction)kickit:(UIButton *)sender function. I guess you didn't initialise brain. If this is not the case, you need to provide more code.
i did just that, i came to the conclusion the setter for brain isnt working properly
i put the alloc init line of code before i needed to alloc init the brain, and it works fine, i stubbed out the setter,
i will go back and see why it wasnt overriding the setter made by properties, but interesting stuff none the less. it means i can change my actual larger app to have a cleaner more organised architecture.
thanks for your time.
Try initializing your brain object in viewDidLoad() using your setter method. You have to call setter method to get your brain object initialized.
Something like this
viewDidLoad()
{
brain = [self setBrain];
//You can also do this
brain = [[calbrain alloc] init];
}
and use that brain object in your (IBAction)kickit: method.
Hope this helps.
Let me begin to tell you that I am new to Objective C. I have just finished Big Nerd Ranch's book and i want to create a real simple and basic app to learn more.
My idea was to create an app that will calculate the weeks between 2 dates. I have created a class for that and tested it. That works.
As you can see below, I have created to views (programmatically), One with the dates and the other will become visible when you click on start or end date.
If you select a date and click on the button 'calculate weeks', you will go back to the first view.
No my big question is, how do I get this selected value back to my main screen? I have tried several possibilities and search the web for information, but I couldn't get is to work.
I know this should be real easy, but for me at this moment it isn't. :-)
I have created a NSMutableArray that contains the values "Start date" and "End date". My idea was to add the value of the UILabel from the SelectDateView to this array.
I have created a property In the inputview #property (readwrite, retain) NSMutableArray *datesArray; for that.
in the selectDateViewController i have created another property #property (nonatomic, assign) BITInputViewController *ivc; so I (in my opinion) can add a value to datesArray.
When I select a date this method is called, it works for the UILabel on SelectDateView, but doesn't do anything with the datesArray.
- (void)LabelChange:(id)sender{
NSDateFormatter *df = [[NSDateFormatter alloc] init];
df.dateStyle = NSDateFormatterMediumStyle;
dateLabel.text = [NSString stringWithFormat:#"%#",
[df stringFromDate:datePicker.date]];
[ivc.datesArray addObject:dateLabel.text];
if (ivc.datesArray) {
for (NSString *d in ivc.datesArray) {
NSLog(#"This is in datesArray %#",d);
}
}else NSLog(#"!ivc.datesArray");
}
When I test the app and select a date, I always see "!ivc.datesArray" in the log file.
I also do this check of ivc.datesArray in -(void)viewWillAppear:(BOOL)animated
and here I see the current date, as I set this date in - (void)viewDidLoad
with this dateLabel.text = [NSString stringWithFormat:#"%#",[df stringFromDate:[NSDate date]]];
(When I print out the array in the inputview, it does show start date and end date, but not the selected date. )
Hopefully someone can give me a few pointers on this.
declare a NSString* date above the #interface in your view1.m file and the create a method in tht first class it should be something like
-(void)passDate:(NSString *)dateString
{
date = [NSString stringWithFormat:#"%#",dateString];
}
and in your second class create object of first class like
View1 * vc = [[View1 alloc]init];
[vc passDate:df];
Hope it works. You have to declare NSString before the #interface and no property and synthesize.