I have a ViewController, and GameController class which is just subclassed from NSObject. The view controller has one button linked to it and fires a IBAction that inits a GameController class. In the init of the GameController class is a CADisplayLink that adds one to an int named score. In the debugger the score is going up, but the label will only display 0.
in ViewController
-(void)setScore:(NSInteger)gameScore{
NSString *string = [NSString stringWithFormat:#"%i", gameScore];
scoreLabel.text = string;
NSLog(#"the score is %i", gameScore);
}
-(IBAction)play:(id)sender{
gameController = [[GameController alloc] init];
//in my header is GameController* gameController;
}
in GameController
-(id)init{
self = [super init];
if (self) {
viewController = [[ViewController alloc] init];
//in header ViewController* viewController;
displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(gameLoop:)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode: NSRunLoopCommonModes];
//in the header CADisplayLink* displayLink;
}
return self;
}
-(void)gameLoop:(CADisplayLink *)sender{
deltaScore++;
if (deltaScore >= 20) {
deltaScore -= 20;
score++;
//deltaScore and score are NSIntegers
[viewController setScore:score];
}
}
score is not updated because you are creating new instance of view controller in game controller, you somehow need to pass the object of view controller to game controller and then use the same object to call method setScore.
Look carefully at your code. Your ViewController's ‘-play:‘ method creates a new GameController. Your GameController's ‘-init‘ creates a new ViewController. Is that really what you want?
You haven't given us much to go on, but it seems likely that either your game controller or your view controller should be "in charge" and create one instance of the other. There are about a million questions here on SO related to how to get one object to send some data to the another, but they all boil down to this: to pass information from object a to object b, a needs a pointer to b. A can then use that pointer to send messages to b.
So, for example, your view controller creates the game controller. That means the view controller has a pointer to the game controller, and it can use an instance variable or property to save that. If the game controller needs to send messages back to the view controller, it'll need a pointer to the view controller. The view controller can supply that pointer (to itself) when the game controller is created, or at some point afterward. To facilitate that, the game controller needs a method that can accept the pointer to the view controller. Maybe you decide to do it at creation, so you change the game controller's initialize toon method to ‘-initWithDelegate:(id)‘, define a GameControllerDelegate protocol, and then adopt that protocol in your ViewController class. You'd do this rather than just using ‘-initWithViewController:(ViewController*)‘ because its good style to prevent GameController from depending on the particular class of the object that's helping it -- GameController probably only cares that its helper implements a few methods, it doesn't need to know that it's helper is a ViewController.
In GameCenter the Game Score is of type long long int so use long long in your methods and return type if using anywhere.
When you want to so it on the label use
yourLabel.text = [NSString stringWithFormat:#"%lld",yourscore];
Usually, when the debugger gets information correctly but the UI isn't updating is because the operation to update the UI isn't performed on the main thread. Try this:
dispatch_async(dispatch_get_main_queue(), ^{ [viewController setScore:score]; });
%i is an invalid format specifier; use %d for signed 32-bit integer or %u for an unsigned 32-bit integer
Have you tried?
NSLog(#"the score is %lu", gameScore);
It should work for you.
Related
I have a class named 'Capture' that is a subclass of NSObject and it also adds views to a superview.
I have added a Settings.bundle which consists of slider. When the slider value is 'ON/1' I show the views in 'Capture' in the main window as subview. When the slider value is 'OFF/0' I have to remove the view added from 'Capture' and release all the objects present in 'Capture'.
I simply don't want to hide the views created by 'Capture' class on to the main view, when the slider value is 'OFF/0'.
I want to dealloc the 'Capture' when the app in sent to background and not when the app terminates, so i cannot use the notification UIApplicationWillTerminateNotification to dealloc a singleton.
If i just hide the views displayed by 'Capture', there will be unnecessary use of memory for the 'Capture' class
So now how do i dealloc a singleton.
'Capture' class views will be on top of every view controller, even when the events occurs.
It is allocated once by class method called in AppDelegate.
singleton is created this way:
static ScreenCapture *sharedScreen = nil;
+(Capture *)sharedScreenCapture
{
if (!sharedScreen)
{
sharedScreen = [[Capture alloc] init];
}
return sharedScreen;
}
Why not to create some ReLoad method which will clear all your cashes and nullify your private variable?
Something like this:
+(void) reLoad {
#synchronized(self) {
// clear all the cashes...
sharedScreen = nil;
}
}
and to call it after receiving UIApplicationWillTerminateNotification
[ScreenCapture reLoad];
You can add another method:
+ (void)destroyScreenCapture
{
sharedScreen = nil;
}
Consider whether singleton is actually the correct pattern for you to use and if you really need to destroy the singleton or whether you should just 'clean' it to release all of the memory for the data that it isn't currently using.
I'm trying to make a modal view which displays the champion of my app.
there's a NSMutableString variable called champ in modal view,
which is supposed to be updated by returnChamp function in main view.
the champ string is correctly set in main view,
but in modal view, the champ value appears as (null).
In fact, it seems it doesn't even go into the returnChamp function.
so apparently something wrong with my calling or implementing returnChamp,
but I have another function that does the similar, and that works fine.
could anyone please help me?
-(void) mainView{
.....
champ = [[currentPlayers objectAtIndex:playerIndex] retain];
NSLog(#"%#",champ);
modalWinner = [[winner alloc] init];
modalWinner.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:modalWinner animated:YES];
}
- (NSMutableString *) returnChamp{
NSLog(#"returnChamp");
return champ;
}
//in modalWinner
-(void) modalView{
..............
champName = [[NSMutableString alloc] init];
NSLog(#"%#", [(MainViewController *)self.parentViewController returnChamp]);
champName = [(MainViewController *)self.parentViewController returnChamp];
UIImage *champImage = [UIImage imageNamed:champName];
}
self.parentViewController is probably not actually a reference to your object. For some reason, it seems that the framework always insists on setting a UINavigationController as self.parentViewController - even for modals, and to the extent that it will create one if there isn't already one. This is probably going unnoticed because you're casting it to your MainViewController type.
You'll need to find a different way of making your original object available to be communicated with, or perhaps pass the appropriate value to the newly-instantiated controller before you present it.
For example, if you add a champName property to the modal class, you can do:
modalWinner = [[ModalWinnerViewController alloc] init];
modalWinner.champName = myValue; /* Set value before presenting controller */
[self presentModalViewController:modalWinner animated:YES];
There will probably be some code needed to update the UI with this value. The viewWillAppear method of the modal view controller is a good place for this as it is called by the framework immediately before the view is presented.
Note that this property-based approach could be used to keep a reference to your intended parent object, as well. And see here for a different approach to solving a similar problem.
I'm pretty new to the objective-c language (less than three months) but it is something that i really need to understand.
Suppose there is a controller (in a iOS environment) that manages a table view for input data from the user. The table must have editable cells and some features to make the value selection easier, for example a button that shows a popover with the possible values for a field.
Suppose there is a field to store country names. The popover first shows a list of continents; when the user selects a continent, the controller of the popover must show the countries of the previews selected continent.
Now, this popover appears in many places in the app so it will be nice if I can encapsulate it for later use. What i will expect for this popover is something like this:
...
#protocol MyPopoverDelegate<NSObject> {
-(void)didSelectCountry:(NSString *)countryName;
{
...
MyPopoverController *dataSelector = [[MyPopoverController] alloc] init];
dataSelector.dataType = CountryDataType;
dataSelector.delegate = self;
[dataSelector show];
[dataSelector release];
...
The problem here is the line [dataSelector release] because the code for managing the popover must stay alive until the country is selected. That's means the dataSelector variable must be a property of the caller class and that sucks.
The question then is:
How can i organize situations like this to have a reusable controller?
Thanks
Edited after vodkhang answer:
Ok, that's a good one, but dataSelector still is a property.
What if i do:
#implementation MyPopoverController
- (id)init {
...
[self retain];
...
}
- (void)popoverControllerDidDismissPopover: (UIPopoverController *)popoverController {
...
[delegate didFinishSelectingCountry:countryName];
[self release];
}
#end
I never see this behavior in objective-c, i feel that this is not the idea.
Why is it wrong?.
One of the way you can do for delegate method is to have:
MyPopOverDelegate
- (void)didFinishSelectingCountry:(NSString *)countryName popOver:(MyPopOver *)popOver;
Caller.m
// the caller
- (void)viewDidLoad {
MyPopoverController *dataSelector = [[MyPopoverController] alloc] init];
dataSelector.dataType = CountryDataType;
dataSelector.delegate = self;
[dataSelector show];
}
- (void)didFinishSelectingCountry:(NSString *)countryName popOver:(MyPopOver *)popOver {
// finish stuff
[popOver release];
}
This way is used a lot like NSUrlConnection, UIImagePickerController
If you want some unique object reusable across an entire app from anywhere in the view hierarchy, you can make it a property of the app delegate, and let the app delegate own it (retain it when live, release it during memory warnings, etc.).
A self retained object may eventually run into problems if you ever port your code to a garbage collected environment.
I want to change an image on a view, from a popup dialog of 4-6 icons (imagine like changing your image on a messenger application).
The way I implement this modal popup is by creating a new view at IB, with opacity on the background, and then I load this as a subview:
IconsViewController *iconsViewController = [[IconsViewController alloc] initWithNibName:#"IconsView" bundle:nil];
[self.view addSubview:iconsViewController.view];
So, when the user touches an icon, I have
- (IBAction)iconIsSelected:(id)sender {
switch ([sender tag]) {
case 1:
[(ParentViewController*)[self superview] changeIcon];
break;
case 2:
// same here..
break;
default:
break;
}
[self.view removeFromSuperview];
[self release];
}
The changeIcon just sets the image to a corresponding icon.
As you can guess, this is not working - the changeIcon message never works.
I can't understand what am I doing wrong, any help much appreciated!
You have a few choices here...
First one is create a property on your IconsViewController of type ParentViewController*, for example:
#property (readwrite,nonatomic,assign) ParentViewController* parentController; // weak reference
To break this down further:
readwrite because we want to be able to access the value via [self parentController] but also change it via [iconsViewController setParentController:self]
nonatomic because I'm not too worried about threading
assign to make it a "weak reference" where the parent will not be retained by the child. If they each retain the other, it could lead to memory leaks later because unless explicitly released you'd end up with a retain circle causing neither object to hit a zero retain count.
When you load from nib, set the property:
IconsViewController *iconsViewController = [[IconsViewController alloc] initWithNibName:#"IconsView" bundle:nil];
iconsViewController.parentController = self;
Then, call to it from inside of iconIsSelected like this:
[[self parentController] changeIcon];
Alternatively, you can create a delegate protocol:
#protocol IconViewSelectedDelegate (NSObject)
- (void) changeIcon;
#end
And use that protocol as a property, instead of the parent view controller type. This is more abstract, but it keeps the design cleaner. The parent view controller would then implement that delegate protocol, as one of many others.
Another option is to use NSNotificationCenter and publish/subscribe to events from your dynamic view. This is the "loosest" coupling between the two objects, but it might be overkill for this scenario.
The superview of a view is a view, not a view controller, yet you cast the superview to be of class ParentViewController. If the view has no superview, it returns nil, and message to nil are no-ops (which explains why you don't crash there).
BTW, that [self release] at the end is highly suspicious.
Iam a newbiew to iPhone development. Version of my SDK is 2.2
In my code, UIViewController is used to change view dynamically once the app is launched, a method in the UIViewController is called to know which view should be initialized along with parameters, which goes to a 'switch-case' and assign a view to current view according to the parameter, like this:
case 1:
currentView = [[View01 alloc] init];
break;
case 2:
currentView = [[View02 alloc] init];
break;
and outside the switch-case:
[self.view addSubview:currentView.view];
I wonder f can pass a parameter along with initialization, like iniWithNibName or so? I need this because have to manipulate in the leaded view, according to the view from which its called.
Thanks.
One way to approach this is to modify your View01 and View02 classes to include an initWithParam: initialiser.
i.e. add
- (id) initWithParam:(NSString *)myParam;
to the #interface section and add
- (id) initWithParam:(NSString *)myParam {
if (self = [self init]) {
// handle or store 'myParam' somewhere for use later
}
return self;
}
to the #implementation section. Notice how the initWithParam: message internally calls the existing init. Obviously you could change the type, or number of parameters passed in as required.
Another approach would be to provide a property on your view class, so you could do something like the following:
currentView = [[View01 alloc] init];
currentView.myParam = #"SomeValue";
Which approach works the best will depend somewhat on your particular application needs.