NSUserDefaults: Saved Number Always 0, iPhone - iphone

I have looked at other answers and the docs. Maybe I am missing something, or maybe I have another issue. I am trying to save a number on exiting the app, and then when the app is loaded I want to check if this value exists and take action accordingly. This is what I have tried:
To save on exiting:
- (void)applicationWillTerminate: (UIApplication *) application
{
double save = [label.text doubleValue]; // This could be the issue
//double save = 3.5; // This works, it saves the value and loads it fine, so that is not the problem here.
[[NSUserDefaults standardUserDefaults] setDouble: save forKey: #"savedNumber"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
To check:
- (IBAction)buttonclickSkip{
double save = [[NSUserDefaults standardUserDefaults] doubleForKey: #"savedNumber"];
if (save == 0) {
[self performSelector:#selector(displayAlert) withObject:nil];
test.enabled = YES;
test.alpha = 1.0;
skip.enabled = NO;
skip.alpha = 0.0;
}
else {
label.text = [NSString stringWithFormat:#"%.1f %%", save];
}
}
The problem is I always get my alert message displayed, the saved value is not put into the label so somehow == 0 is always true. Why would:
double save = [label.text doubleValue];
always equal zero? Before I close the app the number in that label is roughly 0.5% (it varies). If it makes any difference I am testing this on the iPhone simulator.
Many thanks,
Stu

The fact that you can hard-code the value and fetch it back means the problem definitely revolves around your interaction with the label.text and not your use of NSUserDefaults.
Make sure that the label has not already been destroyed at the time you go to fetch its value. As the application is terminating it may have already brought down the view from which you are fetching the value.
Another thing to try would be to get the actual text itself instead of asking the OS to convert the text value into a number first. If you print that out you may get some clue as to what is going on.

Make sure that your applicationWillTerminate: implementation is in your app delegate class.

My guess would be that the text in your label is not a valid double value. From the Apple docs for NSString -doubleValue:
Returns 0.0 if the receiver doesn’t
begin with a valid text representation
of a floating-point number.
Make sure you are passing it something like #"13.2". The best way to check this is to stick a NSLog call right after you create the variable save.

Related

How to save the subView that has been added with NSUserDefaults?

I'm working in a book app, and I want to use a page marker to help the user remember where he stopped reading. So I have added a bar button with an image ("mark.png"). A mark view will be added to the chapter view when it is tapped, and if it is been tapped again the mark will be removed from the superView. I'm using this code:
- (void)showMark {
if (![markView superView]) {
[chapterOne addSubView:markView];
}
else {
[markView removeFromSuperView];
}
}
It is working fine but ever time I exit the app and rerun again the mark view is gone, so how can I keep it?
I found some forums talking about the NSUserDefaults to save actions but I really don't know how to use it with my code. Any help will be appreciated.
You can't use NSUserDefaults to save entire views, but you can save the parmeters that would help determine where the bookmark should be set.
For example if you are basing the book mark by a page number you could save the page to the NSUserDefaults when the user leaves the view controller.
Example:
[[NSUserDefaults standardUserDefaults] setInteger:23 forKey:#"bookMarkPage"];
[[NSUserDefaults standardUserDefaults] synchronize];
When the user comes back the the view controller you can check if there is a bookmark:
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"bookMarkPage"] != nil) {
int pageNumber = [[NSUserDefaults standardUserDefaults] objectForKey:#"bookMarkPage"];
[self setBookmarkForPage:pageNumber];
}
Possible bookmark construction method:
- (void) setBookmarkForPage:(Int)pageNumber {
// run through the logic of placing the bookmark on the correct page
}
You can use whatever parameters you need to determine where to place the book mark. When a user originally places the bookmark what parameters you use to figure out where to place the bookmark? Try to use the same logic for when a user first places the bookmark.
I don't know exactly what you want to save, but you can just about any kind of data with NSUserDefaults, like this:
[[NSUserDefaults standardUserDefaults] setInteger:123 forKey:#"CurrentPageNumber"];
When you have set all the values you need, save them:
[[NSUserDefaults standardUserDefaults] synchronize];
Then when app opens, check to see if the value is set. If it is draw your marker.
if ([defaults valueForKey:#"CurrentPageNumber"] != nil) {
int pageNumber = [defaults valueForKey:#"CurrentPageNumber"]
if (pageNumber == 1) {
[chapterOne addSubView:markView];
}
else {
[markView removeFromSuperView];
}
}
The other answers state great ways to work around the issue. Just to clarify, UIView or any of the derivatives are not supported for NSUserDefaults. NSUserDefaults allows just primitive object types (NSString, NSNumber, NSArray, and NSDictionary). There might be one or two I missed. But UIView or UIViewController object types can't be saved in NSUserDefaults.

UIProgressbar update method

Hello i'm trying to write a method that update a UIProgressbar !
The problem is that when both of values arrive fine to the method (NSLog display values)
the division operation generate a bad_access when i run the application !!
I tried many messages from both value like intValue/inValue ...
Help me to solve this issue and how can I print values of NSNumber
-(void)UpdateProgressbar:(NSNumber *)currentOperationNumer TotalOperationNumber:(NSNumber*)n
{
NSLog(#" operation : %i",currentOperationNumer);
NSLog(#" total : %i",n);
if (currentOperationNumer<=n)
{
[downloadBar setProgress:(currentOperationNumer/n )];
NSLog(#"progress !");
}
else
{
[self.view removeFromSuperview];
}
}
try [currentOperationNumber intValue] in place of currentOperationNumber (or floatValue if setProgress expects a float). So....
int myProgress = [currentOperationNumber intValue] / [n intValue];
[downloadBar setProgress:myProgress];
Actually, isn't it a float value it expects in there?
NSNumber is an object wrapper for various primitive types, with what you are doing you are trying to divide a pointer by a pointer..
maybe try chaining the code to..
[downloadBar setProgress:([currentOperationNumer intValue] / [n intValue])];
You can print value by using following code
-(void)UpdateProgressbar:(NSNumber *)currentOperationNumer TotalOperationNumber:(NSNumber*)n
{
NSLog(#" operation : %#",currentOperationNumer);
NSLog(#" total : %#",n);
if (currentOperationNumer<=n)
{
[downloadBar setProgress:(currentOperationNumer/n )];
NSLog(#"progress !");
}
else
{
[self.view removeFromSuperview];
}
}
The best way to debug EXC_BAD_ACCESS:
If you are not using XCode 4, then upgrade.
In XCode 4, press CMD-I to run Instruments.
Select the Zombies profile. (Background: EXC_BAD_ACCESS means you are sending a message to a deallocated object. Zombies turns on the zombie feature, so that objects with a retain count of 0 are not deallocated, but kept as a zombie. The NSZombie class raises an exception whenever a message is sent to it - thus it can be trapped, and identified the point of origin.)
Run your app via Instruments. When it crashes, Instruments will be brought to the foreground with a pop-up callout that indicates the memory that was freed but accessed.
You can click the little arrow in the lower right corner, this will give you that memory's alloc/retain/release history, and the code that performs the action. Click on the source column, it will take you to that line in the code.
make it sure downloadBar.progress is a float and it's from 0.0 to 1.0 inclusive.
Please, try this:
float current = 0.0f;
float count = 100.0f;
while (stuff..) {
downloadBar.progress = current / count;
}
it should work without problems.

I'm having issues inputing a name if applicable

I have a function here that upon completing a single round, if your score is higher than either a default score entry or a newly placed high score then it will swap its data with your data and push everything else down. removing the last entry from the list. currently this is just one exchange and for functions sake I'm going to hard code it and then refactor it later.
My main problem is that when I set up a text input view to capture the players name execution continues immediately without the players input and crashes the game. I commented out the line that sets the text because I have a default value in place just in case any attempt that I try to make fails. How can I get Execution to wait for a moment while input is taken? Would I have to set up a delegate method? If so I'm still a bit confused by delegates. I could set it up to work but I don't understand it, so I wouldn't be able to do any other special custom tasks with it. I've worked on it for a while and got no further...
-(void)saveData:(ScoreKeep *)stats{
NSMutableDictionary *swap = [[NSMutableDictionary alloc]init];//used for swaping entries
NSString *filePath = [self pathOfFile];
NSLog(#"Writing to %#", filePath);
if ([[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
NSLog(#"Loading previous dictionary to save...");
dataDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
if ([dataDictionary objectForKey:#"1"]) {
NSMutableDictionary *highScore = [dataDictionary objectForKey:#"1"];
if ([stats.score intValue] > [[highScore objectForKey:#"SCORE"] intValue]) {
NSLog(#"You Win! score: %# highscore: %#", stats.score,[NSNumber numberWithInt:[[highScore objectForKey:#"SCORE"] intValue]] );
stats = [[ScoreKeep alloc] initWithNibName:#"Scorekeep" bundle:nil];
NSLog(#"Setting up name entry");
[self.view addSubview:stats.view]; //New view is added so that the player can input data(Assume it is complete);
//stats.nameTag = setName.nameTag;//This line is executed before the new view is dismissed causing an error to occur
[stats setupDictionary]; // It just goes down hill from here if the previous line is uncommented
[dataDictionary setObject:stats.sComponents forKey:#"1"];
}else {
NSLog(#"You Lose: %# highscore: %#", stats.score,[NSNumber numberWithInt:[[highScore objectForKey:#"SCORE"] intValue]] );
}
NSLog(#"Got first place entry");
}else {
NSLog(#"Initilizing Score");
}
}else{
NSLog(#"Creating new dictionary to save...");
dataDictionary = [[NSMutableDictionary alloc]init];
}
[dataDictionary writeToFile:filePath atomically:YES];
}
Help would greatly be appreciated. If more information is needed I'd be happy to provide.
by the way ScoreKeep is an object that contains a dictionary and a function to create a dictionary such that it can set any values I need and package them into sComponents(the dictionary to be entered into the main savefile)
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#class omphalosUtility;
#pragma mark -
#pragma mark Saving data
#pragma mark -
static inline void poop(){
NSLog(#"POOP");
}
I'm going to try making a utility file that works independently of the app so that I Can update files and perform other universal operations such as saving when needed. Its a step in a direction that i'd like to take.
If i get it right, (The code is really nasty, man...) your problem is that you are trying to present a View Controller with the wrong way.
Correct me if i'm wrong, is ScoreKeep is a ViewController? if so, you have to name it properly. that's for a start.
Second, you cant present another view controller only by adding its "view" property to the current view controller's View Hierarchy. that way the view will not respond properly to the events.
the correct way to to what you'r trying to do is by presenting the ScoreKeep ViewController modally.
there is no other right way to do this without using delegation. you will have to acquire this technique.
Your view controller that responsible for getting the name from the user need to have a way to tell it's master view controller that the user entered a name. and that is achieved through delegation.
What you should do:
Basically you create a protocol called something like "NamePrompterViewControllerDelegate"
that will have at least one method that will be called when the user will done entering his name.
Your ScoreKeepViewController should have an instance variable that implemented the protocol (Look at the apple documentation on protocols for assistance)
Your main view controller (the one that contains the method you added) then should implement the protocol you created, and set itself as the delegate of ScoreKeep like that:
stats = [[ScoreKeep alloc] initWithNibName:#"Scorekeep" bundle:nil];
stats.delegate = self;
For more info on presenting and dismissing ViewControllers modally you should read the documentation at Apple Documentation
I hope i helped you, there is just a lot to cover and it hardly can be done by writing an answer.
Feel free to ask more for clearance.

UISlider how to set the initial value

I'm pretty new at this iphone dev stuff and i'm working on some existing code at a company. The uislider i'm trying to set the initial value on is actually in a UITableViewCell and is a custom control. I was thinking in the cell init
cell = (QuantitiesCell *)[self loadCellWithNibName:#"QuantitiesCell" forTableView:ltableView withStyle:UITableViewCellStyleDefault];
i could just call something like
((QuantitiesCell *)cell).value = 5;
The actual QuantitiesCell class has the member value and the following functions
-(void)initCell;{
if (listOfValues == nil) {
[self initListOfValues];
}
quantitiesSLider.maximumValue = [listOfValues count]-1;
quantitiesSLider.minimumValue = 0;
quantitiesSLider.value = self.value;
}
-(void)initListOfValues;{
listOfValues = [[NSMutableArray alloc] initWithCapacity:10];
int j =0;
for (float i = minValue; i <= maxValue+increment; i=i+increment) {
[listOfValues addObject:[NSNumber numberWithFloat: i]];
if (i == value) {
quantitiesSLider.value = j;
}
j++;
}
}
like i said, i'm pretty new at this so if i need to post more of the code to show whats going to get help, let me know,
This slider always is defaulting to 1, the slider ranges usually from 1-10, and i want to set it to a specific value of the item i'm trying to edit.
Setting slider.value is the correct way if your linkage is correct (you have made the connections in Interface Builder). If you have created a UISlider in code, all you have to do is set the value property. If that is not working then be sure firstly that the object is "live" (correctly allocated, not released, not out of scope etc.) and secondly that the functions in which you set slider.value are actually being called.
If you are using Interface Builder and are not sure of how to connect your slider as an IBOutlet, you should check out iTunes University - search for "CS193P" to find some excellent videos from Stanford University. The first couple will take you through making those connections. If using IB and you have not made the connection - nothing will happen.
PS I had the same issuer - you need to add the UISlide view first then you can change the value.

iPhone inputting NSUserDefaults into a UITextField

I am writing a program where I where the first time a user runs it - they will have to fill out about 10 different UITextFields. I am trying to save the fields so that on subsequent runs of the program that whatever they previously put will already be displayed in those UITextFields so the wont have to re-input it in - unless they want to edit something in which case they still have that option. I think that I have figured out a good way to save the strings using NSUserDefaults but now I am trying to figure out how to have those fields populate a UITextField - it doesnt seem as easy as if they were UILabels. This is the route I am attempting:
// in the viewDidLoad portion.
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults]; //Hooks.
NSString *placeHolderName = [userData stringForKey:#"name"];
txtName.text = #"%#", placeHolderName;
When I do this, it simply displays the '%#' in the textfields. I want whatever variable being held by placeHolderName to be automatically put into that UITextField. Is this possible?
Just use:
txtName.text = placeHolderName;
or
txtName.text = [NSString stringWithFormat:#"%#", placeHolderName];
In code you have now you accidentally use Comma operator that evaluates its first expression (txtName.text = #"%#"), discards the result and returns the 2nd (placeHolderName)
If I try to set a text of a UITextField in viewDidLoad, the UITextField is always nil, thus nothing happens. When should the
txtName.text = placeHolderName
be called, if I need to populate the text field with the default value when the UIViewController loads?