"not solved yet..."
I'm having an issue with my iap consumable packages. The thing is that when I buy one, it duplicates the purchase, making another one without requesting it. The alert View appears double also.
A curious thing: this errors only happens when I make an ipa of my app, and not when debugging it! I can't understand why...
Here is the productPurchase method implemented in my main menu where I put the IAP buttons:
- (void)productPurchased:(NSNotification *)notification {
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
NSString * productIdentifier = notification.object;
NSString* plistPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"XXXXX.plist"];
if(plistPath != nil){
int playerSilverCoins = [defaults integerForKey:#"userSilverCoins"];
int playerGoldCoins = [defaults integerForKey:#"userGoldenCoins"];
NSArray *coinsItems = [NSArray arrayWithContentsOfFile:plistPath];
for (NSDictionary *coinItem in coinsItems) {
if ([[coinItem objectForKey:#"productId"] isEqualToString:productIdentifier]) {
playerSilverCoins = playerSilverCoins + [[coinItem objectForKey:#"silverCoins"] integerValue];
playerGoldCoins = playerGoldCoins + [[coinItem objectForKey:#"goldenCoins"] integerValue];
[defaults setInteger:playerSilverCoins forKey:#"userSilverCoins"];
[defaults setInteger:playerGoldCoins forKey:#"userGoldenCoins"];
[defaults synchronize];
NSLog(#"product bought: %#" , productIdentifier);
NSString *message = nil;
if([[coinItem objectForKey:#"goldenCoins"] integerValue] == 0)
message = [NSString stringWithFormat:#"You have bought %d silver coins", [[coinItem objectForKey:#"silverCoins"] integerValue]];
else
message = [NSString stringWithFormat:#"You have bought %d golden coins and %d silver coins", [[coinItem objectForKey:#"goldenCoins"] integerValue],[[coinItem objectForKey:#"silverCoins"] integerValue]];
UIAlertView * alert = [[UIAlertView alloc]initWithTitle:#"XXXXX" message:message delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
[alert release];
NSNumber* achievementContributor = [defaults objectForKey:#"AchievementContributor"];
if ([achievementContributor intValue] != 1 ) {
[[GameCenterAchievement sharedGameCenterAchievement] checkAchievements:_kContributor];
[defaults setObject:[NSNumber numberWithInt:1] forKey:#"AchievementContributor"];
[[GKAchievementHandler defaultHandler] notifyAchievementTitle:#"Contributor"
andMessage:#"Buy a coin pack"];
}
break;
}
}
}
}
I don't know if there's a problem there, or on my IAP classes where I implement the transactions, restore, etc. If someone needs more code, just ask for it. Thanks!
Make sure you are adding observer only ones.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productPurchased:) name:IAPHelperProductPurchasedNotification object:nil];
Adding listener multiply times will cause a multiple calls of your callback method.
If you are reusing your class - remember to removeObserver after you dealloc it - it won't do it on it's own.
Related
I have a settings view where i have provided the option for users to change their settings.
Settings view have a table view having 2 UISwitches. I need help with NSUserdefaults method for saving states, and how I can write the code to load my values.
Here is my code so far
In `cellForRowAtIndexPath` method:
[cell.switchButton addTarget:self action:#selector(switchButtonTapped:) forControlEvents:UIControlEventValueChanged];
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"myBool"];
BOOL hBool = [[ NSUserDefaults standardUserDefaults] boolForKey:#"myBool"];
NSLog(#"tag %i", tappedSwitch.tag);
if (cell.switchButton.tag == 0) {
[cell.switchButton setOn:hBool];
}
return cell;
}
- (void) switchButtonTapped: (id) sender {
tappedSwitch = (UISwitch *) sender;
switch (tappedSwitch.tag) {
case 0:
passcodeSwitchIsOn = tappedSwitch.isOn;
if (passcodeSwitchIsOn) {
GCPINViewController *PIN = [[GCPINViewController alloc]
initWithNibName:nil
bundle:nil
mode:GCPINViewControllerModeCreate];
PIN.messageText = #"Enter a passcode";
PIN.errorText = #"The passcodes do not match";
PIN.title = #"Set Passcode";
PIN.verifyBlock = ^(NSString *code) {
NSLog(#"setting code: %#", code);
code = saveString;
return YES;
};
[PIN presentFromViewController:self animated:YES];
[PIN release];
// method of saving
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:passcodeSwitchIsOn forKey:#"myBool"];
[defaults synchronize];
}
break;
case 1:
bluetoothSwitchIsOn = tappedSwitch.isOn;
if (bluetoothSwitchIsOn) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Bluetooth Switch" message:#"Bluetooth Switch is ON" delegate:self cancelButtonTitle:#"Okay" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
break;
default:
break;
}
[settingsTable reloadData];
}
Create a reference to your user defaults:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
Set a value:
[defaults setBool:1 forKey:#"bluetooth"];
You can set this back to 0 when bluetooth is off:
[defaults setBool:0 forKey:#"bluetooth"];
The user default is identified by the string of your choice. In this case: #"bluetooth". The value for the default will be nil until you create it and set it to something else.
So you can say:
if (!bluetooth) // bluetooth is off
else // bluetooth is on
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html
This can be used for handling your issue.
IBOutlet UISwitch * swi_yourswitch;
#property(nonatomic,retain) UISwitch * swi_yourswitch;
implimentation
#synthesise swi_yourswitch;
- (void) switchButtonTapped: (id) sender {
UISwitch * switchObj = (UISwitch*)sender;
if(switchObj == self.swi_yourswitch){
if (self.swi_yourswitch.on){
NSLog(#"On");
}
else{
NSLog(#"Off");
[self.swi_yourswitch setOn:0];
}
}
To store value
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"mybool"];
[[NSUserDefaults standardUserDefaults] synchronize];
To get stored value
[NSUserDefaults standardUserDefaults] valueForKey:#"mybool"]
Please can anyone refer me to a tutorial or help me out with this problem. I wish to display high scores and other games statistics at game Over but I don't seem to get a headway despite looking at tutorials and trying stuff out. I don't seem to understand how to use NSUserdefaults to achieve this.
My codes are as follows:
Gameplaylayer.mm
- (id)initWithHUDLayer:(HUDLayer *)hudLayer {
if ((self = [super init]))
{
score = 0;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *defaultArray = [NSDictionary dictionaryWithObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:0],
[NSNumber numberWithInt:0],
[NSNumber numberWithInt:0],
[NSNumber numberWithInt:0],
[NSNumber numberWithInt:0],
nil]
forKey:#"Scores"];
[defaults registerDefaults:defaultArray];
[defaults synchronize];
}
return self;
}
-(void)gameOver:(id)sender{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *HighScores = [NSMutableArray arrayWithArray:[defaults arrayForKey:#"Scores"]];
for (unsigned int i = 0; i < [HighScores count]; i++)
{
if (dist >= [[HighScores objectAtIndex:i] intValue])
{
[HighScores insertObject:[NSNumber numberWithInt:dist] atIndex:i];
[HighScores removeLastObject];
[defaults setObject:HighScores forKey:#"Scores"];
[defaults synchronize];
NSLog(#"Saved new high score of %i", dist);
break;
}
}
}
-(void)update:(ccTime)dt {
CGSize winSize = [CCDirector sharedDirector].winSize;
score += (int)delta;
dist = score - 18;
if (!gameOver )
{
if (!lives)
{
gameOver = true;
[self gameOver];
}
}
}
my game over layer is as follows
-(id)init {
self = [super init];
if (self != nil)
{
self.isTouchEnabled = YES;
CGSize windowSize = [CCDirector sharedDirector].winSize;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *HighScores = [defaults arrayForKey:#"Scores"];
CLabelTTF *title = [CCLabelTTF labelWithString:#"high scores" fontName:#"Courier" fontSize:32.0];
[title setPosition:ccp(screenSize.width / 2, screenSize.height - title.contentSize.height)];
[self addChild:title];
NSMutableString *scoresString = [NSMutableString stringWithString:#""];
for (unsigned int i = 0; i < [HighScores count]; i++)
{
[scoresString appendFormat:#"%i. %i\n", i + 1, [[HighScores objectAtIndex:i] intValue]];
}
CCLOG(#"scores saved %i", dist);
CCLabelTTF *scoresLabel = [CCLabelTTF labelWithString:scoresString dimensions:CGSizeMake(screenSize.width, screenSize.height / 3) alignment:CCTextAlignmentCenter fontName:#"Courier" fontSize:40.0];
[scoresLabel setPosition:ccp(screenSize.width / 2, screenSize.height / 2)];
scoresLabel.color = ccc3(0, 0, 0);
[self addChild:scoresLabel z:9];
}
return self;
}
It's hard to understand your code, please look at this small example below.
Ok, that's saving new score, assuming you have GameController singleton, where scores mutable array is declared and currentScore in that singleton also displays current player's score.
-(void) saveScores {
GameController * controller = [GameController sharedController];
for(int i=0; i<5;i++)
{
if([[[controller.scores objectAtIndex:i] objectForKey:#"score"] intValue] == controller.finalscore)
{
break;
}
if([[[controller.scores objectAtIndex:i] objectForKey:#"score"] intValue] < controller.finalscore) {
for(int j=5;j>i+1;j--)
{
[controller.scores replaceObjectAtIndex:j-1 withObject:[controller.scores objectAtIndex:j-2]];
}
NSMutableDictionary *newEntry = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:controller.finalscore],#"score",#"Local player",#"name", nil];
[controller.scores replaceObjectAtIndex:i withObject:newEntry];
break;
}
}
controller.currentScore=0;
[[NSUserDefaults standardUserDefaults] setObject:controller.scores forKey:#"OurScores"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
You can see, that we are saving updated scores in NSUserDefaults. So, at our game start we shoul load them. I added this code to GameController init method:
- (void) loadScores {
if([[NSUserDefaults standardUserDefaults] objectForKey:#"OurScores"] == nil)
{
NSMutableDictionary *empty = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0],#"score",#"empty",#"name", nil];
for(int i=0;i<5;i++)
[scores addObject:empty];
}
else
{
NSLog(#"Loading scores");
scores = [[NSUserDefaults standardUserDefaults] objectForKey:#"OurScores"];
}
}
And that's how we can show this scores using cocos2d:
-(void) drawScores {
for(int i=0; i<5; i++)
{
CCLabelTTF* score = [CCLabelTTF labelWithString:
[NSString stringWithFormat:#"%#",[[controller.scores objectAtIndex:i] objectForKey:#"name"]] fontName:#"Marker Felt" fontSize:30];
score.anchorPoint=ccp(0,0);
score.position = ccp(115, 180-i*35);
CCLabelTTF* score2 = [CCLabelTTF labelWithString:
[NSString stringWithFormat:#"%d",[[[controller.scores objectAtIndex:i] objectForKey:#"score"] intValue] ] fontName:#"Marker Felt" fontSize:35];
score2.anchorPoint=ccp(0,0);
score2.position = ccp(280, 180-i*35);
score.color=currentClr;
score2.color=currentClr;
[self addChild: score z:3 tag:i];
[self addChild: score2 z:3 tag:i+15];
}
}
Your code has a number of problems, some of which just break with convention but all together they make your program quite unreadable. I have not been able to make sense of it. You are not using comments, nor are you telling us, what exactly is not working.
For instance, you might consider following the convention of having instance variables start with small letters, reserving capitalized names for classes. It is generally a bad idea to give misleading names, such as calling an NSDictinoary "defaultArray". Also, try to use empty lines more sparingly to structure your code for better readability.
That being said, this is how you set a default value:
[[NSUserDefaults standardUserDefaults] setValue:scoreArray
forKey:#"Score"];
I see you know how to retrieve the score.
As for your updating routine, consider that it is normally not a good idea to modify the array your iterating through (in your particular case it could still work, but it is not guaranteed). Also, there is no need to store the whole array in the loop. Just save it once you have contructed the complete new high score array.
Otherwise your code is working, right?
So I have push notifications sending to my application.
The code that triggers the alert is in my app delegate file (I think thats where it is supposed to go?)
How do I make an action for my alert button so that I can change to a different view?
// Set Below code in your App Delegate Class...
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
// Call method to handle received notification
[self apnsPayloadHandling:userInfo];
}
-(void) apnsPayloadHandling:(NSDictionary *)userInfo{
// Example Payload Structure for reference
/*
remote notification: {
acme1 = 11114;
aps = {
alert = {
"action-loc-key" = "ACTION_BUTTON_TITLE";
"loc-args" = ("MSG_TEXT");
"loc-key" = "NOTIFICATION_DETAIL_PAGE";
};
badge = 10;
sound = "chime";
};
}
*/
//======================== Start : Fetching parameters from payload ====================
NSString *action_loc_key;
NSArray *loc_args_array;
int badge;
NSString *sound=#"";
NSArray *payloadAllKeys = [NSArray arrayWithArray:[userInfo allKeys]];
// getting "acme1" parameter value...
if([payloadAllKeys containsObject:#"acme1"]){
acme1 = [userInfo valueForKey:#"acme1"]; // getting "ID value" as "acme1"
}
NSString *localizedAlertMessage=#"";
// getting "aps" parameter value...
if([payloadAllKeys containsObject:#"aps"]){
NSDictionary *apsDict = [NSDictionary dictionaryWithDictionary:[userInfo objectForKey:#"aps"]];
NSArray *apsAllKeys = [NSArray arrayWithArray:[apsDict allKeys]];
if([apsAllKeys containsObject:#"alert"]){
if([[apsDict objectForKey:#"alert"] isKindOfClass:[NSDictionary class]]){
NSDictionary *alertDict = [NSDictionary dictionaryWithDictionary:[apsDict objectForKey:#"alert"]];
NSArray *alertAllKeys = [NSArray arrayWithArray:[alertDict allKeys]];
if([alertAllKeys containsObject:#"action-loc-key"]){
action_loc_key = [alertDict valueForKey:#"action-loc-key"]; // getting "action-loc-key"
}
if([alertAllKeys containsObject:#"loc-args"]){
loc_args_array = [NSArray arrayWithArray:[alertDict objectForKey:#"loc-args"]]; // getting "loc-args" array
}
if([alertAllKeys containsObject:#"loc-key"]){
loc_key = [alertDict valueForKey:#"loc-key"]; // getting "loc-key"
}
if([loc_args_array count] == 1){
localizedAlertMessage = [NSString stringWithFormat:NSLocalizedString(loc_key, nil),[loc_args_array objectAtIndex:0]];
}
else if([loc_args_array count] == 2){
localizedAlertMessage = [NSString stringWithFormat:NSLocalizedString(loc_key, nil),[loc_args_array objectAtIndex:0],[loc_args_array objectAtIndex:1]];
}
else if([loc_args_array count] == 3){
localizedAlertMessage = [NSString stringWithFormat:NSLocalizedString(loc_key, nil),[loc_args_array objectAtIndex:0],[loc_args_array objectAtIndex:1],[loc_args_array objectAtIndex:2]];
}
}
else{
localizedAlertMessage = [apsDict objectForKey:#"alert"];
}
}
if([apsAllKeys containsObject:#"badge"]){
badge = [[apsDict valueForKey:#"badge"] intValue]; // getting "badge" integer value
}
if([apsAllKeys containsObject:#"sound"]){
sound = [apsDict valueForKey:#"sound"]; // getting "sound"
}
}
//======================== Start : Handling notification =====================
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (state == UIApplicationStateActive){ // application is already open
if(apnsAlert){
apnsAlert = nil;
}
if(action_loc_key){ // View Button title dynamic...
apnsAlert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:#"%# %#",[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleDisplayName"],NSLocalizedString(#"NOTIFICATION", nil)] message:localizedAlertMessage delegate:self cancelButtonTitle:NSLocalizedString(#"CANCEL", nil) otherButtonTitles: NSLocalizedString(action_loc_key, nil),nil];
}
else{
apnsAlert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:#"%# %#",[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleDisplayName"],NSLocalizedString(#"NOTIFICATION", nil)] message:localizedAlertMessage delegate:self cancelButtonTitle:NSLocalizedString(#"OK", nil) otherButtonTitles:nil];
}
[apnsAlert show];
}
else{ // application is in background or not running mode
[self apnsViewControllerRedirectingFor_loc_key:loc_key with_acme1:acme1];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
// the user clicked one of the OK/Cancel buttons
if(alertView == apnsAlert){
if (buttonIndex == 1){
[self apnsViewControllerRedirectingFor_loc_key:loc_key with_acme1:acme1];
}
}
}
-(void) apnsViewControllerRedirectingFor_loc_key:(NSString *)loc_key_para with_acme1:(NSString *)acme1_para{
if([loc_key_para isEqualToString:#"NOTIFICATION_DETAIL_PAGE"]){
DetailPageClass *detailPage = [[DetailPageClass alloc] initWithNibName:#"DetailPageClass" bundle:nil];
[self.navigationcontroller pushViewController:detailPage animated:YES]; // use nav controller where you want to push...
}
else if([loc_key_para isEqualToString:#"NOTIFICATION_MAIN_PAGE"]){
}
...
}
To change the title of the button, use the action-loc-key key in the notification dictionary (see this section of the guide).
To do something when the notification is tapped, you can implement a few methods in your app delegate: Handling notifications.
Disclaimer: major noob
I'm writing an arithmetic flash card app as a learning project. I have a UITabViewController with the bottom tab bar that toggles between a few different views. Everything works okay until I try to set NSUserDefault boolean values in the Settings view controller and try to read those values in the Flashcards view controller.
The settings view has a switch to enable/disable each operator (addition, subtraction, etc) and the flashcard view should randomly present a flash card if that type of operation was enabled.
I believe that my mistake is that I don't understand the key concept of data encapsulation, objects, etc. I'd appreciate any help.
Here is the Settings view controller. I'm not even sure how to put the code into this forum so this might be a laughable moment... here goes:
// settings_flashcards.h
#import <UIKit/UIKit.h>
#interface settings_flashcards : UIViewController {
UISwitch *additionSwitch;
UISwitch *subtractionSwitch;
UISwitch *multiplicationSwitch;
UISwitch *divisionSwitch;
}
#property (nonatomic,retain) IBOutlet UISwitch *additionSwitch;
#property (nonatomic,retain) IBOutlet UISwitch *subtractionSwitch;
#property (nonatomic,retain) IBOutlet UISwitch *multiplicationSwitch;
#property (nonatomic,retain) IBOutlet UISwitch *divisionSwitch;
#end
and...
/ settings_flashcards.m
#import "settings_flashcards.h"
#implementation settings_flashcards
#synthesize additionSwitch;
#synthesize subtractionSwitch;
#synthesize multiplicationSwitch;
#synthesize divisionSwitch;
- (void)viewDidLoad {
[additionSwitch addTarget:self action:#selector(additionSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[subtractionSwitch addTarget:self action:#selector(subtractionSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[multiplicationSwitch addTarget:self action:#selector(multiplicationSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[divisionSwitch addTarget:self action:#selector(divisionSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[super viewDidLoad];
}
-(void) additionSwitchFlipped {
if (additionSwitch.on) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"additionKey"];
}else {
[[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:#"additionKey"];
}
}
-(void) subtractionSwitchFlipped {
if (subtractionSwitch.on) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"subtractionKey"];
}else {
[[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:#"subtractionKey"];
}
}
-(void) multiplicationSwitchFlipped {
if (multiplicationSwitch.on) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"multiplicationKey"];
}else {
[[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:#"multiplicationKey"];
}
}
-(void) divisionSwitchFlipped {
if (divisionSwitch.on) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"divisionKey"];
}else {
[[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:#"divisionKey"];
}
}
Here is the Flashcards view...
// flashcardsViewController.h
#import <UIKit/UIKit.h>
#interface flashcardsViewController : UIViewController <UIActionSheetDelegate>{
UILabel *firstNumberLabel;
UILabel *secondNumberLabel;
UILabel *answerNumberLabel;
UILabel *operatorLabel;
BOOL additionIsEnabled;
BOOL subtractionIsEnabled;
BOOL multiplicationIsEnabled;
BOOL divisionIsEnabled;
}
#property (nonatomic,retain) IBOutlet UILabel *firstNumberLabel;
#property (nonatomic,retain) IBOutlet UILabel *secondNumberLabel;
#property (nonatomic,retain) IBOutlet UILabel *answerNumberLabel;
#property (nonatomic,retain) IBOutlet UILabel *operatorLabel;
-(void) buttonClicked:(id)sender;
#end
and...
// flashcardsViewController.m
#import "flashcardsViewController.h"
#implementation flashcardsViewController
#synthesize firstNumberLabel;
#synthesize secondNumberLabel;
#synthesize answerNumberLabel;
#synthesize operatorLabel;
- (void)viewDidLoad {
srand(time(0)); //seed random
//the following should assign the keys if they don't exist
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"additionKey"]){
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"additionKey"];
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"subtractionKey"]) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"subtractionKey"];
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"multiplicationKey"]) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"multiplicationKey"];
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"divisionKey"]) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"divisionKey"];
}
//the following should assign each BOOL variable based on the key
additionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:#"additionKey"];
subtractionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:#"subtractionKey"];
multiplicationIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:#"multiplicationKey"];
divisionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:#"divisionKey"];
[super viewDidLoad];
}
-(void) buttonClicked:(id)sender{
int a = rand() % 4;// random number generator (number to enter loop)
if ( additionIsEnabled || subtractionIsEnabled || multiplicationIsEnabled || divisionIsEnabled) {
while (a < 5) {
switch (a) {
case 0:
if (additionIsEnabled == TRUE){
int x = rand() % 11 + 1;
int y = rand() %11 + 1;
firstNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",x];
secondNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",y];
answerNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",(x+y)];
operatorLabel.text = [[NSString alloc]initWithFormat: #"+"];
a = 5;
}
else a++;
break;
case 1:
if (subtractionIsEnabled == TRUE){
int x = rand() % 19 + 1;
int y = rand() %11 + 1;
firstNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",x];
secondNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",y];
answerNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",(x-y) ];
operatorLabel.text = [[NSString alloc]initWithFormat: #"-"];
a = 5;
}
else a++;
break;
case 2:
if (multiplicationIsEnabled == TRUE){
int x = rand() % 11 + 1;
int y = rand() %11 + 1;
firstNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",x];
secondNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",y];
answerNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",(x*y)];
operatorLabel.text = [[NSString alloc]initWithFormat: #"×"];
a = 5;
}
else a++;
break;
case 3:
if (divisionIsEnabled == TRUE){
int x = rand() % 11 + 1;
int y = rand() % 11 + 1;
firstNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",(x*y)];
secondNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",y];
answerNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",x];
operatorLabel.text = [[NSString alloc]initWithFormat: #"÷"];
a = 5;
}
else a = 0;
break;
default:
break;
}
}
}
else
{
UIAlertView *noOperatorSelectedAlert = [[UIAlertView alloc]initWithTitle:#"You have not set any operations."
message:#"Return to the settings menu and decide which operations you wish to perform. (addition, subtraction, etc.)"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[noOperatorSelectedAlert show];
[noOperatorSelectedAlert release];
}
}
There are a few things to do here. Firstly you want a better way of saying what the default state is before the user has made any explicit decisions. Next you want to know when you should refresh your in-app state from user preferences.
Defaults Initialization
The first item's solution is to put the default values into the registration domain for your user preferences. This is something you'll do during application initialization, rather than having your views individually check preferences and update them at initialization time. The preferences system looks in quite a few places for data, and the first place it looks is in the in-memory-only registration domain. This is where you'll put the default (i.e. no value specified means 'x') values for each of your user preferences.
The API you'll use for this is -[NSUserDefaults registerDefaults:], which takes an NSDictionary of values. To set your default values of YES (in Objective-C the BOOL type uses YES and NO rather than TRUE and FALSE) you'll use something like this, commonly executed in a +initialize method for your application's main class:
+ (void) initialize
{
// in any +initialize, make sure it's being called on your class
// +initialize is different from all other methods in this respect
if ( [self isKindOfClass: [MyApplicationDelegate class]] == NO )
return; // being called on a superclass, don't do my stuff
// set up default values for certain preferences
NSMutableDictionary * defaults = [NSMutableDictionary new];
[defaults setObject: [NSNumber numberWithBool: YES] forKey: #"additionKey"];
[defaults setObject: [NSNumber numberWithBool: YES] forKey: #"subtractionKey"];
[defaults setObject: [NSNumber numberWithBool: YES] forKey: #"multiplicationKey"];
[defaults setObject: [NSNumber numberWithBool: YES] forKey: #"divisionKey"];
// set this as the registration domain
[[NSUserDefaults standardUserDefaults] registerDefaults: defaults];
[defaults release];
}
Usually you'll put everything into one method like this, so if you have other parts of the application which expect a default non-zero value for any preference, you should add those to this group and place it into your application delegate's +initialize method.
Now, when your other classes use [[NSUserDefault standardUserDefaults] boolForKey: #"additionKey"] and there is nothing saved to the preference files for that key, they will get the value supplied above.
Refreshing Cached Values
So, you have a view where the user is able to change these preferences. Your next job is to make the view above that update its member variables using the new preferences. For this we can use either delegation or notification. In this case, I'll go with notifications for to reasons:
You're using NSUserDefaults, which can theoretically change in many different places. Delegation would only inform you of changes made by one object.
NSUserDefaults already implements a handy notification which you can watch.
So, in your flashcardsViewController you'll have something like these few methods:
- (void) updateFromPreferences
{
// fetch current values from user defaults into your member variables
additionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey: #"additionKey"];
// etc...
}
- (void) viewWillLoad
{
// load variables from user defaults
[self updateFromPreferences];
// find out when the preferences have been changed
// this will cause -updateFromPreferences to be called
// whenever something changes preferences, inside the app or outside
[[NSNotificationCenter defaultNotificationCenter] addObserver: self
selector: #selector(updateFromPreferences)
name: NSUserDefaultsDidChangeNotification
object: nil];
}
- (void) viewDidUnload
{
// always remove notification observers.
[[NSNotificationCenter defaultNotificationCenter] removeObserver: self
name: NSUserDefaultsDidChangeNotification
object: nil];
}
- (void) dealloc
{
// add this to your existing dealloc routine
[[NSNotificationCenter defaultNotificationCenter] removeObserver: self
name: NSUserDefaultsDidChangeNotification
object: nil];
}
Summary
Taken together, these two should give you everything you need to make this work.
In addition to the "always YES" problem pointed out by Chiefly Izzy, your -buttonClicked: method does not read new values from NSUserDefaults. These values are read (once) in [flashcardsViewController viewDidLoad]. If they are changed in settings, the change will not be detected in flashCardsViewController until the next time it is loaded (probably the next time the application is launched).
The simplest approach would be to move your code for reading the IsEnabled BOOL values into the -buttonClicked method, like so:
-(void) buttonClicked:(id)sender {
// the following should assign each BOOL variable based on the key
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
additionIsEnabled = [defaults boolForKey:#"additionKey"];
subtractionIsEnabled = [defaults boolForKey:#"subtractionKey"];
multiplicationIsEnabled = [defaults boolForKey:#"multiplicationKey"];
divisionIsEnabled = [defaults boolForKey:#"divisionKey"];
int a = rand() % 4;
if ( additionIsEnabled || subtractionIsEnabled ...
This code ...
//the following should assign the keys if they don't exist
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"additionKey"]){
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"additionKey"];
}
... simply always set's additionKey to TRUE. If you would like to check if additionKey is set, do this ...
//the following should assign the keys if they don't exist
if (![[NSUserDefaults standardUserDefaults] objectForKey:#"additionKey"]){
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"additionKey"];
}
... boolForKey: documentation: If a boolean value is associated with defaultName in the user defaults, that value is returned. Otherwise, NO is returned.
Translated to human language - if there's an value associated with additionKey, this value is returned. If there's no associated value, NO/FALSE is returned.
So, your code does this - if value is not associated with additionKey or if it is set to NO, set it to YES. This leads to this - additionKey is always set to YES/TRUE.
I am fairly new to iPhone coding and wanted to know if someone can help me.
Have got the code ready so that when the app loads a UIAletView loads and prompts the user to review/rate the application but i have a button called "Never Rate"
I needed help to find out how to code the "never rate" button so what when it is pressed the UI Alert does not load everytime the app is loaded.
This is my code so far:
(void)viewDidLoad {
UIAlertView *alert;
alert = [[UIAlertView alloc] initWithTitle:#"Rate My Appication" message:#" Please Rate my Application and check out my other Apps"
delegate: self
cancelButtonTitle:#" Cancel "
otherButtonTitles: #" Rate Now ", #"Check Other Apps", #" Never Rate ", nil];
[alert show];
[alert release];
}
(bool)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// Never Review Button
if (buttonIndex == 3)
{
}
// Review Button
else if (buttonIndex == 1)
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://itunes.apple.com/gb/app/calculator/id389197581?mt=8"]];
}
// Other Apps Button
else if (buttonIndex == 2)
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://itunes.com/apps/2headsolutions"]];
}
}
You could store a boolean flag somewhere on disk. Then the next time, you check for neverRate == NO and display the alert accordingly.
You could store this using NSUserDefaults:
[[NSUserDefaults standardDefaults] setBool:YES forKey:#"neverRate"];
Then to retrieve it:
BOOL neverRate = [[NSUserDefaults standardDefaults] boolForKey:#"neverRate"];
if(neverRate != YES) {
//Show alert here
}
This is my code so far:
- (void)viewDidLoad {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Rate My Appication"
message:#" Please Rate my Application and check out my other Apps"
delegate:self
cancelButtonTitle:#" Cancel "
otherButtonTitles: #" Rate Now ", #"Check Other Apps", #" Never Rate ", nil];
[alert show];
[alert release];
}
- (bool)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// Never Review Button if (buttonIndex == 3) {
//}
// Review Button else if (buttonIndex == 1) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://itunes.apple.com/gb/app/calculator/id389197581?mt=8"]]; }
// Other Apps Button else if (buttonIndex == 2) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://itunes.com/apps/2headsolutions"]]; }
}
Rengers is right
Try using NSUserDefaults
Saving
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// saving a BOOL
[prefs setBool:BOOL_var forKey:#"RATE_KEY"];
[prefs synchronize];
Reading
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// getting a BOOL
BOOL var = [prefs boolForKey:#"RATE_KEY"];
Check This link