Check if NSString/NSMutableArray already exists in a NSUserDefault? - iphone

I'm making this app with a tableview and stuff. I want to make a "Favorites"-tab, and in the detail-view I want to make a star-like button. I'm done with the "add to favorites" stuff and want to make it impossible to add a string to my favoriteViewController more than once.
Basically, I want to check if my NSUserDefaults contains a certain string in its NSMutableArray that matches the indexPath.row in the table view. It's really hard to explain...
Here's a snippet out of the code. I want to check if the "indexPathRowString" already exists in favoriteKey, before adding it again.
-(IBAction)addToFavorite:(id)sender {
NSMutableArray* alreadyFavourites = [[[NSUserDefaults standardUserDefaults] objectForKey:#"favoriteKey"] mutableCopy];
[favoritedAlready addObject:indexPathRowString];
[[NSUserDefaults standardUserDefaults] setObject:alreadyFavourites forKey:#"favoriteKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I'm very thankful for answers, I really need to fix this! :)

You want NSArray's containsObject:.
Because NSString implements the NSObject Protocol method isEqual: to return YES for two strings of the same content, this method will work correctly for you with two different string objects of the same value.

you can check using
if (![favoritedAlready containsObject:indexPathRowString])
{
[favoritedAlready addObject:indexPathRowString];
}

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.

objective-c - using a boolean value from one class in another class

I've looked around and couldn't find a distinct answer to this question. So I'm asking here. I have two classes. ClassA and ClassB. I have a bool value in ClassB. I have a method in ClassA where it is looking for that value in ClassB to be True in order for the method to fire off. I'm not really sure how to get ClassA to see that value. Any help would be great. Thanks in advance!
be careful with the "global definition".
if your class must save the user settings, you can use:
for save:
NSUserDefaults *pref = [NSUserDefaults standardUserDefaults];
[pref setBool:YES forKey:#"AudioIsON"];
[pref synchronize];
for reading:
BOOL myBooleanSetting = [[NSUserDefaults standardUserDefaults] boolForKey:#"AudioIsON"];
instead of, is better to learn the delegate and the property.
hope this help you.

Implement App Instructions on First Start Up

I have a couple of images that I would like to display the first time a user runs the app to show the user how to effectively use the app. What is the best method to do this sort of thing?
Thanks
The easiest way is to set a flag that says you have shown the app instructions. You can store them in user defaults.
So you would put something like this in your app delegate.
static NSString* const kAppHasShownStartupScreen = #"kAppHasShownStartupScreen";
BOOL hasShownStartup = [[NSUserDefaults standardUserDefaults] boolForKey:kAppHasShownStartupScreen];
if(hasShownStartup)
{
window.rootViewController = //your normal startup view controller
}
else
{
window.rootViewController = //your new view controller with instructions
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAppHasShownStartupScreen];
}
Create some boolean values to store in NSUserDefaults or in Core Data that represent whether or not the user has viewed the 'tutorial'. Show the images by loading them into UIImageView's and adding them as subviews if the flag is false. Set the flag to true after they have viewed the images.
You can accomplish this functionality by using a BOOL value in NSUserDefaults:
#define appdb ((NSUserDefaults *)[NSUserDefaults standardUserDefaults])
if(![appdb boolForKey:#"applicationHasRunBefore"]) {
[appdb setBool:YES forKey:#"applicationHasRunBefore"];
[appdb synchronize];
...
// Do the tutorial
}

How to convert NSNumbers into integers from an NSArray(iPhone)

I have a a game for the iphone where the tags of 32 buttons are vital. I am trying to make a feature where it would save these 32 integers into an array so you can play a different game on the board with the same integers(tags) and then later come back to your game with the integers in the same way.(Not asking about multitasking or anything like that) There are some questions like this but none of their answers seem to work.
Right now I have:
savedGame = [NSArray arrayWithObjects: [NSNumber numberWithInt:a2.tag],
[NSNumber numberWithInt:a4.tag],
[NSNumber numberWithInt:a6.tag],
[NSNumber numberWithInt:a8.tag],
[NSNumber numberWithInt:b1.tag],
[NSNumber numberWithInt:b3.tag],
[NSNumber numberWithInt:b5.tag],
[NSNumber numberWithInt:b7.tag],
[NSNumber numberWithInt:c2.tag],
[NSNumber numberWithInt:c4.tag],
all the way to:
[NSNumber numberWithInt:h7.tag],nil];
for the part where you save the game
and:
a2.tag = [((NSNumber*)[savedGame objectAtIndex:0]) intValue];
all the way up to:
h7.tag = [((NSNumber*)[savedGame objectAtIndex:32]) intValue];
at the part where you resume your game. However, this code is apparently SO BAD that whenever it gets to this code in the ios simulator it crashes XCODE TOO(which gets very annoying because I need to force quit and start the program again)(its xcode 4)
Saved game is created in the .h like:
NSArray *savedGame;
I guess I will have to add something where it checks if there are 32 numbers in that second part--so if you could add that in your answer that would be great--and thanks in advance! I think the problem is with the second part of that code-- the
g2.tag = [((NSNumber*)[savedGame objectAtIndex:25]) intValue];
I have also heard of NS Mutable Arrays, so maybe that could solve my problem?
Thanks for any and all help!! And I am also open to new ways--maybe not arrays--that could solve this problem!! Thankyou in advance!!!
Use an NSMutableArray instead and you can do things like
[savedGame replaceObjectAtIndex:4 withObject:[NSNumber numberWithInt:a8.tag]];
You can also save these in NSUserDefaults:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setInteger:a4.tag forKey#"a4.tag"];
a4.tag = [prefs integerForKey:#"a4.tag"];
This latter approach will also allow you to use a for loop to set and retrieve these values.
You should really use an NSDictionary for this job.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html
This will allow you to query your data using keys.
If you are suspecting that you could be accessing your array beyond its size, then you could check if the array has got the number of objects you expect by inspecting [savedGames count].
Don't know if it helps.
As a general suggestion, I would first run your unmodified program in debugging mode (if you are not doing that already), and possibly step-by-step in the region where it fails. So you will know exactly which is the offending statement.
In order to further try and understand what is happening, I would split your assignments in multiple lines:
NSNumber* number = [savedGame objectAtIndex:0];
a2.tag = [number intValue];
if you set a breakpoint on the first statement you can see what objectAtIndex is returning.
Of course I understand it is big work to make all those changes.
As an alternative approach, since it seems to me that what matters to you are the ints (not the NSNumbers, you are using those only to store the ints somewhere), you could simply resort to using a C array of longs. This would avoid all the conversions back and forth. Of course, I am not sure whether you are really only interested in the ints.
Ummmm, based on the way your tags are situated, i guess its some kind of checkers like game? As in you have a 64 square grid but only half of the spaces are playable.
As for your comment about a mutable array, a mutable array just allows you to dynamically add or remove objects to the array. A regular NSArray has a fixed size and components (you cant add to it or remove from it). So you dont need to do the initWithObjects: part with a mutable array.
But, I really think you should restructure the way you are doing this. In this situation, you would want an object that has a location. Then you can just do
#interface GamePiece : NSObject
{
CGPoint location; //an object with an x and y
}
#property (assign, readwrite) CGPoint location;
-(void)saveTheGame {
[self setSavedGame:[[[NSMutableArray alloc] init] autorelease]];
for(GamePiece *piece in allPieces)
{
[savedGame addObject:piece]
}
}
Then in loadGame
if([savedGame count] == 32)
{
[self setAllPieces:nil]; //allPieces is an array object of a "Game" that contains
//references to all the pieces on the board
for(GamePiece *piece in savedGame)
{
[allPieces addObject:piece];
}
}

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.