Two lines of code that are saying the same thing? - objective-c

I am folowing this exercise in a book, and at one point, there is an implementation for a method that creates labels placed at random in the view. Here is the code:
1 - (void)drawHypnoticMessage:(NSString *)message{
2 for(int i =0; i<20; i++){
3 UILabel *messageLabel = [[UILabel alloc] init];
4 messageLabel.backgroundColor = [UIColor clearColor];
5 messageLabel.textColor = [UIColor whiteColor];
6 messageLabel.text = message;
7 [messageLabel sizeToFit];
8 int width = self.view.bounds.size.width - messageLabel.bounds.size.width;
9 int randomX = arc4random() % width;
10 int height = self.view.bounds.size.height - messageLabel.bounds.size.height;
11 int randomY = arc4random() % height;
12 CGRect frame = messageLabel.frame;
13 frame.origin = CGPointMake(randomX, randomY);
14 messageLabel.frame = frame;
15 [self.view addSubview:messageLabel];
16 }
17 }
This works fine. My question is regarding the lines 12 and 14. When I was copying this exercise to Xcode from the book and I reached line 12, I instinctively changed it to:
12 CGRect frame;
This didn't work, and I don't understand why. To me, lines 12 and 14:
12 CGRect frame = messageLabel.frame;
14 messageLabel.frame = frame;
Are saying the same thing twice, surely. Can anyone please explain why it is not so?

Objective-C overloads C's struct member access operator (.) to also access properties of Objective-C objects. The problem is that you can't mix the two in a single assignment statement because the compiler's parser gets confused.
messageLabel is an object that has a property called frame. frame is a struct with two members: origin and size.
To get around this limitation, you need to use a temporary variable to hold the struct (e.g. frame), manipulate this copy (structs are copied on assignment), and then assign the new struct (frame) to the property, which updates the property's value.

You are modifying the frame in line 13, i.e. changing the origin value. The book is showing you a common technique to change the frame, because you can't change point or size value by itself.

Related

Add label with for loop in different coordinates

I wan't to add labels (strings from an array) onto buttons with a for loop.
I'm new in objective-c and I don't know how I can fit all the changes into the loop on each iteration.
If there is a better way to do this, please show me. Right now I got this, which only prints the second element in the array out at upper right corner.
for (int i=0; i< sizeof(arrayOfLetters); i++ ) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(441,11,30,20)];
label.text = [NSString stringWithFormat:#"%#",[arrayOfLetters objectAtIndex:1]];
[self.view addSubview:label];
}
You are close. You want:
for (int i = 0; i < arrayOfLetters.count; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(441, 11 + i * 25, 30, 20)];
label.text = arrayOfLetters[i];
[self.view addSubview:label];
}
You should also give each label a different frame as I did here. Adjust as needed.
Keep in mind that the sizeof function gives you the size of the variable. Since arrayOfLetters is an object pointer the result will probably be 4. You want the actual count of the array. See the docs for NSArray.
Also, do not needlessly use stringWithFormat:. Only use it when you actually have a string that needs formatting.

Calculating the exact size of a CGRect frame for a pdf based on UITextField

I am creating a pdf report that renders frames based on text in a series of UITextFields. Currently I am using a series of methods as below. The variables are used to keep track of the position for the next frame and are based on the length of the text in the current UITextField being considered and the assumption that when rendered the length of each line is around 97 characters on the pdf doc.
-(void) drawTextObservationComment;
{
//pdfLineHeight is the height of size 12 rendered text
pdfLineHeight = 15;
CGContextRef summaryContext = UIGraphicsGetCurrentContext();
UIFont *font = [UIFont fontWithName:#"arial" size:12];
CGContextSetFillColorWithColor (summaryContext, [UIColor blackColor].CGColor);
//pdfCurrentLine is the y-axis coordinate from which to begin the new frame
CGRect textRect = CGRectMake(60, pdfCurrentLine, 650, 300);
NSString *myString = self.observationComment.text;
[myString drawInRect:textRect withFont:font lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentLeft];
//checks for the no of carriage returns within the text field
NSInteger numberOfLines = [[myString componentsSeparatedByString:#"\n"] count];
//the new pdfCurrentLine value is the previous value
// + the no of new lines based on text length
// + the no lines based on the no of carriage returns within text field
// + 40 as standard gap
pdfCurrentLine = pdfCurrentLine + (([observationComment.text length]/97)*pdfLineHeight) + ((numberOfLines - 1) * pdfLineWidth) + 40;
}
This works to a certain extent, but I realise that it is not perfectly accurate. The rendered text in the CGRect frame is often not 97 characters (although it is usually around this figure give or take 10 chars). This depends on the text entered (the letter 'i' being thinner for example and therefore there may be more chars on a line where there are a lot of 'i's).
I would like to know if there is any way of accurately calculating exactly how many lines the rendered text will actually use, therefore allowing me to accurately calculate the exact position of the next frame. Or any other advice gratefully received.
Have you looked at the Documentation for NSString UIKit Additions?
There are methods in there that return the size of rendered text such as sizeWithFont:constrainedToSize and sizeWithFont:constrainedToSize:lineBreakMode:.

Reusing sprites when using a lot of them

I am in front of a problem when adding sprites on my game layer.
When the game starts it's initialize a two dimensional array (that I created) and put 30 sprites in it (5 columns, 6 lines). It's always the same image that I use for the 30 sprites.
The problem is that each time I create a new sprite...
Every 3 sec all sprites deseaper and new ones appear. I am using ARC but when I check into Instruments (allocations part) I can see the number of living sprites increasing every 3sec 30, 60, 90, 120 ...
So when I play for 15 minutes my game crash. How can I use the same sprite without creating a new one ?
I tried to create it in the init method but then when I enter in [self addChild : mysprite] it says to me that the sprite had already been add.
Here is my code :
int yValue = 400;
for (int line = 0; line < nbLines; line++) {
int xValue = 32;
for (int section = 0; section < nbSection ; section++) {
MySprite *mySprite = [[MySprite alloc] initWithFile:#"mySprite.png"];
mySprite.position = ccp(xValue, yValue);
[gameTab setObject:mySprite :section :line];
[self addChild:mySprite];
xValue += 64;
}
yValue -= 64;
}
and here is what I do for deleting the sprite :
[gameTab removeChild:mySprite cleanup:YES];
[self removeChild:mySprite cleanup:YES];
I don't know when ARC deallocate my sprites.
Thx.

Draw polyline between given points on the map

I am implementing an iOS application, and I want to draw a polyline between several given coordinates on the map.
I wrote the code and got the polylines being drawn from my point reaching an infinite point. In other words the beginning point of the line starts from the my given lat and long point, but the end of the line is infinite and not the other point.
This is my code...
I filled the coordinates in a NSMutableArray called routeLatitudes. The array cells are being filled one for latitude and one for longitude.
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * [routeLatitudes count]);
for(int idx = 0; idx < [routeLatitudes count]; idx=idx+2)
{
CLLocationCoordinate2D workingCoordinate;
workingCoordinate.latitude=[[routeLatitudes objectAtIndex:idx] doubleValue];
workingCoordinate.longitude=[[routeLatitudes objectAtIndex:idx+1] doubleValue];
MKMapPoint point = MKMapPointForCoordinate(workingCoordinate);
pointArr[idx] = point;
}
// create the polyline based on the array of points.
routeLine = [MKPolyline polylineWithPoints:pointArr count:[routeLatitudes count]];
[mapView addOverlay:self.routeLine];
free(pointArr);
and overlay delegate
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
MKOverlayView* overlayView = nil;
if(overlay == routeLine)
{
self.routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];
self.routeLineView.fillColor = [UIColor colorWithRed:51 green:51 blue:255 alpha:1];
self.routeLineView.strokeColor = [UIColor colorWithRed:204 green:0 blue:0 alpha:1];
self.routeLineView.lineWidth = 3;
overlayView = routeLineView;
}
return overlayView;
}
So I need the lines to be drawn between the points over the map. The beginning of the line is the first dropped pin, and the end is on the last dropped pin.
According to the code, the routeLatitudes array has objects listed like this:
index 0: latitude for point 1
index 1: longitude for point 1
index 2: latitude for point 2
index 3: longitude for point 2
index 4: latitude for point 3
index 5: longitude for point 3
...
So if routeLatitudes.count is 6, it actually has only 3 points.
This means the malloc is allocating the wrong number of points and the polylineWithPoints call is also specifying the wrong number of points for the overlay.
The other problem is that since pointArr will contain only half the objects that routeLatitudes has, you can't use the same index value for both arrays.
The for loop index counter idx is being incremented by 2 at each iteration because that's how the routeLatitudes points are layed out but then the same idx value is used to set pointArr.
So for idx=0, pointArr[0] is set but then for idx=2, pointArr[2] is set (instead of pointArr[1]), and so on. This means every other position in pointArr is left uninitialized resulting in the lines "going to infinity".
So the corrected code might look like this:
int pointCount = [routeLatitudes count] / 2;
MKMapPoint* pointArr = malloc(sizeof(MKMapPoint) * pointCount);
int pointArrIndex = 0; //it's simpler to keep a separate index for pointArr
for (int idx = 0; idx < [routeLatitudes count]; idx=idx+2)
{
CLLocationCoordinate2D workingCoordinate;
workingCoordinate.latitude=[[routeLatitudes objectAtIndex:idx] doubleValue];
workingCoordinate.longitude=[[routeLatitudes objectAtIndex:idx+1] doubleValue];
MKMapPoint point = MKMapPointForCoordinate(workingCoordinate);
pointArr[pointArrIndex] = point;
pointArrIndex++;
}
// create the polyline based on the array of points.
routeLine = [MKPolyline polylineWithPoints:pointArr count:pointCount];
[mapView addOverlay:routeLine];
free(pointArr);
Also note in the malloc line, I corrected sizeof(CLLocationCoordinate2D) to sizeof(MKMapPoint). This technically wasn't causing a problem because those two structs happen to be the same length but it's correct to use sizeof(MKMapPoint) since that's what the array is going to contain.

Assigning to array element in Objective C assigns to wrong index

I'm seeing some rather strange behavior as I'm debugging my iPhone project. Among other things, in the header file for a UIViewController descendant, I have this:
#interface OOGameDetailsViewController : UIViewController <OOXMLParserDelegate,OOServerDelegate>
{
...
OOXMLNode *node;
float elheights[6];
...
}
#property (readwrite, retain) OOXMLNode *gameNode;
...
#end
Then in the code, I'm assigning values to this array:
- (void)setNode:(OOXMLNode *)aNode
{
node = aNode;
[self updateUserDefaults];
[background setImage:[gameNode imageFromKey:#"background"]];
CGFloat maxw = 240.0f;
NSString *text = [gameNode elementNodeWithKey:#"title"].value;
CGSize sizeOfText=[text sizeWithFont:[UIFont boldSystemFontOfSize:20.0f]
constrainedToSize:CGSizeMake(maxw, CGFLOAT_MAX)
lineBreakMode:UILineBreakModeWordWrap];
...
elheights[0] = 10 + sizeOfText.height;
text = [gameNode elementNodeWithKey:#"age"].value;
sizeOfText=[text sizeWithFont:[UIFont systemFontOfSize:16.0f]
constrainedToSize:CGSizeMake(maxw, CGFLOAT_MAX)
lineBreakMode:UILineBreakModeWordWrap];
...
elheights[1] = 10 + sizeOfText.height;
...
elheights[2] = 10 + height;
...
elheights[3] = 10 + separatorHeight;
text = [gameNode elementNodeWithKey:#"intro"].value;
sizeOfText=[text sizeWithFont:[UIFont boldSystemFontOfSize:14.0f]
constrainedToSize:CGSizeMake(maxw, CGFLOAT_MAX)
lineBreakMode:UILineBreakModeWordWrap];
...
elheights[4] = sizeOfText.height;
...
elheights[5] = 20 + btnHeight;
[mainTable reloadData];
}
The ideas is quite simple: I'm assigning 6 floating point values to a 6-element array - with indices from 0 to 5. However as I'm stepping through this code in the debugger, I consistently see each of the assignments assign the element of the array with index 1 higher than indicated. That is, elheights[0] = ... actually assigns this vlaue into elheights[1] and so on. The last assignment (assigning to element with index 5 simply does nothing).
To prove that I'm not crazy, here are screenshots of the debugger (crops of the relevant part - to keep image size to minimum). The first one is right before the assignment, the second one is right after the assignment.
I cleaned the project and restarted; I closed the simulator, closed Xcode, reopened and restarted - same results. Is there something special about the way ObjectiveC handles simple C-style arrays?
Thanks in advance,
-Aleks
XCode4 uses new LLDB debugger by default, which does not always work perfectly yet. (For example, the current version does not recognize structure alignment attribute). It also has some bugs here and there.
Switch to GDB (click Scheme / Edit Scheme in the toolbar, there is the “Debugger” popup button with two choices) if you're having problems with it.
I've experienced strange debugger behaviour in Xcode when stepping through optimised (i.e. non -O0) code that was a result of the compiler re-ordering instructions. Try making sure the GCC_OPTIMIZATION_LEVEL isn't something you aren't expecting.