Objective C, array operations - objective-c

I am trying to do actions on array's elements but i really don't understand why my code doesn't work:
here is my .h:
#interface ViewController : UIViewController
{
NSArray *tableauScore;
UILabel * modificationScore;
}
#property (weak, nonatomic) IBOutlet UILabel *nom1;
#property (weak, nonatomic) IBOutlet UILabel *nom2;
#property (weak, nonatomic) IBOutlet UILabel *nom3;
#property (weak, nonatomic) IBOutlet UILabel *nom4;
#property (weak, nonatomic) IBOutlet UILabel *bsc1;
#property (weak, nonatomic) IBOutlet UILabel *bsc2;
#property (weak, nonatomic) IBOutlet UILabel *bsc3;
#property (weak, nonatomic) IBOutlet UILabel *bsc4;
#end
my .m:
tableauScore = [NSArray arrayWithObjects:nom4, nom3, nom2, nom1, bsc1, bsc2, bsc3, bsc4, nil];
for(int i = 0; i < 8; i++)
{
modificationScore = [tableauScore objectAtIndex:i];
modificationScore.hidden = NO;
modificationScore.center = CGPointMake(modificationScore.center.x, modificationScore.center.y -40);
}
The issue is that i have a thread point at "modificationScore = [tableauScore objectAtIndex:i];" line and I don't know why. I saw a lot of topics but no one can help me. Is anybody of you have an idea?
thank you!

I assume by "thread point" you mean a crash? If so, one of the properties you add to the array is probably nil.
Check the number of elements in the array ([tableauScore count]) before you loop over them; don't assume there are eight. Or, even better, use the syntax to loop over all elements:
for (a in tableauScore) {

I would not globally define modificationScore if you don't really need it.
I would do:
for(UILabel *tempLabel in tableauScore)
{
tempLabel.hidden = NO;
tempLabel.center = CGPointMake(modificationScore.center.x, modificationScore.center.y -40);
}
I don't know what you want to achieve with chaning the center, that needs to be changed

[NSArray arrayWithObjects:] will only create an NSArray up to the first nil given, so if one of the UILabels is nil you will not get an array of size 8 as you assume, but only an array containing the UILabels up to that point.
Since you have a hard coded loop size, you're probably ending up indexing outside of the array.

If you are changing objects in tableauScore you cannot use an NSArray because NSArray is immutable.
Use a NSMutableArray, reconfigure your labels and use replaceObjectAtIndex:withObject: to change them in the array.

Related

How to do deep copying Objective-C

There is a class Patient with properties:
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) Symptoms *symptoms;
#property (assign, nonatomic) Status status;
#property (weak, nonatomic) id <PatientDelegate> delegate;
There is a class Symptoms with properties:
#property (assign, nonatomic) CGFloat temperature;
#property (assign, nonatomic) BOOL headache;
#property (assign, nonatomic) BOOL stomach_ache;
Both classes implement protocol NSCopying:
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
Patient *newPatient = [[[self class] allocWithZone:zone] init];
[newPatient setName:self.name];
[newPatient setSymptoms:self.symptoms];
[newPatient setStatus:self.status];
[newPatient setDelegate:self.delegate];
return newPatient;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
Symptoms *newSymptoms = [[[self class] allocWithZone:zone] init];
[newSymptoms setTemperature:self.temperature];
[newSymptoms setHeadache:self.headache];
[newSymptoms setStomach_ache:self.stomach_ache];
return newSymptoms;
}
Also there is a class Doctor:
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSMutableArray *history;
- (void)makeNoteIn:(Patient *)patient card:(NSMutableArray *)history;
- (void)report;
When patient gets well, doctor calls method makeNoteIn:
- (void)makeNoteIn:(Patient *)patient card:(NSMutableArray *)history {
Patient *newRecord = [patient copy];
[history addObject:newRecord];
}
After the record is made, all property of the patient return to the original values. While we are in the method makeNoteIn and the current patient is proceeded, in history there is link to this object which has correct property values. As soon as we exit the method or start to proceed another patient, all the property values reset to the initial value.
I tried to realise copying, but something still wrong.
When you want to deep-copy an object, you have to implement copy on all the substructures:
[newPatient setName:[self.name copy]];
[newPatient setSymptoms:[self.symptoms copy]];
Otherwise they will still reference the same object and changing one will affect all.
Note that you can do that automatically by declaring the properties as copy:
#property (copy, nonatomic) NSString *name;
#property (copy, nonatomic) Symptoms *symptoms;
It's common to use copy with NSString and NSArray to prevent assigning NSMutableString and NSMutableArray which could be changed externally by mistake. Make sure you implement NSCopying on Symptoms.

NSTextField - actions within *textField

I need help using NSTextField and its inputs/outputs.
In a functioning code I have three different NSTextFields (in Cocoa + Obj-C) I know how calculate result from more integer inputs...
--- AppController.h ---
#interface AppController : NSObject {
IBOutlet NSTextField *firsTextField; // 1st Outlet
IBOutlet NSTextField *secondTextField; // 2nd Outlet
IBOutlet NSTextField *resultTextField; // 3rd Outlet
}
- (IBAction)result:(id)sender;
#end
--- AppController.m ---
#Implementation AppController
- (IBAction)result:(id)sender {
double first = [firstTextField doubleValue]; // set value 1st outlet
double second = [secondTextField doubleValue]; // set value 2nd outlet
double result = first + second; // count result
[resultTextField setDoubleValue:result]; // set value 3rd outlet
}
#end
But while I try do the same thing in only one NSTextField, I don't know how to do it... My idea is, that process should be following:
Set 1st integer (input)
*Choose math function "/, , -, +" (method/action)
Set 2nd integer (input)
Result (method/action) for calculating above mentioned inputs based on math function
But it is actually all what I am able to explain... problem is that I don't know how I can store 1st input value (before choosing math function) and how to count up result between 1st and 2nd input value.
Thank you in advance for every tip / link / reference / tutorial / etc.
As long as you have only one cycle, it is pretty easy. (BTW: The human math syntax (infix notation) is a little bit weird for computer programming, priority rules and brackets are needed, so some invented another algebraic syntax, the PN or reverse PN.)
When a operator is pressed, you store the operator and the first operand into a ivar. This is a state you enter and usually in GUI programming you try to prevent states. However it is, what users expect from a calculator.
First let us beautify your code with properties and generated ivars.
#interface AppController : NSObject
#property (weak, nonatomic) IBOutlet NSTextField *operandTextField;
#property (weak, nonatomic) IBOutlet NSTextField *resultTextField;
#end
Add properties for the first value and the operation:
#interface AppController : NSObject
#property (weak, nonatomic) IBOutlet NSTextField *operandTextField;
#property (weak, nonatomic) IBOutlet NSTextField *resultTextField;
#property (nonatomic) double operand;
#property (nonatomic) NSString *operator;
#end
Then you need the actions for the operations. Here is an example for one:
#interface AppController : NSObject
#property (weak, nonatomic) IBOutlet NSTextField *operandTextField;
#property (weak, nonatomic) IBOutlet NSTextField *resultTextField;
#property (nonatomic) double operand;
#property (nonatomic) NSString *operator;
- (IBAction)plus:(id)sender;
#end
The next thing you need is to add an action for "operation begin". You can connect it to the text field and make it its "enter" action.
#interface AppController : NSObject
#property (weak, nonatomic) IBOutlet NSTextField *operandTextField;
#property (weak, nonatomic) IBOutlet NSTextField *resultTextField;
#property (nonatomic) double operand;
#property (nonatomic) NSString *operator;
- (IBAction)plus:(id)sender;
- (IBAction)operate:(id)sender;
#end
Okay, done with the interface. Let's go to the implementation:
#implementation AppController
- (IBAction)plus:(id)sender
{
// Store operator
self.operator = [valueTextField doubleValue]; // Some checking?
// Store operation
self.operation = #"+"; // You can use int constant for that, but this is the easy way
}
- (IBAction)operate:(id)sender
{
// Check, whether a operation is already selected
if ([self.operation length]==0)
{
// No: Do nothing
return;
}
// Get the second operand
double operand = [valueTextField doubleValue];
// Do the calculation
double result;
if ([self.operation isEqualToString:#"+"])
{
result = self.operand + operand;
}
else if ([self.operation isEqualToString:#"-"])
{
result = self.operand - operand;
}
…
[self.resultTextField setDoubleValue:result];
self.operation = #"";
// If you do not set this, the next operand will start a new calculation with the first
// operand. Another feature would be to store the result we got and repeatedly perform
// the operation on the last result. But I would change the UI in that case.
}
#end
Typed in Safari.
after what I studying your code ... I'm finally got functional code ;-)
at first... here is how looks app:
and here is the code:
---------- CalcController.h -----------
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#interface CalcController : NSObject {
IBOutlet NSTextField *textField;
}
#property (weak) IBOutlet NSTextField *operandTextField;
#property (weak) IBOutlet NSTextField *resultTextField;
#property (nonatomic) double value1;
#property (nonatomic) double value2;
#property (nonatomic) double result;
#property (nonatomic) NSString *operator;
- (IBAction)plus:(id)sender;
- (IBAction)result:(id)sender;
#end
---------- CalcController.m -----------
#import "CalcController.h"
#implementation CalcController
#synthesize operandTextField;
#synthesize resultTextField;
#synthesize value1;
#synthesize value2;
#synthesize result;
#synthesize operator;
- (IBAction)plus:(id)sender {
value1 = [textField doubleValue];
NSLog(#"operand1 = %lf", value1); // for test and see value in console
[textField setStringValue:#""]; // for clear text after 1st input done
operator = #"+";
}
- (IBAction)result:(id)sender {
value2 = [textField doubleValue];
NSLog(#"operand2 = %lf", value2); // for test and see value in console
if ([operator isEqualToString:#"+"]) {
result = value1 + value2;
NSLog(#"value1: %lf + value2: %lf = result: %lf", value1, value2, result); // for tests and see values in console
}
[resultTextField setDoubleValue:result];
operator = #"";
}
#end
I Think that working good .... now it is time to add next functions, buttons with numbers etc...
Thanks for showing the right way to do this!

NSDictionary: Comparing NSDictionaries

There are two of files. Lets call them fileOne and fileTwo
Each has several NSMutableDictionary properties with identical names. To list a few:
#property (retain, nonatomic) NSMutableDictionary * lunchStartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * lunchLocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * lunchLocationAddressObject;
#property (retain, nonatomic) NSMutableDictionary * activity1NameObject;
#property (retain, nonatomic) NSMutableDictionary * activity1StartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * activity1LocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * activity1CommentsFieldObject;
#property (retain, nonatomic) NSMutableDictionary * activity1LocationAddressObject;
#property (retain, nonatomic) NSMutableDictionary * activity2NameObject;
#property (retain, nonatomic) NSMutableDictionary * activity2StartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * activity2LocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * activity2CommentsFieldObject;
#property (retain, nonatomic) NSMutableDictionary * activity2LocationAddressObject;
I would like to compare the dictionaries with the same name in the two files by calling the method below (or something similar):
-(NSMutableDictionary *)cellColorForChanges:(NSMutableDictionary *)newdictionary :(NSMutableDictionary *)oldDictionary;
{
if(![newdictionary isEqualToDictionary:oldDictionary])
{
[newdictionary setValue:#"UIColor yellowColor" forKey:#"cellColor"];
}
return newdictionary;
}
I'm trying avoid writing code for each NSMutableDictionary manually. Is there a way to avoid the following:
if(![fileOne.lunchStartTimeObject isEqualToDictionary:fileTwo.lunchStartTimeObject])
{
fileOne.lunchStartTimeObject setValue:#"UIColor yellowColor" forKey:#"cellColor"];
}
I'm having trouble figuring out the most efficient way to accomplish the above. Is it somehow possible to send each dictionary to a method and get back the dictionary (updated with another key, if it's not equal)? Or what I'm trying to avoid is unavoidable?
You are running into this problem because you are not abstracting your objects sufficiently. It seems to me that your property list above is ridiculously redundant. Even the simple code code you provided is hardly readable.
Try to think of your problem in a more conceptional way. Try to think of objects that could encapsulate the functionality you are looking for.
Looking at your properties you probably want a class like this
#interface Activity : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSDate *startTime;
#property (nonatomic, strong) Location *location;
-(BOOL)isEqualToActivity:(Activity*)activity;
#end
Maybe you need a location class that stores more information about a location; perhaps you could use even more fields like firstName, lastName; maybe you need a type (enum or string) property that tells what kind of activity it is, etc. -- you get the idea.
As indicated, you could write your own comparison method where you could tweak for allowing more or less strict capital or small letters, number formats etc.

UITextfield is not showing the assigned NSString

UITextfield is showing null eventhough nsstring has value...
I've taken uitextfield in IB. Connection is correct in IB.
This is .h file
{
IBOutlet UITextField *dobTextField;
}
#property (nonatomic, retain) UITextField *dobTextField;
In .m file
#synthesize dobTextField;
-(void)displayDOB:(NSString*)str
{
NSLog(#"%#", str);
dobTextField.text = str;
}
In the log it is showing some value... but dobTextField is showing null...i tried
dobTextField.text = [NSString StringWithValue:#"%#", str];
but no use....
Please help me out guys.
Try doing...
NSLog(#"%#", self.dobTextField);
is it nil? (it shouldn't be)
Sounds like you possibly haven't connected up the outlet in IB.
EDIT
Hmm... second thoughts I think you're confusing things.
Remove the ivar from the .h file. Just use the #property.
#property (nonatomic, retain) IBOutlet UITextField *dobTextField;
Remove the #synthesize from the .m file.
In displayDOB function use this...
self.dobTextField.text = str;
This will ensure that everything is referring to the same thing and also uses best practise methods when it comes to properties.
It seems that you have not set your outlet of UITextField in xib
Check that in your interface builder
Also,
change this line
#property (nonatomic, retain) UITextField *dobTextField;
to
#property (nonatomic, retain) IBOutlet UITextField *dobTextField;
Also, change this line
dobTextField.text = str;
to
self.dobTextField.text = str;
Hope this helps you..

this class is not key value coding-compliant for the key XXX

I'm a beginner in everything programming and have been trying to implement some self learned stuff from the Big Nerd Ranch Books. But I'm really stumped by this problem, and yes, I have searched this and other forums for possible solutions with no success. Here is the code:
ANKYViewController.h:
#import <UIKit/UIKit.h>
#interface ANKYViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *weightFieldDeadlift;
#property (weak, nonatomic) IBOutlet UITextField *repFieldDeadlift;
#property (strong, nonatomic) IBOutlet UILabel *workingOneRMDeadlift;
#property (weak, nonatomic) IBOutlet UITextField *weightFieldBenchPress;
#property (weak, nonatomic) IBOutlet UITextField *repFieldBenchPress;
#property (strong, nonatomic) IBOutlet UILabel *workingOneRMBenchPress;
#property (weak, nonatomic) IBOutlet UITextField *weightFieldSquat;
#property (weak, nonatomic) IBOutlet UITextField *repFieldSquat;
#property (strong, nonatomic) IBOutlet UILabel *workingOneRMSquat;
#property (weak, nonatomic) IBOutlet UITextField *weightFieldMilitaryPress;
#property (weak, nonatomic) IBOutlet UITextField *repFieldMilitaryPress;
#property (strong, nonatomic) IBOutlet UILabel *workingOneRMMilitaryPress;
- (IBAction)calculateOneRM:(id)sender;
#end
ANKYViewController.m:
#import "ANKYViewController.h"
#interface ANKYViewController ()
#end
#implementation ANKYViewController
#synthesize weightFieldDeadlift;
#synthesize repFieldBenchPress;
#synthesize workingOneRMBenchPress;
#synthesize weightFieldSquat;
#synthesize repFieldSquat;
#synthesize workingOneRMSquat;
#synthesize weightFieldMilitaryPress;
#synthesize repFieldMilitaryPress;
#synthesize workingOneRMMilitaryPress;
#synthesize repFieldDeadlift;
#synthesize workingOneRMDeadlift;
#synthesize weightFieldBenchPress;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[self setWeightFieldDeadlift:nil];
[self setRepFieldDeadlift:nil];
[self setWorkingOneRMDeadlift:nil];
[self setWeightFieldDeadlift:nil];
[self setRepFieldBenchPress:nil];
[self setWeightFieldBenchPress:nil];
[self setWorkingOneRMBenchPress:nil];
[self setWeightFieldSquat:nil];
[self setRepFieldSquat:nil];
[self setWorkingOneRMSquat:nil];
[self setWeightFieldMilitaryPress:nil];
[self setRepFieldMilitaryPress:nil];
[self setWorkingOneRMMilitaryPress:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (IBAction)calculateOneRM:(id)sender {
float dw = [[weightFieldDeadlift text]floatValue];
float dr = [[repFieldDeadlift text]floatValue];
float d = (dw * dr * 0.0333) + dw;
NSLog(#"Deadlift: %f", d);
NSString *deadlift = [[NSString alloc]initWithFormat:#"%f", d];
[workingOneRMDeadlift setText:deadlift];
float bpw = [[weightFieldBenchPress text]floatValue];
float bpr = [[repFieldBenchPress text]floatValue];
float bp = (bpw * bpr * 0.0333) + bpw;
NSLog(#"Bench Press: %f", bp);
NSString *benchPress = [[NSString alloc]initWithFormat:#"%f", bp];
[workingOneRMBenchPress setText:benchPress];
float sw = [[weightFieldSquat text]floatValue];
float sr = [[repFieldSquat text]floatValue];
float s = (sw * sr * 0.0333) + sw;
NSLog(#"Squat: %f", s);
NSString *squat = [[NSString alloc]initWithFormat:#"%f", s];
[workingOneRMSquat setText:squat];
float mpw = [[weightFieldMilitaryPress text]floatValue];
float mpr = [[repFieldMilitaryPress text]floatValue];
float mp = (mpw * mpr * 0.0333) + mpw;
NSLog(#"Military Press: %f", mp);
NSString *militaryPress = [[NSString alloc]initWithFormat:#"%f", mp];
[workingOneRMMilitaryPress setText:militaryPress];
}
#end
File's owner class is already stated as ANKYViewController. Linking the outlets etc was by control dragging instead of manually coding (I gave up after spending too many hours).
The error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UIApplication 0x6c22e70> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key repsField.'
There's no copy and pasting of code, and it's certainly distressing that there is no mentioning of "repsField"(instead of repFieldxxx) anywhere.
I hope that this is enough information to help lead to a solution, as I've spent days looking through other people's solutions with no success.
Thanks.
Looks like the problem is that someone is accessing the UIApplication instance with a key (repsField) that is not KVC compliant.
Update I don't think you even need to subclass to add a breakpoint. Go to the breakpoint navigator and add a symbolic breakpoint for symbol -[UIApplication setValue:forUndefinedKey:] and then run the application.
I think you can debug it by subclassing UIApplication and set a breakpoint in a dummy override of the setValue:forUndefinedKey: method.
In the file where you call UIApplicationMain add this class:
#interface TestApplication : UIApplication
#end
#implementation TestApplication
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
[super setValue:value forUndefinedKey:key]; // set breakpoint here
}
#end
And then change the third argument to UIApplicationMain to #"TestApplication", for example:
UIApplicationMain(argc, argv, #"TestApplication", NSStringFromClass([AppDelegate class]));
Now run the application and it should break into to debugger and you should be able to see the call stack causing it.
When this error message appears it is very often a bad connection in a xib file or storyboard. I'll guess that this is an old example that has a MainWindow.xib file in it, with the File's Owner set as the UIApplication class. There is most likely a connection in that xib to an outlet called repsField in the File's Owner which, of course, does not match anything in the actual class.