What is the following structure called in Objective C,
CG_INLINE CGRect
CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
{
CGRect rect;
rect.origin.x = x; rect.origin.y = y;
rect.size.width = width; rect.size.height = height;
return rect;
}
I want to create my own definition for making an object that would hold multiple parameters that I pass. Currently I am using a static method within a class that would take multiple parameters and return an object. But this syntax of CGRectMake is a perfect solution i could use. What is the structure actually called in Objective C syntax?
CGRectMake() is a function. Functions are a C language feature, not unique to Objective-C. Therefore, they don't have a special name in Objective-C. They're called C functions.
If you're asking about this seemingly cryptic syntax:
CG_INLINE CGRect
CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
CG_INLINE is a special token that does nothing more than declare a function as inline. You can find a description of CG_INLINE in this question:
What does CG_INLINE do?
CGRect is the return type. The line break that follows is mere whitespace; it's a somewhat common code-formatting convention in C to break a line after the return type in a function definition.
CGRectMake is the function name.
The rest that follows is its argument list and function body.
If you want to create a function, you can place its definition in your .m file, with a corresponding forward declaration in the .h file.
Related
What would be the equivalent of extensions in Objective-C as in Swift? Is it the same as creating a class function within a class?
extension CGRect{
static func rectWithTwoPoints(p1:CGPoint,p2:CGPoint) -> CGRect
{
return CGRectMake(min(p1.x, p2.x),min(p1.y, p2.y),fabs(p1.x - p2.x),fabs(p1.y - p2.y));
}
}
In objective C its category and in swift its extension
1.Click File -> New -> File
2.Select Objective-C file under Sources in iOS or Mac OS respectively and Click Next
3.Now select File Type as Category
Select UIView as baseclass of category and set name as "UIView+CGRect"
And you can add your methods like
UIView+CGRect.h of category :
+ (CGRect) rectWithTwoPoints:(CGPoint) p1 andWith:(CGPoint) p2;
UIView+CGRect.m of category :
+ (CGRect) rectWithTwoPoints:(CGPoint) p1 andWith:(CGPoint) p2 {
return CGRectMake(MIN(p1.x, p2.x), MIN(p1.y, p2.y), fabs(p1.x - p2.x), fabs(p1.y - p2.y));
}
And just import your category in view controller where you want to use it and access like
In ViewController.h
#import "UIView+CGRect.h"
And code will be
CGrect rect = [UIView rectWithTwoPoints:POINT_ONE andWith:rectWithTwoPoints:POINT_TWO];
You will get desired result.
There is no single equivalent, they're different languages with different capabilities.
For your example the 'equivalent' would be a utility function declared somewhere, likely just in a file, because CGRect isn't a class. It would be a C function, not an Obj-C method.
You could even declare a macro for it.
In Objective-C structures are not similar to classes, but plain C. Therefore they do not have functions associated with them. The usual pattern is that there are (C) functions dealing with the structure. You will find a bunch of them in the headers, for example:
CGRect CGRectMake ( CGFloat x, CGFloat y, CGFloat width, CGFloat height );
If you want to have an extra function, just write it:
CGRect rectWithTwoPoints(CGPoint p1, CGPoint p2)
{
return CGRectMake(min(p1.x, p2.x),min(p1.y, p2.y),fabs(p1.x - p2.x),fabs(p1.y - p2.y));
}
And put a prototype into a header, if you want to use it outside the defining compilation unit:
CGRect rectWithTwoPoints(CGPoint p1, CGPoint p2); // <- There is a semicolon for prototyping
I have a function like so:
- (void)addBalloon:(COLOR)color:(VELOCITY)velocity:(LOCATION)location
Where COLOR is an enum, and VELOCITY and LOCATION are structs defined in a constants header file..
VELOCITY and LOCATION both store two ints, x and y.
When calling this method, I would call it like so:
VELOCITY vel;
LOCATION loc;
vel.x = 100.0;
vel.y = 0.0;
loc.x = 10.0;
loc.y = 10.0;
[self addBalloon:Red:vel:loc];
But to me, this seems disorganized. I would like to call the function directly in one line while creating the struct on the line..
Here is my question: I'm not sure if this can be done using #define.. but if it can't.. is the only other viable option creating a function that returns VELOCITY or LOCATION and takes inputs x, and y?
I would like to do something like the following:
[self addBalloon:Red:VELOCITY(100.0, 0.0):LOCATION(10.0, 10.0)];
You can use the C99 syntax for designated initialisers:
[self addBalloon:Red:(VELOCITY){100.0, 0.0}:(LOCATION){10.0, 10.0}];
or
[self addBalloon:Red:(VELOCITY){.x=100.0, .y=0.0}:(LOCATION){.x=10.0, .y=10.0}];
In Cocoa there is a NSPoint type to represent points in 2d-space. I can't find a way to represent a 3D type. Is there one and in that case what is it called? Or should I roll my own?
Right, make your own. I use something like this:
typedef struct Point3D_ {
CGFloat x, y, z;
} Point3D;
Also do this to make creation of Point3D easier:
Point3D Point3DMake( CGFloat xx, CGFloat yy, CGFloat zz ){
Point3D p;
p.x = xx; p.y = yy; p.z = zz;
return p;
}
While I have yet to remember or find something in the core of AppKit or Cocoa that's directly analagous to CGPoint, Apple do provide some types within "extra" frameworks. Here's a collection of what I've got so far.
SceneKit
Apple's SceneKit provides a type SCNVector3, which is documented to be a simple struct:
typedef struct SCNVector3 { CGFloat x, y, z; } SCNVector3;
There are a few helper functions like SCNVector3Make and
SCNVector3EqualToVector3 and one to convert between GLKit vectors.
GLKit
Apple's GLKit provides a type GLKVector3, with even more features and helper functions.
union _GLKVector3 {
struct { float x, y, z; };
struct { float r, g, b; };
struct { float s, t, p; };
float v[3];
};
typedef union _GLKVector3 GLKVector3;
As you can see, it is a union of a number of types, including a simple x/y/z struct and an indexable array of floats — so once you create one you can conveniently access its value in many different ways as makes sense in your context(s).
Besides a simple GLKVector3Make, there's a number of geometric calculation helpers, e.g. GLKVector3Length and GLKVector3DotProduct and and GLKVector3Lerp.
This one seems like a nice one to use if you don't mind pulling in GLKit.
SpriteKit
Apple's SpriteKit defines a vector_float3, which is a bit messier:
typedef __attribute__((__ext_vector_type__(3))) float vector_float3;
This uses a compiler extension (Clang and perhaps GCC too?) to define this as a SIMD-friendly type. But it doesn't seem to be particularly developer-friendly other than that.
CoreLocation
I'm guessing this isn't a good choice in most cases, but Apple's CoreLocation provides a specialized multidimensional type for locations. CLLocation has properties for latitude/longitude/altitude (in addition to a timestamp, accuracy values, speed, course, etc.) So you could use this for 3D data, but only recommended if your data's relative to the earth's surface!
I have three questions surrounding what I think is the topic of structs in obj-c
1) Why is it that I often (or always) cannot take a member var that is a CGPoint or a CGRect and set the values one by one? I find I have to do:
CGPoint point;
point.x = someValue;
point.y = someOtherValue;
obj.myPoint = point;
instead of simply obj.myPoint.x = someValue etc.
2) Is this behavior that is consistent across all structs in obj-c?
3) Is there an easy way to add two CGPoints? It seems like there should already be, but I couldn't find one. I thought it'd be cumbersome if I'd have to use a temporary CGPoint to accumulate values between two CGPoints before setting the dest var to the temp var (because of not being able to just do pointA.x += pointB.x (same for y).
1) From #sb in an answer to Cocoa Objective-c Property C structure assign fails
That won't accomplish anything, because [t member] returns a struct, which is an "r-value", ie. a value that's only valid for use on the right-hand side of an assignment. It has a value, but it's meaningless to try to change that value.
Basically you just have to live with the fact that you can't set the fields of struct directly when returned from a function.
2) Yes
3) Unfortunately I don't think there is a built-in convenience method for adding two CGPoint. If you find your self doing this frequently you can make your own:
CGPoint CGPointAdd(CGPoint p1, CGPoint p2)
{
return CGPointMake(p1.x + p2.x, p1.y + p2.y);
}
and then use it like:
obj.pointA = CGPointAdd(obj.pointA, pointB);
not as elegant as obj.pointA.X += ... but sometimes life isn't fair.
This is perfectly normal. The 'obj' owns that var and has getters and setters, you can not modify parts of that variable.
The best thing to do is copy the struct and modify whatever you need.
Also note you can use the CGPointMake(x, y) function (and the same for all CG structs), which is much easier.
To update this is easiest:
CGPoint point = obj.myPoint;
point.x += 10.0f;
obj.myPoint = point;
Obj-C 2.0 hides the getters and setters which looks like this:
CGPoint point = [obj myPoint];
point.x += 10.0f;
[obj setMyPoint:point];
I want to make an method which takes an CGFloat by reference.
Could I do something like this?
- (void)doStuff:(CGFloat*)floatPointer
I guess this must look different than other object pointers which have two of those stars. Also I'm not sure if I must do something like:
- (void)doStuff:(const CGFloat*)floatPointer
And of course, no idea how to assign an CGFloat value to that floatPointer. Maybe &floatPointer = 5.0f; ?
Could someone give some examples and explain these? Would be great!
objective-c is still c, so
-(void) doStuff (CGFloat *) f
{
*f = 1.2;
}
call with
CGFloat f = 1.0;
[self doStuff:&f];
If you are passing a CGFloat by reference, then accessing it is simple:
- (void)doStuff:(CGFloat*)floatPointer {
*floatPointer = 5.0f;
}
Explanation: as you are getting a reference, you need to de-reference the pointer (with the *) to get or set the value.
if you (hate pointers and ;-) prefer objective-c++ pass by reference, the following is an alternative:
-(void) doStuffPlusPlus:(CGFloat &) f
{
f = 1.3;
}
call by
CGFloat abc = 1.0;
[self doStuffPlusPlus:abc];
and, you need to rename the source filename from ???.m to ???.mm