What is the most efficient way to move NSArray objects to UITextFields? - objective-c

I have some code where there may or may not be objects in the Array... this is the code I am dealing with:
oServices1.text = CustomServicesArray[0];
oServices2.text = CustomServicesArray[1];
oServices3.text = CustomServicesArray[2];
oServices4.text = CustomServicesArray[3];
oServices5.text = CustomServicesArray[4];
oServices6.text = CustomServicesArray[5];
oServices7.text = CustomServicesArray[6];
oServices8.text = CustomServicesArray[7];
oServices9.text = CustomServicesArray[8];
oServices10.text = CustomServicesArray[9];
oServices11.text = CustomServicesArray[10];
oServices12.text = CustomServicesArray[11];
oServices13.text = CustomServicesArray[12];
oServices14.text = CustomServicesArray[13];
oServices15.text = CustomServicesArray[14];
oServices16.text = CustomServicesArray[15];
oServices17.text = CustomServicesArray[16];
oServices18.text = CustomServicesArray[17];
oServices19.text = CustomServicesArray[18];
oServices20.text = CustomServicesArray[19];
oServices21.text = CustomServicesArray[20];
oServices22.text = CustomServicesArray[21];
oServices23.text = CustomServicesArray[22];
Rather than check each and every array object for nil, is there a way I can take the oServices*xx*.text UIFields and put them into some kind of array so I can just use a loop?

Are you aware of reflexivity? With KVC you could save up much code and time:
for(int i=1; i<=23; i++) {
NSString* key= [NSString stringWithFormat: #"oServices%d"i];
// Remember that variables should start with a lowercase letter
[[self valueForKey: key] setText: customServicesArray[i-1] ];
}
But if you don't want to bind all these variables in your storyboard/xib file (even this may be too much), just set the tag of each text field in the order that you want (from 1), so that you can get them back using viewWithTag:
// From the UIViewController
for(int i=1; i<=23; i++) { // Consider defining a constant instead of 23
[[self.view viewWithTag: i] setText: customServicesArray[i-1] ];
}
I consider this last solution better because you avoid binding so many variables.

You can use an OutletCollection to hold oServices and loop on that. Note however that outlet collections are not sorted so you would need to sort them beforehand (on the tag criteria, or location for example).
For ordering see this question.

Set the tag property of the UITextFields to their corresponding ordinal in the array. The default value of tag is 0, so you may need to set the tag property to ordinal + 1 if there are other views in the parent view of your UITextFields. On the parent view of your text fields, you can use the viewWithTag: method to retrieve the appropriate UITextField.

Related

Adding a NSDictionary to a NSArray duplicates the first NSDictionary every time

So I pulled some JSON data from a web service which is stored in an NSArray called _infoFromJSON. Each array element in _infoFromJSON essentially has a dictionary of key/value pairs. The goal is to add them to myVehicleObject which is an NSMutableArray
for (NSDictionary* myDictionary in _infoFromJSON) {
myVehicleObject *vehicleInMembersProfile;
vehicleInMembersProfile = [[myVehicleObject alloc] init];
vehicleInMembersProfile.make = [[_infoFromJSON objectAtIndex:carCount] objectForKey:#"make"];
vehicleInMembersProfile.carName = [[_infoFromJSON objectAtIndex:carCount] objectForKey:#"nickname"];
vehicleInMembersProfile.year = [[_infoFromJSON objectAtIndex:carCount] objectForKey:#"year"];
carCount ++;
[self.myVehicleObject addObject:vehicleInMembersProfile] ;
};
With the above code I sort of achieved it, however it keeps adding the same 1st dictionary to myVehicleObject, so it inserts the same NSDictionary 4 times In the past I have used this:
[self.myVehicleObject addObject:[vehicleInMembersProfile copy]] ;
When I do it it throws the following exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[myVehicleObject copyWithZone:]: unrecognized selector sent to instance 0xab4e130'
What am I doing wrong here? Thanks!
Update Requested Sample JSON:
{
color = SILVER;
engine = "";
id = "CF270B81-3821-4585-8C90-7089D7A8654E";
imageUrl = "https://www.someprotectedurl.com/somefile.png";
licensePlate = "ABC-123";
make = Honda;
model = "CR-V";
nickname = "My CR-V";
vin = "XX551234687687687654";
year = 2009;
}
This may or may not be related to the problem, but your forin loop is completely broken. Whether or not your incorrect usage is the actual cause of the problem, this is something you should definitely fix as it can do nothing but cause problems.
If you want to use indexOfObject: to grab an object at an index of the array you're iterating through, you should use a regular for loop:
for (int carCount=0; index < [_infoFromJSON count]; ++carCount) {
// loop body remains identical to what you have...
// except remove carCount++;
}
But for what you're doing, a forin loop is indeed better, and forin loops can be faster since they can be handled in batches. But if you're using a forin loop, use the object you're defining in the loop declaration:
for(NSDictionary* myDictionary in _infoFromJSON) {
// myDictionary is a reference to an object in _infoFromJSON,
//for whatever given index it is currently working on
myVehicleObject *vehicleInMembersProfile = [[myVehicleObject alloc] init];
vehicleInMembersProfile.make = myDictionary[#"make"];
vehicleInMembersProfile.carName = myDictionary[#"nickname"];
vehicleInMembersProfile.year = myDictionary[#"year"];
[self.myVehicleObject addObject:vehicleInMembersProfile];
}
The error is that your class does not implement NSCopying.
However, there isn't any need to copy the object here. You're creating a new object each time through the loop. If you insert a copy, you're just pointlessly throwing away the original each time. The more likely cause if you're seeing odd loop behavior is that your loop is mixing up different kinds of enumeration, as pointed out by nhgrif. Instead of accessing [_infoFromJSON objectAtIndex:carCount], just use myDictionary.
I would probably use a for loop rather than a foreach since you need the array index, I would code it like this
NSMutableArray *vehiclesArray = [[NSMutableArray alloc]init];
for (int i = 0; i < [_infoFromJSON count]; i++)
{
//create vehicle
myVehicleObject *vehicleInMembersProfile = [[myVehicleObject alloc] init];
vehicleInMembersProfile.make = _infoFromJSON[i][#"make"];
vehicleInMembersProfile.carName = _infoFromJSON[i][#"nickname"];
vehicleInMembersProfile.year = _infoFromJSON[i][#"year"];
//finally add the vehicle to the array
[vehiclesArray addObject:vehicleInMembersProfile] ;
}
I wasn't sure what myVehicleObject was , and why is it the same type you are adding to itself, so I changed it to an array. But you get the point.

Search in CCSprite array element Objective C (cocos2d)

I'm using cocos2d and I want to see if a specific string is in the array's element. Here is the element, which is a CCSprite object:
<theSwift = 08A6EA70 | Rect = (0.00,0.00,27.00,75.00) | tag = 2 | atlasIndex = -1>
I am spawning "monsters" and one type of monsters get the tag = 1 and some get the tag = 2. Is it possible to check if the last monster spawned got the tag = 2 in the element above?
If that object is in an array, you could use an NSPredicate to find the object with a certain tag:
NSArray *myArray;
NSObject childWithTag = [[myArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"tag == 2"]]] lastObject];
EDIT: Since you are using cocos2d, its as simple as this:
CCSprite *spriteWithTag = (CCSprite *)[myLayer childWithTag:2];
Not sure I understand your question, I'll give it a shot though:
If the line above is simply an NSString and all you want is to check for tag = 2, then you'd do:
NSRange range = [theString rangeOfString:#"tag = 2"];
if (range.location != NSNotFound) {
// theString contains "tag = 2"
}
If you are talking about NSArray, then do this:
You can use containsObject in an if statement:
if ([array containsObject:#"tag = 2"]) {
//contains tag = 2
}
It would be better to use NSDictionary though. Use setObject:forKey: in NSMutableDictionary to set the values for their keys, and to check the value do:
[dict objectForKey:#"tag"]
I would recommend using the NSDictionary method.
You can easily use this method
[layerName getChildByTag:<(NSInteger)>]
for getting the child of any layer.

Pointer that can reference a UITextView or a UITextField

I want to print a form that contains a mixture of UITextFields and UITextViews, doing exactly the same thing with each one regardless of its actual type. To keep the code clean I'd like to assign each item in the views array to the same variable in order to retrieve its printing parameters. I thought I could do this with a variable of type id, but I haven't hit on anything that will compile. The code below doesn't compile but it shows what I want to do. My thanks to anyone who tells me how to do this correctly.
id theField;
//for each field on the page
for (j = offsetToFirstFormField; j < [self.fields count]; j++) {
theField = [self.fields objectAtIndex: j];
printStr = theField.text;
if ([printStr length] > 0) {
theFont = [theField font];
maxSize = CGSizeMake(theField.frame.size.width, theField.frame.size.height);
printStrSize = [printStr sizeWithFont:theFont constrainedToSize:maxSize lineBreakMode:UILineBreakModeClip];
printRect = CGRectMake((theField.frame.origin.x * xScale) + xOffset, (theField.frame.origin.y * yScale) + yOffset, printStrSize.width, printStrSize.height);
[printStr drawInRect:printRect withFont:theFont];
}
}
You can't use dot-syntax with id variables. You have to stick with message-sending syntax. For example, you have to change this:
printStr = theField.text;
to this:
printStr = [theField text];
If that doesn't fix it, edit your question and paste in the actual error messages you're getting.
Encapsulate your printable functionality in a protocol. So you will be using id<Printable> rather than id. Your protocol will have methods like getText getFont. Or you can use category to extend the existing classes.

UISegmentedControl method to set Segment Index?

I'm using a UISegmentedControl in a view to select a specific client. This is then setting an NSString property in my data model from the unique segment title as and when the view is closed. All works exactly as I had hoped. When I reload the view depending on what is stored in the model, I am then setting the UISegmentedControl with the following code in viewDidLoad. i.e. it reads the string property from the model, converts it to an index and selects the correct segment to reflect which client is stored in the model.
if ([self.itemToEdit.client isEqualToString:#"John"]) {
myIndex = 3;
} else if ([self.itemToEdit.client isEqualToString:#"David"]) {
myIndex = 2;
} else if ([self.itemToEdit.client isEqualToString:#"Paul"]) {
myIndex = 1;
} else if ([self.itemToEdit.client isEqualToString:#"Stephen"]) {
myIndex = 0;
}
self.reportEditorClient.selectedSegmentIndex = myIndex;
All works as planned, it's just that it seems quite clunky. I have scoured the documentation to see if there is a UISegmentedControl method that will do this but cannot find anything. Is there a better approach, or am I on the right lines here?
Put this name-to-index mapping in a dictionary, then this chain of if-else pretty much becomes a one liner.
Code formatting wise, you could use an array of names
NSArray *names = [NSArray arrayWithObjects:#"John", #"David", #"Paul", #"Stephen", nil];
then
self.reportEditorClient.selectedSegmentIndex = [names indexOfObject:self.itemToEdit.client];
(Though it's only slightly neater)

Objective-C modify parameter to method at runtime

I'd like to change the value of a parameter at runtime and curious how this works in Obj-C.
I have a loop where value of 'n' is 0 increasing by 1 with each loop. How does one increment the passed parameter by 1 as n moves.
UIViewSubclass *uiViewSubclass = [[UIViewSubclass alloc] initWithValue:([value integerValue])
andPlacement:kPlacement0;
next time through loop I'd like 2nd argument to read as: andPlacement:kPlacement1;
and then: andPlacement:kPlacement2; and on...
do I make kPlacement a string and stringByAppendingString:[[NSNumber numberWithInt:n] stringValue]; ?
What's the Obj-C/Cocoa approach?
You can't modify your source code or make up variable references at runtime. Objective-C isn't that dynamic.
If the values of kPlacement0 through kPlacementMax are sequential, you may be able to use a for loop to step through them directly:
for (MyPlacement placement = kPlacement0; placement += kPlacementIncrement; placement <= kPlacementMax) {
UIViewSubclass *instanceOfUIViewSubclass = [[UIViewSubclass alloc] initWithValue:([value integerValue])
andPlacement:placement];
//Do something with instanceOfUIViewSubclass.
[instanceOfUIViewSubclass release];
}
(You will need to define kPlacementIncrement and kPlacementMax in addition to the kPlacement0, etc. constants. I'm using MyPlacement as the name of your enumeration type that the kPlacement0 etc. constants correspond to.)
If they are not sequential, put them in a C array and iterate on that array:
enum { numPlacements = <#Insert the number of placement constants here#> };
MyPlacement placements[numPlacements] = {
kPlacement0,
kPlacement1,
kPlacement2,
⋮
}
for (unsigned i = 0U; i < numPlacements; ++i) {
UIViewSubclass *instanceOfUIViewSubclass = [[UIViewSubclass alloc] initWithValue:([value integerValue])
andPlacement:placements[i]];
//Do something with instanceOfUIViewSubclass.
[instanceOfUIViewSubclass release];
}
You probably can come up with more-descriptive names than kPlacement0 etc. When you want to refer to them by number, do that; when you want to refer to them by name, give them good names.