Sending nil to CGPoint type parameter - objective-c

Suppose I have this method:
- (void)placeView:(UIView*)theView withCenterIn:(CGPoint)centerPoint;
So I pass the view and a point to te the view's center.
But it happens that I do not need to specify the center, just the view.
Passing "nil" leads to error.
Please, suggest how to skip passing the center point.
Keep in mind that I need to use that method like this:
- (void)placeView:(UIView*)theView withCenterIn:(CGPoint)centerPoint{
if(centerPoint == nil){//and I understand that it's a wrong comparison, as I cannot pass "nil" to CGPoint
//set a random center point
}
else{
//set that view to the specified point
}
}
Thanks in advance

You can't use nil as a "no point" indicator, because it is only for objects, and CGPoint is a struct. (As dasblinkenlight has already said.)
In my geometry library, I've defined a "null" CGPoint to use as a "no point" placeholder, and a function to test for it. Since the components of a CGPoint are CGFloats, and floats have a "invalid value" representation already -- NAN, defined in math.h -- I think that's the best thing to use:
// Get NAN definition
#include <math.h>
const CGPoint WSSCGPointNull = {(CGFloat)NAN, (CGFloat)NAN};
BOOL WSSCGPointIsNull( CGPoint point ){
return isnan(point.x) && isnan(point.y);
}

CGPoint is a C struct, you cannot pass nil for it. You can create a separate method that does not take the unnecessary CGPoint, and get rid of your if statement, like this:
- (void)placeView:(UIView*)theView withCenterIn:(CGPoint)centerPoint{
//set that view to the specified point
}
- (void)placeView:(UIView*)theView {
//set a random center point
}
If you insist on keeping one method, you could designate one point as "special" (say, CGMakePoint(CGFLOAT_MAX, CGFLOAT_MAX)), wrap it in a #define, and use instead of nil.
Yet another solution would be to wrap your CGPoint in NSValue:
NSValue *v = [NSValue withPoint:CGMakePoint(12, 34)];
CGPoint p = [v pointValue];

Related

Variable Scope in Objective-C

I'm getting started with Objective-C, and there is something about variables scope that I still didn't get. I searched about it, but I still couldn't catch what I'm doing wrong.
I'm trying to create a code that will give me the x,y difference between two point. The first NSLog inside the first IF shows the right value for pointWhereDragBegan.x and .y, but when I try to get the value of the pointWhereDragBegan in the second IF statement, the value I get for pointWhereDragBegan.x is -1.998683 and .y is 0.0.
I'm sure it is something really simple, I just can't catch my mistake.
- (void)drag:(UILongPressGestureRecognizer *)drag{
CGPoint pointWhereDragBegan;
if(drag.state == UIGestureRecognizerStateBegan){
pointWhereDragBegan = [drag locationInView:self];
NSLog(#"Drag started at %f,%f",pointWhereDragBegan.x,pointWhereDragBegan.y);
}
if(drag.state == UIGestureRecognizerStateEnded){
CGPoint pointWhereDragEnded = [drag locationInView:self];
float xDragged = pointWhereDragEnded.x - pointWhereDragBegan.x;
float yDragged = pointWhereDragEnded.y - pointWhereDragBegan.y;
NSLog(#"Drag ended at %f,%f",pointWhereDragEnded.x,pointWhereDragEnded.y);
NSLog(#"The user moved %f, %f",xDragged,yDragged);
}
}
drag.state will never be simultaneously UIGestureRecognizerStateBegan and UIGestureRecognizerStateEnded. This method should be invoked twice: once in each state.
As a result, in order to fix your issue, you'll need to persist pointWhereDragBegan outside the method scope. For example, you might use an instance variable.

cocos2d sub-classing

I know that cocos2d has scheduling callbacks to do nice things but when you need to use one CCAction (like CCMoveTo one) in order to move a sprite from position a to b, you do not have the ability to make small position arrangements to the sprite position for as long as the action is in effect.
The only possible way I found is by making a sub-class of CCMoveTo in order to check for obstacles and therefore provide some kind of movement to the left or right to a sprite that was moving from top to the bottom of the iPhone screen. The problem is that the sub-class does not have access to the parent class' instance variables (like the startPosition_ one) because they have not been declared as properties.
So I used the following snippet to overcome this situation but I wonder if I am doing something wrong...
- (void)myUpdate:(ccTime)time {
if(delegate && method_) {
NSNumber *num = (NSNumber *)[delegate performSelector:method_ withObject:ownTarget];
if(num) {
double xpos = [num doubleValue];
[num release];
CCMoveTo *parent = [super retain];
parent->startPosition_.x += xpos;
[parent release];
}
[super update:time];
}
Is it correct to retain/release the super-class? The "[super update:time];" at the bottom of the code will make the final positioning.
CCMoveTo *parent = [super retain];
Ouch! This statement makes absolutely no sense. It is the same as writing:
[self retain];
As for accessing the super class' instance variables: unless they're declared #private you can access them. I just checked: they're not #private. You should be able to write in your subclass:
startPosition_.x += xpos;
If that doesn't work make sure your class is really a subclass of CCMoveTo, and not some other class.
Finally, I'd like to say that actions are very limited when it comes to implementing gameplay. You're probably much better off to simply animate your game objects by modifying their position property every frame, based on a velocity vector. You have much more freedom over the position and position updates, and none of the side effects of actions such as a one-frame delay every time you run a new action.
-(void) update:(ccTime)delta
{
// modify velocity based on whatever you need, ie gravity, or just heading in one direction
// then update the node's position by adding the current velocity to move it:
self.position = CGPointMake(self.position.x + velocity.x, self.position.y + velocity.y);
}

Setting the frame.size using the dot operator

I have a UIController and as you all know UIController is associated to a view and you can access it using the getter and setters methods which are synthesized
UIController controller = init code ..
..
controller.view -> this gives me my UIView object which retained and autoreleased, this will be synthesized get method(If at all my synthesized getmethod understanding is correct)
controller.view.frame -> this gives me my CGRect struct
controller.view.frame.size -> CGSize struct
why cannot I assign a value directly to this frame structure
controller.view.frame.size.width = 20;
for the above statement I get this error "lvalue required as left operand of assignment"
This is a normal c dot operator I think it should work.Please enlighten if I am missing anything
Using the dot operator in this situation is using the frame getter method behind the scenes. Since the frame property is a CGRect, which is a simple C struct, frame returns you a copy of the value, not a pointer to the value. Changing it will modify the CGRect you have copied locally on the stack, not the CGRect of your view's frame property. To update the actual frame property you must go through the setter method [yourView setFrame:yourNewFrame]; or yourView.frame = yourNewFrame;.
the easiest to solve that is to set the whole frame again
controller.view.frame = CGRectMake(controller.view.frame.origin.x, controller.view.frame.origin.y, 20, controller.view.frame.size.height);
the reason why this is not working directly is described in James link, it has only getters.
Dot syntax is just syntax sugar.
In this case, this code:
controller.view.frame.size.width = 20;
Is actually this code:
[[controller view] frame].size.width = 20;
In C terms, that's like this:
ViewGetFrame(ControllerGetView(controller)).size.width = 20;
Bottom line is you can't set a subfield of a function result this way. And even if you could, it wouldn't affect the original but only a copy.

How do I return CGpoint function?

If i have this function:
-(CGPoint)limitPosition:(CGPoint)position {
//code here
return position;
}
how do I return it to a variable?
This:
CGPoint a;
CGPoint b;
a = [self limitPosition: b];
Doesnt work.
Without a clearer description of what you mean by "doesn't work", and probably what's going on where you have //code here, it's hard to say.
Basically, you can pass a CGPoint to and from a function or method with the syntax as you have it. It'll be passed by value, so that any changes to position inside the function will not be reflected in the variable passed as argument (b), but should be copied back in the return value (to a).
In the code fragment shown, you don't initialise a or b, so they may contain garbage. And obviously the method body isn't doing much. But otherwise it looks kosher, so the problem is probably elsewhere.

How to use a C array as instance variable?

For example I want to store this in an ivar:
CGFloat color[4] = {red, green, blue, 1.0f};
so would I put this in my header?
CGFloat color[];
How would I assign values to that guy later? I mean I can't change it, right?
Instance variables are zeroed out on allocation so you can't use initialisers with them.
You need something like this:
// MyObject.h
#interface MyObject
{
CGFloat color[4];
}
#end
// MyObject.m
#implementation MyObject
-(id) init
{
self = [super init];
if (self != nil)
{
color[0] = red;
color[1] = green;
color[2] = blue;
color[3] = alpha;
}
return self;
}
You'd need to put the size in so that enough space is reserved.
CGFloat color[4];
Or use a pointer to the array, but that's more work and hardly superior for representing something as well-known as a color.
You are better off using a NSColor object if you can.
However, to your original question one of my first questions is where do you want to create this array. When you say put it in a header do you mean as a member of a class or as a global array, you certainly can do both however there are some serious gotchas with putting globals in headers. If you need that follow up and I can explain it better.
If it is in a class then you can just declare it like any other member field. If you say
CGFloat color[4];
then the space for the array is allocated in your object itself. You can also just use a
CGFloat *color;
or its moral equivalent to refer to an array that is stored outside of the object. You do need to manage that storage appropriately however.
This matters in the case you hinted at where you use a constant array object and cannot later change it. That can happen but is rare, since it cannot happen with the first approach, you don't see it in the wild very often.
There is a whole dissertation on the corner cases in here, I am sure it is not helping to go into it. Just use CGFloat color[4] in your object and it won't matter, by the time you see things they will be mutable and you can just use them the way you expect.