Change label text with value from different file? - iphone

I have these functions here that will record the time between starting point and pressing a button to stop the time, then finally print the length of time recorded:
-(void)informToPress
{
textLabel.text = #"Test, press the button";
//begin record and end record on button press
startDate = [[NSDate alloc]init];
}
-(IBAction)stopTime{
stopDate = [[NSDate alloc]init];
textLabel.text = [NSString stringWithFormat:#"Time : %f", [stopTimer timeIntervalSinceDate:startTimer]];
}
But where I have:
textLabel.text = [NSString stringWithFormat:#"Time : %f", [stopTimer timeIntervalSinceDate:startTimer]];
I need this to be placed in a different View, and therefore a different .m file! How could I use this line of code in a completely different file? As the new file/view doesn't know what the values are or textLabel.

You can implement a singleton called dateManager with the properties startTimer and stopTimer.
You will create only one instance of dateManager so it will be the same value for your properties anywhere in the code.
Or you can also create in one .h file (it's easier):
static NSDate *startTimer = nil;
static NSDate *stopTimer = nil;
And give them values when you need but don't forget to include the .h file where you need the variables.
And the right way to initiate a NSDate with the current date is to do like this :
NSDate *currentDate = [NSDate date];

Related

How do I measure the time interval from a starting point using NSDate?

I have one method that I use in many places throughout my project that looks like the following:
-(void)showSignInView
{
if(check for time interval)
[[self superview] addSubview:loginController.view];
}
I'd like to note the first time that this method is called, then on every subsequent call of this method check to make sure that the interval has been more than 15 minutes from the original call. Only then will it execute the rest of its code.
I know that you can use NSDate to measure time intervals using code like the following:
NSDate *firstTime = [[NSDate date] retain];
NSDate *SecondTime = [NSDate date];
NSLog(#"Time elapsed: %f", [SecondTime timeIntervalSinceDate:firstTime]);
but I'm not sure how to implement the initial time check, then subsequent comparisons to that time. How can I do this?
Create a property named previousTime.
#property(nonatomic, retain) NSDate *previousTime;
And create a method to find the time difference.
- (NSTimeInterval)timeDifferenceSinceLastOpen {
if (!previousTime) self.previousTime = [NSDate date];
NSDate *currentTime = [NSDate date];
NSTimeInterval timeDifference = [currentTime timeIntervalSinceDate:previousTime];
self.previousTime = currentTime;
return timeDifference;
}
You could use GCD to achieve this. The dispatch_once() function can arrange that a block is only executed once in the lifetime of your app.
NSDate *firstTime = nil;
- (void)loadView {
[self calculateTime:[NSDate dateWithTimeIntervalSince1970:1312996898]];
}
- (void)calculateTime:(NSDate*)secondTime
{
double offset = [secondTime timeIntervalSinceDate:[self getFirstTime]];
if (offset >= 900.0) {
NSLog(#"15 min gone");
}
}
- (NSDate *)getFirstTime
{
if (!firstTime) {
firstTime = [[NSDate date] retain];
}
return firstTime;
}

iphone how to pass string to another view label?

My problem is how to pass string to another view label? I got try so many example but still can not get the value where I pass.
here is I save the data.
-(IBAction)Save:(id)sender{
timedata = [datePicker date];
NSLog(#"timedata save is = %#",timedata);
time = [NSString stringWithFormat:#"%#",timedata];
NSLog(#"String time = %#",time);
[self dismissModalViewControllerAnimated:YES];
}
here is I want to show the save data.
- (void) viewWillAppear:(BOOL)animated {
show = [[SelectDate alloc]initWithNibName:#"SelectDate" bundle:nil];
show.time = time;
NSLog(#"time = %#",time);
Selectime.text = show.time;
NSLog(#"show.time = %#",show.time);
[super viewWillAppear:animated];
}
If you have set property for time in SelectDate viewController so that it can be accessed in other viewControllers.
//SelectDate.h
NSString *time;
// Your declarations
#property(nonatomic,retain) NSString *time;
//SelectDate.m
#synthesize time;
Now you can use time in other ViewControllers like you are doing.
To get a NSString representation of a date you should look at NSDateFormatter
example:
NSDate* timedata = [datePicker date];
NSDateFormatter* dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd/MM/yyyy"];
NSString *time = [dateFormat timedata];
[dateFormat release];
Instead of accessing last view, pass your data to your next view:
Hi,My problem is how to pass string to another view label? I got try so many example but still can not get the value where I pass.
here is I save the data.
-(IBAction)Save:(id)sender{
timedata = [datePicker date];
NSLog(#"timedata save is = %#",timedata);
time = [NSString stringWithFormat:#"%#",timedata];
NSLog(#"String time = %#",time);
[self dismissModalViewControllerAnimated:YES];
YourNextView *yourNextView=[[YourNextView alloc] init];
yourNextView.anString=[NSString stringWithFormat:#"%#",timedata];
[yourNextView release];
}
here is You want to show the save data.
- (void) viewWillAppear:(BOOL)animated {
//show = [[SelectDate alloc]initWithNibName:#"SelectDate" bundle:nil];
//show.time = time;
//NSLog(#"time = %#",time);
Selectime.text = self.anString;
NSLog(#"show.time = %#",show.time);
[super viewWillAppear:animated];
}

iphone uitableview grouped by date implementation

So currently I have a table that displays messages, but I'd like to group the messages by date, e.g.
Tuesday 4/5/98 //header
message 1
message 2
message 3
Wednesday 4/6/98
message 1
etc.
So right now, it's just one long NSMutableArray oldArr (sorted).
What I was thinking of doing, was creating another NSMutableArray (groupArr) of unique date objects (DateGroup), in which each DateGroup would have the following ivars:
int size; //number of messages for date
int index; //index of first message in the total array
//so I can easily retrieve the object when the section and row is asked
NSDate date; //need the date for the header
With these ivars, I can get all the sections with groupArr size, all the individual row sizes by accessing the DateGroup size, and the individual cell when given a section, row arguments by getting the index + row.
I think this is the best way to do it. However, I am having problems populating the groupArr from the oldArr (which will dynamically increase in size). I was thinking of going one by one through the oldArr with this psuedocode:
NSDate date = nil;
int size = 1;
int index = 0;
for (int i = 0; i < oldArr.size; i++) {
OldGroup* cur = [oldArr objectAt:i];
if (date is different from cur->date){ //i know, it's pseudocode
DateGroup* newGroup = [[DateGroup alloc] initWithDate:cur->date index:index];
[groupArr add:NewGroup];
date = cur->date;
index += size;
size = 1; <br/>
} else{ //the date is the same, so the object belongs in the group
[groupArr lastObject].size++;
}
}
Anyway, while I think this will work, it seems very unelegant to me. I was thinking about using the "indexOfObjectPassingTest" of NSMutableArry to find the next date, but can't seem to implement it conceptually. I'm trying to design a good way to do this. Any suggestions?
I haven't even compiled this so beware of errors and leaks, and it is perhaps not the most efficient way. But I think you can use the NSDateFormatter to chop the time component that you don't care about when sorting dates into groups. The result is an array of groups with each group being an array of your events, sorted into dates. Then you just need a sort predicate to arrange it in the order you choose to display it and it's ready for the UITableView.
- (void)addDatedEvent:(MyEventClass*)newEvent ToGroup:(NSMutableArray*)dateGroups
{
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
NSDate* eventDate = newEvent.evDate;
NSString* dateString = [dateFormatter stringFromDate:eventDate];
BOOL added = NO;
for(NSMutableArray* group in dateGroups)
{
MyEventlass* firstEvent = [group objectAtIndex:0];
NSDate* firstEventDate = firstEvent.evDate;
NSString* firstEventDateString = [dateFormatter stringFromDate:firstEventDate];
if([firstEventDateString isEqualToString:dateString])
{
// match - this event joins others in an existing group
[group addObject:newEvent];
added = YES;
}
}
if(added == NO)
{
// need to create a new group since this is the first date
NSMutableArray* newGroupArray = [NSMutableArray arrayWithObject:newEvent];
[dateGroups addObject:newGroupArray];
}
}

NSDate timeInterval = nil

I am trying to compare two NSDates one is created by the viewDidLoad method and the other by clicking a button. I want to be able to click the button and find the time difference since the viewDidLoad was ran. I keep getting a difference of nil. Any Ideas?
#import "TimeViewController.h"
id startTime;
#implementation TimeViewController
- (void)viewDidLoad {
NSDate *startTime = [NSDate date];
NSLog(#"startTime = %#",startTime);
}
- (IBAction)buttonPressed{
NSDate *now = [NSDate date];
NSLog(#"now = %#",now);
double timeInterval = [now timeIntervalSinceDate:startTime];
NSLog(#"time difference = %#",[NSString stringWithFormat:#"%g",timeInterval]);
}
You have
id startTime;
in the global scope, and also
NSDate *startTime = [NSDate date];
inside viewDidLoad. The second statement creates a local variable called startTime, which hides the global variable. Use
startTime=[[NSDate date] retain];
instead.
That said, I'd suggest you not to create the global variable. Instead, make it an instance variable and a property:
#interface TimeViewController :NSObject{
....
NSDate*startDate;
}
...
#end
and as Kubi said, don't forget
-(void)dealloc{
[startDate release];
[super dealloc];
}
I'd also suggest not to use id to hold a known object. Who told you that? That's a very bad practice. Even when you declare a global variable, you should use
NSDate*startDate;
so that the compiler can warn you against non-defined methods.

Converting NSString to Currency - The Complete Story

After over a day of poking around with this problem I will see if I can get some help. This question has been more or less asked before, but it seems no one is giving a full answer so hopefully we can get it now.
Using a UILabel and a UITextView (w/ number keyboard) I want to achieve an ATM like behavior of letting the users just type the numbers and it is formatted as currency in the label. The idea is basically outlined here:
What is the best way to enter numeric values with decimal points?
The only issue is that it never explicitly says how we can go from having an integer like 123 in the textfield and displaying in the label as $1.23 or 123¥ etc. Anyone have code that does this?
I have found a solution, and as per the purpose of this question I am going to provide a complete answer for those who have this problem in the future. First I created a new Helper Class called NumberFormatting and created two methods.
//
// NumberFormatting.h
// Created by Noah Hendrix on 12/26/09.
//
#import <Foundation/Foundation.h>
#interface NumberFormatting : NSObject {
}
-(NSString *)stringToCurrency:(NSString *)aString;
-(NSString *)decimalToIntString:(NSDecimalNumber *)aDecimal;
#end
and here is the implementation file:
//
// NumberFormatting.m
// Created by Noah Hendrix on 12/26/09.
//
#import "NumberFormatting.h"
#implementation NumberFormatting
-(NSString *)stringToCurrency:(NSString *)aString {
NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setGeneratesDecimalNumbers:YES];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
if ([aString length] == 0)
aString = #"0";
//convert the integer value of the price to a decimal number i.e. 123 = 1.23
//[currencyFormatter maximumFractionDigits] gives number of decimal places we need to have
//multiply by -1 so the decimal moves inward
//we are only dealing with positive values so the number is not negative
NSDecimalNumber *value = [NSDecimalNumber decimalNumberWithMantissa:[aString integerValue]
exponent:(-1 * [currencyFormatter maximumFractionDigits])
isNegative:NO];
return [currencyFormatter stringFromNumber:value];
}
-(NSString *)decimalToIntString:(NSDecimalNumber *)aDecimal {
NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setGeneratesDecimalNumbers:YES];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
if (aDecimal == nil)
aDecimal = [NSDecimalNumber zero];
NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithMantissa:[aDecimal integerValue]
exponent:([currencyFormatter maximumFractionDigits])
isNegative:NO];
return [price stringValue];
}
#end
The first method, stringToCurrency, will take an integer number (passed in from a textfield in this case) and convert it to a decimal value using moving the decimal point as appropriate for the users locale settings. It then returns a string representation formatted as currency using NSNumberFormatter.
The second method does the reverse it takes a value like 1.23 and converts it back to 123 using a similar method.
Here is an example of how I used it
...
self.accountBalanceCell.textField.text = [[NumberFormatting alloc] decimalToIntString:account.accountBalance];
...
[self.accountBalanceCell.textField addTarget:self
action:#selector(updateBalance:)
forControlEvents:UIControlEventEditingChanged];
Here we set the value of the text field to the decimal value from the data store and then we set a observer to watch for changes to the text field and run the method updateBalance
- (void)updateBalance:(id)sender {
UILabel *balanceLabel = (UILabel *)[accountBalanceCell.contentView viewWithTag:1000];
NSString *value = ((UITextField *)sender).text;
balanceLabel.text = [[NumberFormatting alloc] stringToCurrency:value];
}
Which simply takes the textfield value and run it through the stringToCurrency method described above.
To me this seems hackish so please take the a moment to look over and clean it up if you are interested in using it. Also I notice for large values it breaks.
Take a look at NSNumberFormatter, which will format numerical data based on the current or specified locale.
Since I still didn't see correct Answers to this question I will share my solution without using the NSScanner (the scanner doesn't seem to work for me). Is's a combination out of this " What is the best way to enter numeric values with decimal points? " and this " Remove all but numbers from NSString " answers.
First I present a NSString with the users local currency settings in a UITextField like this:
//currencyFormatter is of type NSNumberFormatter
if (currencyFormatter == nil) {
currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setLocale:[NSLocale currentLocale]];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
//[currencyFormatter setGeneratesDecimalNumbers:YES];
decimalSeperator = [currencyFormatter decimalSeparator]; //NSString
currencyScale = [currencyFormatter maximumFractionDigits]; //short
//[currencyFormatter release]; don't forget to release the Formatter at one point
}
//costField is of type UITextField
NSDecimalNumber *nullValue = [NSDecimalNumber decimalNumberWithMantissa:0 exponent:currencyScale isNegative:NO];
[costField setText:[currencyFormatter stringFromNumber:nullValue]];
You might do this in the viewControllers method viewDidLoad:.
Depending on the users settings there will be displayed a string like this: $0.00 (for local settings United Stated). Depending on your situation here you might want to present a value out of your data model.
When the user touches inside the text field I will present a Keyboard with type:
costField.keyboardType = UIKeyboardTypeDecimalPad;
This prevents the user to enter anything else but digits.
In the following UITextField's delegate method I separate the string to get only the numbers (here I avoid using the NSScanner). This is possible, because I know where to set the decimal separator by using the before specified 'currencyScale' value:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
if (textField == costField) {
//if for what ever reason ther currency scale is not available set it to 2
//which is the most common scale value
if (!currencyScale) {
currencyScale = 2;
}
// separate string from all but numbers
// https://stackoverflow.com/questions/1129521/remove-all-but-numbers-from-nsstring/1163595#1163595
NSString *aString = [textField text];
NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];
for (int i=0; i<[aString length]; i++) {
if (isdigit([aString characterAtIndex:i])) {
[strippedString appendFormat:#"%c",[aString characterAtIndex:i]];
}
}
//add the newly entered character as a number
// https://stackoverflow.com/questions/276382/what-is-the-best-way-to-enter-numeric-values-with-decimal-points/2636699#2636699
double cents = [strippedString doubleValue];
NSLog(#"Cents:%f ",[strippedString doubleValue]);
if ([string length]) {
for (size_t i = 0; i < [string length]; i++) {
unichar c = [string characterAtIndex:i];
if (isnumber(c)) {
cents *= 10; //multiply by 10 to add a 0 at the end
cents += c - '0'; // makes a number out of the charactor and replace the 0 (see ASCII Table)
}
}
}
else {
// back Space if the user delete a number
cents = floor(cents / 10);
}
//like this you could save the value as a NSDecimalNumber in your data model
//costPerHour is of type NSDecimalNumber
self.costPerHour = [NSDecimalNumber decimalNumberWithMantissa:cents exponent:-currencyScale isNegative:NO];
//creat the string with the currency symbol and the currency separator
[textField setText:[currencyFormatter stringFromNumber:costPerHour]];
return NO;
}
return YES;
}
In this way the currency entered by the user will always be correct and there is no need to check it. No matter which currency settings is selected, this will always result to be the correctly formatted currency.
I didn't really like the existing answers here so I combined a couple of techniques. I used a hidden UITextField with the number pad keyboard for input, and a visible UILabel for formatting.
I've got to properties that hold on to everything:
#property (weak, nonatomic) IBOutlet UILabel *amountLabel;
#property (weak, nonatomic) IBOutlet UITextField *amountText;
#property (retain, nonatomic) NSDecimalNumber *amount;
I've got the amount and a NSNumberFormatter as ivars:
NSDecimalNumber *amount_;
NSNumberFormatter *formatter;
I setup my formatter at init:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// Custom initialization
formatter = [NSNumberFormatter new];
formatter.numberStyle = NSNumberFormatterCurrencyStyle;
}
return self;
}
Here's the code I'm using to validate the input convert it to
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *asText = [textField.text stringByReplacingCharactersInRange:range withString:string];
if ([asText length] == 0) {
[self setAmount:[NSDecimalNumber zero]];
return YES;
}
// We just want digits so cast the string to an integer then compare it
// to itself. If it's unchanged then it's workable.
NSInteger asInteger = [asText integerValue];
NSNumber *asNumber = [NSNumber numberWithInteger:asInteger];
if ([[asNumber stringValue] isEqualToString:asText]) {
// Convert it to a decimal and shift it over by the fractional part.
NSDecimalNumber *newAmount = [NSDecimalNumber decimalNumberWithDecimal:[asNumber decimalValue]];
[self setAmount:[newAmount decimalNumberByMultiplyingByPowerOf10:-formatter.maximumFractionDigits]];
return YES;
}
return NO;
}
I've got this setter than handles formatting the label and enabling the done button:
-(void)setAmount:(NSDecimalNumber *)amount
{
amount_ = amount;
amountLabel.text = [formatter stringFromNumber:amount];
self.navigationItem.rightBarButtonItem.enabled = [self isValid];
}