Retaining a value when switching back and forth between views - objective-c

I've set up two views: ViewControllerOne and ViewControllerTwo.
In my ViewControllerTwo, I have a numeric variable (dataValue) that I input in a text field that I later use as a float value in a calculation.
The user can move to another Class view (ViewControllerOne), but when they return to ViewControllerTwo, the variable they inputted in the text field is not retained and returns to the default value I define in my ViewControllerTwo.m file.
How can I get my ViewControllerTwo to remember my dataValue?
I've #synthesized my dataValueTextField and defined my default dataValue in ViewControllerTwo.m:
#synthesize dataValueTextField;
float dataValue = 1.00;
Then, later, I've converted my dataValueTextField into a float value:
float textFieldData = [dataValueTextField.text floatValue];
dataValue = textFieldData;
But it never remembers dataValue when I switch views back and forth. Do I need to add something to my viewWillDisappear or do I need to add something to my viewWillDisappear or ViewWillAppear methods?
Any help would be appreciated. I've been grasping at straws trying different things to no avail.
Thanks.

Is it possible that when you're switching views, its actually creating a new instance of the view controller each time? That might explain the value resetting back to its default. Another possibility is that you have code setting it to the default value being called from an event callback which is triggered whenever the view shows up. You could try logging out all the event handlers and see whats getting called when switching between views.

If you are getting a memory warning, the View may be getting unloaded and then reloaded when you return to it causing viewDidLoad to be called again. This would cause your value in the UITextField to be reset.

dataValue is a float, which means it is a primitive. primitives are not retained and are lost at the end of each method. it looks by how you declare dataValue, it is a singleton? i'm not 100% on how singleton's are handled when you switch view, if they are released or are retained.
maybe best to make dataValue into a instance variable? like you did with dataValueTextField.

Related

Override a property to make it read-only from the subclass

I would like to subclass UILabel in such a way that the user of the class cannot set the text directly through label.text = #"foo". Instead I'd like to set the text from inside the subclass depending on some values.
What I tried:
BalanceLabel.h:
#interface BalanceLabel : UILabel
#property(nonatomic,copy, readonly) NSString *text;
#end
However, I get a warning telling me I'm restricting text access (like I wanted to) but I don't get any compile time errors if I try to set the text directly using an object of my subclass.
You can't do this. As a trivial example as to why not, just think of how the following code should behave:
UILabel *label = [[BalanceLabel alloc] init];
label.text = #"string";
That code creates a BalanceLabel, but stores it in a variable of type UILabel, which means that the subsequent setting of the .text property can't know that you tried to make the property readonly in BalanceLabel.
Unfortunately there's not much you can do about this. You could override the setter to throw an exception, which will let users know what they did wrong, but of course will also crash the app.
You should be putting the logic into controller that managed the view instead of view directly.
I assume you have some view that gets updated with new values and you want to update the BalanceLabel based on these new values.
Your controller is a delegate for your view so it receives new values, from either user or other modules of your app that populated new values (like loaded from file, downloaded from network and so on).
Your controller then figures out which bits of view needs update and sets new values - in your case calculate balance, I assume

In Objective-C, why is casting needed when assigning to a variable whose type is specified?

This use of type casting comes up a lot, e.g. in a button tap action:
UIButton *button = (UIButton *)sender;
My question is why the explicit cast "(UIButton *)" is necessary. Doesn't assigning the value of "sender" to variable "button" already effectively cast it as a pointer to UIButton?
It tells the compiler that you know what you're doing: "I know sender technically isn't a UIButon* but I promise that it always will be at run-time."
As Stephen said, it is not really required.
But it is a quesiton of style and good business practice.
BTW, when you omit it, you get a warning, not an error.
Just for getting rid of the warning you could simply do:
someObject = (id) anotherObject;
That will work with references to any Object of any class. You see that there is hardly any casting acutally done.
You can allways assign to superclasses without cast:
objectOfAClass = objectOfSubclassOfA;
Bottom line is, it helps you avoiding mistakes by forcing you to think a very brief moment about what you are actually doing there.
In objective-c it is common to write action methods like
- (void)didAction:(id)sender
This method can be called by any object(eg. UIButton, UIControl etc.), and expected set the sender self.
Ever object is an id. So in that method you have a variable in type of id. If you are sure it is a button then you can cast to UIButton.
Another way is, if you are sure that the method will be called only by a UIButton instance then you can change your action method to
- (void)didAction:(UIButton *)senderButton
If you do it so, you do not need to cast.
Please note that, for that kind castings you are responsible, that nothing goes wrong. If you unsure you can always check the class of object with
- (BOOL)isKindOfClass:(Class)aClass
method.

understanding whether to use protocol or inheritance (or both?)

I have a type of object in my game that simply moves horizontally.. It has a method like this:
-(void)moveLeftWithBlock:(void(^)())block {
self.targetX = self.position.x - MOVE_AMOUNT;
id action = [CCMoveTo actionWithDuration:0.125f position:ccp(self.targetX, self.targetY)];
id ease = [CCEaseInOut actionWithAction:action rate:4];
[self runAction: [CCSequence actions: ease,
[CCCallBlock actionWithBlock:block], nil]];
}
...
Now, I wanted to make a second type of object that's exactly the same, except that it can also move vertically... My initial thought was to just subclass that object's class, and overwrite the moveLeftWithBlock and update its targetY.. but not only do I not like that solution because I still end up with 99% duplicate code amongst the two classes, but also because my game needs the Y position set prior to that moveLeftWithBlock method being called.
So next I thought-- Ok, so I can make my game call a prepareToMove method, which could do any kind of setup that might be required... For the main object type, nothing.. For this 2nd object type, set the targetY.
However, I immediately started thinking-- wait---- I know protocols are about explicitly defining methods that are OPTIONAL or REQUIRED, and it made me think that perhaps I should be utilizing this here, but I just am not sure how.
So, can anyone explain to me how a protocol could be used in this case-- and if it is "the way to go" or not?
Any of these would work. You could add to this andAlsoMoveVertically: (bool) isVertical and then add the vertical movement in an if statement in the function.

Cocos2d update:(ccTime)dt giving screwed up values

Inside of a class that subclasses CCNode, I have scheduled an -update:(ccTime)dt method. I also have a bunch of behavior objects that don't subclass CCNode, but also have an -update:(ccTime)dt method. Here's the inside of my CCNode's update method:
-(void)update:(ccTime)dt{
for(Behavior *currentBehavior in behaviors){
[currentBehavior update:dt];
}
}
When I NSLog the dt value passed into my CCNode's update, it prints out normal values (0.116699, 0.162726). However, when I NSLog the dt value from inside the behaviors' update methods, the printed numbers are all of a sudden really screwed up (0.000, 36893488147419103232, -2.000). It's the strangest thing. When I debug it, I'll see that the first dt value is normal, and then I'll step inside the behavior's update, and the value will suddenly change to something crazy. What's going on?
I figured it out. I didn't have -update:(ccTime)dt in my behavior class's .m file, and my theory is that it took the complier extra time to look for the method selector, which therefore screwed up the ccTime.

How do I set Cocoa NSLevelIndicatorCell values?

I have a tableview. One of the columns in the tableview uses an NSLevelIndicatorCell.
I want to be able to allow the user to edit the warn and critical values for the level indicator such that when they enter a value into a a "warning level" textbox, it changes the warn value of the level indicators being displayed in ALL of the tableview's rows.
I am very much a newbie with Objective-C so all I can figure out so far is that I must need a delegate method to watch the textbox BUT if I succeed in doing that, how on earth do I send the new value to the particular tableview column so that the update happens to ALL of the rows (i.e. how do I send what message to the tableview and target a cell within a column within a tableview)?
Here is the code to the solution I came up with should anyone need it.
- (IBAction)setWarningLevel:(id)sender {
double v;
NSScanner *ns = [NSScanner scannerWithString:[warnLevel stringValue]];
[ns scanDouble:&v];
[levelIndicator setWarningValue:v];
}
This is a textbook case for using Cocoa bindings. Just bind the value of the text field to the NSLevelIndicatorCell in your table view (do that in Interface Builder). The updates should happen automagically.
I think it should apply for all the cells in the table view if you apply the binding to the cell in IB. However if it doesn't, you will need to write a couple lines of code that set up the binding every time a new row in the table is created. That link above will explain everything in detail, but basically you will be setting up a Key-Value Observer relationship in code between the text field and the instance of the level indicator in the row being created.
I think you may have overdone it.
NSTextField subclasses NSControl, so you need to look in the docs for NSControl for a useful function.
Try re-writing it like this; assuming you're taking the value from a warnLevel textfield.
- (IBAction)setWarningLevel:(id)sender {
double v = [warnLevel doubleValue];
[levelIndicator setWarningValue:v];
}
Although this is usually shortened to this;
- (IBAction)setWarningLevel:(id)sender {
[levelIndicator setWarningValue:[warnLevel doubleValue]];
}
You should probably have some validation that the textfield has a valid number. If you're only choosing a couple of numbers have a look at using a stepper control.
Usually, with Cocoa, if you feel like you're jumping through too many hoops, there is sometimes an easier way.
Usually ;-)