How do I add a CGPoint to NSMutableArray? - iphone

I want to store my CGPoint to the NSMutable Array, so , I have method like this:
[self.points addObject:CGPointMake(x, y)];
But I got the error, it said that :
Incompatible type for argument 1 of
"addObject".
So, I check out the API,
- (void)addObject:(id)anObject
anObject The object to add to the end
of the receiver's content. This value
must not be nil.
So, I think the "CGPointMake" can make a Object, but it can't be assigned. What happens?

The problem is that CGPoint is actually just a C structure it is not an object:
struct CGPoint {
CGFloat x;
CGFloat y;
};
typedef struct CGPoint CGPoint;
If you are on the iPhone you can use the NSValue UIKit additions to convert the CGPoint to an NSValue object.
See this previous answer for examples: How can I add CGPoint objects to an NSArray the easy way?

You can also do the following:
[myArray addObject:[NSValue valueWithCGPoint:MyCGPoint]];

Unfortunately for you a CGPoint isn't an Objective-c object. It is a c struct. if you Apple double click on CGPoint you should jump to the definition
struct CGPoint {
CGFloat x;
CGFloat y;
};
typedef struct CGPoint CGPoint;
If you want to store CGPoint in an NSArray you will need to wrap them first. You can use NSValue for this or write your own wrapper.
see Converting a CGPoint to NSValue
EDIT> There is a small overhead for each objective-c method call, and creating and destroying objects involves many method calls before they are even used for anything. You shouldn't worry about this normally but for very small objects which encapsulate little behaviour and that have short lifetimes it can affect performance. If Apple used objects for all points, rect, sizes and even ints, floats, etc performance would be worse.

To build on the answer given by atbreuer11, you can convert your CGPoint to NSValue, store it in NSMutableArray and convert it back using the following:
//Convert CGPoint and Store it
CGPoint pointToConvert = CGPointMake(100.0f, 100.0f);
NSValue *valueToStore = [NSValue valueWithCGPoint:pointToConvert];
NSMutableArray *arrayToKeep =[NSMutableArray arrayWithObject:valueToStore];
Then restore it again:
CGPoint takeMeBack;
for (NSValue *valuetoGetBack in arrayToKeep) {
takeMeBack = [valuetoGetBack CGPointValue];
//do something with the CGPoint
}
That's probably the easiest way to do it. You can write a complete class and do all types of data manipulation, but I think it would be an overkill, unless you really have to.
EDIT
For Swift 5 (I'm not sure why one would want to do this, given that we can use literal arrays nowadays, but here goes):
Save Values:
let somePoint = CGPoint(x: 200, y: 400)
let array = NSMutableArray(array: [somePoint])
To retrieve it:
let points = array.compactMap({ ($0 as? NSValue)?.cgPointValue })

Swift 3.x
// Convert CGPoint to NSValue
let cgPoint = CGPoint(x: 101.4, y: 101.0)
let nsValue = NSValue(cgPoint: cgPoint)
var array = NSArray(object: nsValue)
// Restore it again
var cgPoint : CGPoint!
for i in array {
cgPoint = i as? CGPoint
}

A simple way to handle CGPoint (or any other non NSObject inherited structure) is to create a new class inherited from NSObject.
The code is longer, but clean. An example is below:
In .h file:
#interface MyPoint:NSObject
{
CGPoint myPoint;
}
- (id) init;
- (id) Init:(CGPoint) point;
- (BOOL)isEqual:(id)anObject;
#end
In .m file:
#implementation MyPoint
- (id) init
{
self = [super init];
myPoint = CGPointZero;
return self;
}
- (id) Init:(CGPoint) point{
myPoint.x = point.x;
myPoint.y = point.y;
return self;
}
- (BOOL)isEqual:(id)anObject
{
MyPoint * point = (MyPoint*) anObject;
return CGPointEqualToPoint(myPoint, point->myPoint);
}
#end
Here is some code sample showing the usage, do not forget to release!!!
//init the array
NSMutableArray *pPoints;
pPoints = [[NSMutableArray alloc] init];
// init a point
MyPoint *Point1 = [[MyPoint alloc]Init:CGPointMake(1, 1)];
// add the point to the array
[pPoints addObject:[[MyPoint alloc] Point1]];
//add another point
[Point1 Init:CGPointMake(10, 10)];
[pPoints addObject:[[MyPoint alloc] Point1]];
[Point1 Init:CGPointMake(3, 3)];
if ([pPoints Point1] == NO))
NSLog(#"Point (3,3) is not in the array");
[Point1 Init:CGPointMake(1, 1)];
if ([pPoints Point1] == YES))
NSLog(#"Point (1,1) is in the array");

Related

CGPoints, NSValues, NSMutableArray and the ID Type

I am trying to store CGPoints from UITouches in an Array. From reading other threads I have realized that using NSValue seems to be the best way to do this.
Currently I have:
NSMutableArray *dotsArray;
NSValue *pointObj;
UITouch *touch = obj;
CGPoint touchPoint = [touch locationInView:self.view];
// Need to store the CGPoints, can't store directly into an array // as it is a C struct // so we use NSValue
// CGPoint converted to NSValue
CGPoint point = touchPoint;
pointObj = [NSValue valueWithCGPoint:point];
dotsArray = [NSArray arrayWithObjects:pointObj,nil];
// Print to console the objects in the collection
NSLog(#"array content: %#", dotsArray);
NSLog(#"[dotsArray count] = %i",[dotsArray count]);
// Restore from NSValue to C structures
CGPoint pointRestored = [pointObj CGPointValue];
I want to be able to use:
[dotsArray insertObject:pointObj atIndex:0];
in place of
dotsArray = [NSArray arrayWithObjects:pointObj,nil];
so that I can increment the placing as I recall the method to store multiple points.
From reading I think this has something to do with the ID type required for the 'insertObject', but I don't fully understand it and am unable to find a solution online.
EDIT: When I use
[dotsArray insertObject:pointObj atIndex:0];
then
NSLog(#"array content: %#", dotsArray);
outputs NULL
Use addObject: method.
[dotsArray addObject:pointObj];
I think it will be helpful to you.
The method which you want to use [dotsArray insertObject:pointObj atIndex:0]; refers to the id of the array that is the array position. This means atIndex:0 is the first position in your array as the array starts from Zeroth position. So now if you put this in your DotsArray , the index which you want to call or the ID type is the value at the point you want to show.

How to assign different data types to NSMutableData

I'm trying to create a vertex attribute array for storing vectors and indices in a mesh. I realise that vertex attributes can be made up from vector2, vector3 and/or vector4 so i'd like to create a class that caters for them all without constraining methods to return a certain type of object. As a test, i've written the following class making use of NSMutableData:
//VertexAttributeArray.h
#interface VertexAttributeArray : NSObject
{
NSMutableData *vertexData;
}
- (void *)data;
- (CGRect *)rect;
//VertexAttributeArray.m
- (id)init
{
self = [super init];
if(self)
{
vertexData = [[NSMutableData alloc] initWithLength:0];
}
return self;
}
- (void *)data
{
return [vertexData mutableBytes];
}
- (CGRect *)rect
{
return [vertexData mutableBytes];
}
- (void)dealloc
{
if(vertexData)
{
[vertexData release];
vertexData = nil;
}
[super dealloc];
}
From within my mesh class, i'd like to be able to call something like:
VertexAttributeArray *vertexAttributeArray = [[VertexAttributeArray alloc] init];
[vertexAttributeArray data][0] = CGRectMake(0.0, 0.0, 0.0, 0.0); //doesn't work - compiler error
[vertexAttributeArray rect][0] = CGRectMake(0.0, 0.0, 0.0, 0.0); //works fine
The first method accessing data would be preferred as this doesn't constrain the class to work with a specific type of struct but Xcode just throws the warning "Incomplete type 'void' is not assignable."
I don't want to have multiple methods for different types of data structures but I can't see any way to get around this currently. Having methods that return pointers to different types doesn't look very elegant and adds a lot of vestigial unused (and unwanted) code.
Is there a way to work around this to create an agnostic data store? I'd prefer for my mesh class to have different VertexAttributeArray objects to store vertices, indices, normals and colours etc so storing vector2, vector3 and vector4 is ideally where i'd like to be. How would I work around achieving this?
There is no way to do what you want. It's not known at compile-time what size the objects inside data is. ((void *)foo)[x] would be illegal in C as well. The compiler must know what struct is stored inside the array for you to be able to index it.
You can change the call to:
((CGRect *)[vertexAttributeArray data])[0] = CGRectMake(0.0, 0.0, 0.0, 0.0);
if you prefer, since then it's known that there is sizeof(CGRect) distance between the objects inside data.

How to store not id type variable use a objc_getAssociatedObject/objc_setAssociatedObject?

I use a category, and some variable stored in a UIView.
but only stored id type, so I really want none id type (int, float, double, char... and so on)
How to write a code?
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#interface UIView (CountClip)
#property (nonatomic, copy) NSString *stringTag;
#property (nonatomic) float offsetY;
#end
#import "UIView+CountClip.h"
#implementation UIView (CountClip)
static NSString *kStringTagKey = #"StringTagKey";
- (NSString *)stringTag
{
return (NSString *)objc_getAssociatedObject(self, kStringTagKey);
}
- (void)setStringTag:(NSString *)stringTag
{
objc_setAssociatedObject(self, kStringTagKey, stringTag, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (float)offsetY
{
// how to write a correct code?
return objc_getAssociatedObject(self, <#what is a code?#>);
}
- (void)setOffsetY:(float)offsetY
{
// how to write a correct code?
objc_setAssociatedObject(self, <#what is a code?#>,...);
}
#end
You need to convert floats to NSNumber instances and vice versa:
static NSString *kOffsetYTagKey = #"OffsetYTagKey";
- (float)offsetY
{
// Retrieve NSNumber object associated with self and convert to float value
return [objc_getAssociatedObject(self, kOffsetYTagKey) floatValue];
}
- (void)setOffsetY:(float)offsetY
{
// Convert float value to NSNumber object and associate with self
objc_setAssociatedObject(self, kOffsetYTagKey, [NSNumber numberWithFloat:offsetY], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
To store variables such as CGPoint, CGRect or CGSize, have a look at NSValue class and use appropriate method:
NSValue *value;
value = [NSValue valueWithCGPoint:point];
value = [NSValue valueWithCGRect:rect];
value = [NSValue valueWithCGSize:size];
Then convert it back via:
NSValue *value = ...;
CGPoint point = value.CGPointValue;
CGRect rect = value.CGRectValue;
CGSize size = value.CGSizeValue;
FYI:
A much faster way of setting and getting associated objects could be this:
objc_setAssociatedObject(self, #selector(stringTag), stringTag, OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_getAssociatedObject(self, #selector(stringTag));
This way you don't have to create the kStringTagKey variable.

iphone why is he using valueForKeyPath

I found some mapkit code on the internet that looks like this:
- (void)recenterMap {
NSArray *coordinates = [self.mapView valueForKeyPath:#"annotations.coordinate"];
CLLocationCoordinate2D maxCoord = {-90.0f, -180.0f};
CLLocationCoordinate2D minCoord = {90.0f, 180.0f};
for(NSValue *value in coordinates) {
CLLocationCoordinate2D coord = {0.0f, 0.0f};
[value getValue:&coord];
if(coord.longitude > maxCoord.longitude) {
maxCoord.longitude = coord.longitude;
}
--omitted the rest
I want to know why valueForKeypath is used to get the coordinate data, instead of just
[self.mapView.annotations]
Has it something to do with speed?
He's using the valueForKeyPath: construct to return an array of all the coordinates for all of the annotations.
Assuming that self.mapView is an MKMapView then it has an annotations property which returns an array of objects conforming to the MKAnnotation protocol. That means that every object in that array should implement the coordinate property. By issuing a call to [self.mapView valueForKeyPath:#"annotations.coordinate"] he is immediately getting an array of all of the coordinates without having to iterate over each individual annotation item in the array.
Without using the KVC construct here he'd have to do something similar to this:
NSMutableArray *coordinates = [NSMutableArray array];
for (id<MKAnnotation> annotation in self.mapView.annotations)
[coordinates addObject:annotation.coordinate];
In all, it just makes for simpler, easier to read code.

Simple layer problem [iphone]

Basically I'm sorting through objects in an array by y value (The furthest down the page is at the start of the array) but I'm having a bit of trouble. I assign all of the UIImageViews in an array a value:
for (UIImageView *Blocky in objectsArray){
[Blocky.layer setValue:[NSString stringWithFormat: #"%f",
Blocky.center.y] forKey:#"value"];
}
Because it's a UIImageView I have to put layer after "Blocky" otherwise I get the following error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[<UIImageView 0x3d44880>
setValue:forUndefinedKey:]: this class
is not key value coding-compliant for
the key value.'
My problem is when I'm sorting it I don't know where to put the ".layer" so I get the same problem because UIImageViews can't deal with keys on there own. Here is my sorting code:
NSSortDescriptor *sortDescriptor =
[[NSSortDescriptor alloc] initWithKey:#"value" ascending:YES];
[objectsArray sortUsingDescriptors:[NSArray
arrayWithObject:sortDescriptor]];
[sortDescriptor release];
Thanks in advance,
Ozzie
Use the NSMutableArray sortUsingFunction:context: method:
// top of file
static NSInteger compareImageHeights(id image1, id image2, void* context) {
CGFloat temp = ((UIImageView*)image2).center.y - ((UIImageView*)image1).center.y;
return (temp>0)?NSOrderedAscending:(temp<0)?NSOrderedDescending:NSOrderedSame;
}
// within file somewhere
[objectsArray sortUsingFunction:compareImageHeights context:NULL];
The reason the function is required (and you can't just use sortUsingSelector: or sortUsingSortDescriptor:) is that you can't specify a KVC key to identify just the y component only as a sort variable.
Edit: Changed center.y to size.height.
Edit: Oops. Changed size.height back to center.y. Fixed type to UIImageView rather than UIImage. It seems KVC objects can in theory support arbitrary keys, but some will and some won't.
Are you sure CALayer is supposed to accept arbitrary KVC keys? This looks wrong to me.
Shouldn't you extract the heights to a buffer of structures and qsort that?
eg
I think it would be something like this:
//
// before #implementation sections
//
typedef struct __MySortEntry {
UIImageView* image;
CGFloat height;
} MySortEntry, *PMySortEntry;
static int compareMySortEntry (const void * a, const void * b)
{
CGFloat temp =( ((PMySortEntry)a)->height - ((PMySortEntry)b)->height );
return (temp<0)?-1:(temp>0)?1:0;
}
//
// in #implementation section somewhere
//
NSMutableData* mutable = [[NSMutableData alloc] initWithLength:
sizeof(MySortEntry)*objectsArray.count];
PMySortEntry entries = (PMySortEntry)mutable.mutableBytes;
for (int c = 0; c < objectsArray.count; c++) {
entries[c].image = (UIImageView*)[objectsArray objectAtIndex:c];
entries[c].height = entries[c].image.center.y;
}
qsort(entries, objectArray.count, sizeof(MySortEntry), compareMySortEntry);
for (int c=0; c < objectArray.count; c++) {
UIImage* imageInSequence = entries[c].image;
// do something with images **in sequence**
}
[mutable release];
Edit: Changed center.y to size.height.
Edit: Changed size.height back to center.y. Oops.
Edit: Changed UIImage to UIImageView.
Why not set the tag property on each view as you create them based on the height and retrieve them by tag?