I am trying to make a small game where users answer questions. The overall flow will go like this:
Select a difficulty level by clicking a button (push segue)
Player 1 enters name, press "Next" (push segue)
Answer question
Answer question
Finish.
Player 2 enters name, press "Next" (push segue)
Answer question
Answer question
Finish.
Look at everyone's answers
Get sent back to #1
My problem is I don't really need to store user info (name, answers, etc) in a database since that data is useless after the round is over. However, I need it to persist enough so that I can access that data across the view controllers before the round is over.
For the difficulty level I'm using a custom property on AppDelegate to persist the setting:
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) NSString *difficultyLevel;
#end
DifficultyViewController.m
- (IBAction)setDifficultyLevel:(id)sender {
UIButton *button = (UIButton *)sender;
NSString *difficulty = [[button titleLabel] text];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.difficultyLevel = difficulty;
}
PlayerProfileViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"Edmund: %#", appDelegate.difficultyLevel);
}
However, this doesn't feel very scalable since I am going to have many other properties and I don't feel like that's what AppDelegate is meant to be used for.
Is there a common way to persist data for this sort of thing?
I think you need to have a kind of game manager. A manager is commonly used as a singleton that means you will have only one instance of this class. It could be something like:
-header file:
#interface GameManager : NSObject
#property (nonatomic, strong) NSString *difficultyLevel;
#property (nonatomic, strong) NSString *player1Name;
#property (nonatomic, strong) NSString *player2Name;
#end
-source file:
#implementation GameManager
#synthesize difficultyLevel, player1Name, player2Name;
+ (id)sharedManager {
static GameManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
// add what you need here
}
return self;
}
#end
And so you will update your code like that:
- (IBAction)setDifficultyLevel:(id)sender {
UIButton *button = (UIButton *)sender;
NSString *difficulty = [[button titleLabel] text];
GameManager *gameManager = [GameManager sharedInstance];
gameManager.difficultyLevel = difficulty;
}
You can also have a weak property on your manager in each view controller if you prefer.
Related
So I am creating an app in the form of a social media app. I am using the tutorial found here as a springboard since I am still trying to wrap my brain around Core Data. I've deviated from the tutorial by adding a sign up button that takes the user to a new View Controller and created a .h and a .m file and set the New Member screen to reference the .h and .m files. They are set up as follows for the .h:
#import <UIKit/UIKit.h>
#interface NewMemberViewController : UIViewController
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (strong, nonatomic) IBOutlet UITextField *nameTF;
#property (strong, nonatomic) IBOutlet UITextField *ageTF;
#property (strong, nonatomic) IBOutlet UITextField *usernameTF;
#property (strong, nonatomic) IBOutlet UITextField *passwordTF;
- (IBAction)alreadyMember:(id)sender;
- (IBAction)checkAndLogin:(id)sender;
#end
and for the .m:
#import "NewMemberViewController.h"
#import "CoreDataHelper.h"
#interface NewMemberViewController ()
#end
#implementation NewMemberViewController
#synthesize usernameTF, ageTF, passwordTF, nameTF, managedObjectContext;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//If the user is already a member simply dismiss the VC
- (IBAction)alreadyMember:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
//When done editing keyboard
- (IBAction)checkAndLogin:(id)sender {
managedObjectContext =self.managedObjectContext;
//Textfield Reference
UITextField *tf = (UITextField *)sender;
//Check tag numbers If its equal to 1 or 2(nameTF or ageTF) then
if (tf.tag==1||tf.tag==2)
{
[sender resignFirstResponder];
NSLog(#"This is working");
}
//If its equal to 3 then this means the username text field is active
else if (tf.tag == 3)
{
[sender resignFirstResponder];
//do a quick search to see if username is availible
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(username == %#)", [usernameTF text]];
//Run the query to check if user exists
if([CoreDataHelper countForEntity:#"Users" withPredicate:pred andContext:managedObjectContext] > 0)
{
//we found a user
NSLog(#"oh no...");
}
}
}
#end
With that being said if I run my app and go to the signup screen and test to see if the user exists, by typing in admin because it already exists, i get the following error in Xcode:
Canvases[779:11603] * WebKit discarded an uncaught exception in the webView:shouldInsertText:replacingDOMRange:givenAction: delegate: +entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'Users'
What does this mean and why is it being caused?
Have you pass the context to the view controller? Try with:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication]delegate];
yourVontext = [appDelegate managedObjectContext];
before fetch.
There is issue with below line.
managedObjectContext = self.managedObjectContext;
self.managedObjectContext is returning nil to managedObjectContext. Instead of self.managedObjectContext you should create Core Data Stack (method implementation)in the Application Delegate and then pass object reference of ManagedObjectContext from AppDelegate to your caller class.
I'm having trouble making a shopping cart sort-of concept in my app. I have my AppDelegate (named ST2AppDelegate) that contains an NSMutableArray called myCart. I want RecipeViewController.m to pass an NSString object to myCart, but every time I pass it the NSString and use NSLog to reveal the contents of the array, it is always empty.
Can anyone please tell me what I am doing wrong? I have worked on this code for days, and there is a line of code in which I don't understand at all what's going on (in the RecipeViewController.m, labeled as such).
Any help would be so appreciated... I'm just a beginner. Here are the relevant classes:
ST2AppDelegate.h:
#import <UIKit/UIKit.h>
#interface ST2AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) NSMutableArray* myCart;
- (void)addToCart:(NSString*)item;
- (void)readCartContents;
#end
ST2AppDelegate.m:
#import "ST2AppDelegate.h"
#implementation ST2AppDelegate
#synthesize myCart;
// all the 'applicationDid...' methods...
- (void)addToCart:(NSString *)item
{
[self.myCart addObject:item];
}
- (void)readCartContents
{
NSLog(#"Contents of cart: ");
int count = [myCart count];
for (int i = 0; i < count; i++)
{
NSLog(#"%#", myCart[count]);
}
}
#end
RecipeDetailViewController.h:
#import <UIKit/UIKit.h>
#import "ST2AppDelegate.h"
#interface RecipeDetailViewController : UIViewController
#property (nonatomic, strong) IBOutlet UILabel* recipeLabel;
#property (nonatomic, strong) NSString* recipeName;
#property (nonatomic, strong) IBOutlet UIButton* orderNowButton;
- (IBAction)orderNowButtonPress:(id)sender;
#end
RecipeDetailViewController.m:
#import "RecipeDetailViewController.h"
#implementation RecipeDetailViewController
#synthesize recipeName;
#synthesize orderNowButton;
// irrelevant methods...
- (IBAction)orderNowButtonPress:(id)sender
{
// alter selected state
[orderNowButton setSelected:YES];
NSString* addedToCartString = [NSString stringWithFormat:#"%# added to cart!",recipeName];
[orderNowButton setTitle:addedToCartString forState:UIControlStateSelected];
// show an alert
NSString* addedToCartAlertMessage = [NSString stringWithFormat:#"%# has been added to your cart.", recipeName];
UIAlertView* addedToCartAlert = [[UIAlertView alloc] initWithTitle:#"Cart Updated" message:addedToCartAlertMessage delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[addedToCartAlert show];
// add to cart (I don't understand this, but it works)
[((ST2AppDelegate*)[UIApplication sharedApplication].delegate) addToCart:recipeName];
// read cart contents
[((ST2AppDelegate*)[UIApplication sharedApplication].delegate) readCartContents];
}
#end
You need to initialize myCart when your application launches:
self.myCart = [[NSMutableArray alloc] init];
otherwise you are just attempting to add objects to a nil object which while it won't throw an exception because of the way objective-c handles nil objects it will not function as expected until you initialize it.
Do you ever initalize the shopping cart variable?
Try doing lazy instantiation.
-(NSMutableArray *) myCart{
if (!_myCart){
_myCart = [[NSMutableArray alloc] init];
}
return _myCart;
}
This way you will know it will always get allocated. Basically, this method makes it so that whenever someone calls your classes version of the object it checks to see if that object has been allocated and then allocates it if it has not. It's a common paradigm that you should employ with most of your objects.
This method should go in the app delegate (where the object was declared).
I have a UIButton in MainViewController.
MainViewController has a childViewContoller.
I need to access the UIButton (tcButton) property in MainViewController FROM the childViewController and set it to setSelected:YES in viewDidLoad. I have the following code in my ChildViewController.m file and it's not working.
#import "ChildViewController.h"
#import "MainViewController.h"
#import "CoreData.h"
#interface ChildViewContoller ()
#property (nonatomic, strong) CoreData *coreData;
#property (nonatomic, strong) MainViewController *mainViewController;
#end
#implementation ChildViewController
#synthesize coreData, mainViewController;
-(void)viewDidLoad
{
[super viewDidLoad];
self.managedObjectContext = [(STAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
[[(mainViewController *)self.parentViewController tcButton] setSelected:YES];
}
Your code is kind of a mess. Why are you creating a new instance of yourself in viewDidLoad? This makes no sense. If ChildViewController is truly a child view controller, then you can access the parent with self.parentViewController. You only need one line in the viewDidLoad:
-(void)viewDidLoad // Line 4
{
[[(MainViewController *)self.parentViewController tcButton] setSelected:YES]; // Line 8
}
There are several issues in your code but the main idea to perform what you want is getting a pointer to the mainViewController. There are many ways to do that but here a simple example how you can implement such thing. For instance in the initializer of the ChildViewContoller you can pass a pointer to the mainViewController:
#interface ChildViewContoller ()
#property (nonatomic, strong) MainViewController *mainViewController;
#end
#implementation ChildViewContoller
- (id)initWithMainViewController:(MainViewController *)mainViewController
{
self = [super init];
if (self)
{
_mainViewController = mainViewController;
}
return self;
}
- (void)viewDidLoad
{
[_mainViewController.tcButton setSelected:YES];
}
#end
Please not that I have not tested the code above but you can get the idea.
I'm working on an ios app with uitabviewcontroller that plays some music. I don't want each tab viewcontroller to create it's own audio player. I want to have one single audio player and have all the viewcontrollers share it.
so I have created a class called player, which would initiate avaudioplayer with the song url and plays the song,
#import <AVFoundation/AVFoundation.h>
#interface player : NSObject {
AVAudioPlayer *theMainAudio;
}
-(void)playSong:(NSString *)songName;
#end
I want to create only one instance of this class and all my viewcontrollers share it. I've tried creating it in my delegate,
#interface AppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
player *theMainPlayer;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#property (nonatomic, retain) player *theMainPlayer;
#end
in .m file,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//some other stuff here....
theMainPlayer = [[player alloc]init];
return YES;
}
and then I called it in my viewcontrollers,
player myPlayer = ((AppDelegate *)[UIApplication sharedApplication].delegate).theMainPlayer;
but this didn't work.
can anyone tell me what's wrong with what I've done or if there is any other way to do what I want to do, which is to create a player object and share it among all of my viewcontrollers.
Thanks
create a singleton, in your player.m
#import "player.h"
#implementation player
static player *sharedInstance = nil;
+ (player *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if (self) {
// Work your initialising here as you normally would
}
return self;
}
-(void)playSong:(NSString *)songName
{
// do your stuff here
}
to use this class, just import the player.h
[[player sharedInstance] playSong:#"something"];
How did it go wrong? To me it seems the last line would cause compile error. You should add asterisk for class declaration :
player * myPlayer = ((AppDelegate *)[UIApplication sharedApplication].delegate).theMainPlayer;
Other than that, I didn't catch anything.
But I prefer using Singleton pattern for cases like this.
Reference :
http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/Singleton.html
http://www.galloway.me.uk/tutorials/singleton-classes/
I think that I am a bit confused about iOS #property getter and setters. I am trying to set an NSString iVar in my AppDelegate.h file from another class so that it can be used by all of the classes in the project?
For example, I am working on an iPhone project that stores an iVar NSString *currentUser in AppDelegate.h. I need to be able to set this through one method in a ViewController.m and then get it through another method in a second ViewController?
Maybe Getter and Setter is the wrong direction of attack all together? I understand that i don't want to alloc init the AppDelegate as the iVar will only exist in that object and I want it accessible to ALL objects in ALL classes?
Please someone set me straight.
All the best,
Darren
Here's the setup for the app delegate.
#interface AppDelegate
{
NSString *__currentUser;
}
#property (monatomic, copy) NSString* currentUser;
#end
#implementation AppDelegate
#synthesize currentUser = __currentUser;
- (void) dealloc
{
[__currentUser release];
[super dealloc];
}
#end
From one view controller, you could set a value for the current user, and from a subsequent view controller, get that value for some nefarious purpose.
#implementation LoginController
- (void) viewDidLoad
{
...
AppDelegate *bob = [[UIApplication sharedApplication] delegate];
[bob setCurrentUser: #"Jim Kirk"];
...
}
#end
In some other view controller that appears later, the value of the current user can be accessed.
#implementation ProfileViewController
- (void) viewDidLoad
{
...
AppDelegate *bob = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString * user = [bob currentUser];
// insert nefarious purpose for current user value here
...
}
#end