12

For my application I am trying to store CGRect objects into an NSMutableArray. It is loading well and printing in the log statement, but trying to take the CGRects from the array shows an error. Here is a code snippet:

CGRect lineRact = CGRectMake([[attributeDict objectForKey:@"x"] floatValue], 
  [[attributeDict objectForKey:@"y"] floatValue], 
  [[attributeDict objectForKey:@"width"] floatValue], 
  [[attributeDict objectForKey:@"height"] floatValue]);

  [lineRactangle addObject:NSStringFromCGRect(lineRact)];

How can I get the rects back from the array?

jscs
  • 63,694
  • 13
  • 151
  • 195
ajay
  • 3,245
  • 4
  • 31
  • 59

7 Answers7

42

A CGRect is a struct, not an object, and thus cannot be stored in NSArrays or NSDictionaries. You can turn it into a string and turn that string back into a CGRect, but the best way is to encapsulate it via an NSValue:

NSValue *myValue = [NSValue valueWithCGRect:myCGRect];

You can then store this NSValue object in arrays and dictionaries. To turn it back into a CGRect, you'd do:

CGRect myOtherCGRect = [myValue CGRectValue];
DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • or today, `NSValue *myValue = [NSValue valueWithRect:myCGRect];` – Fitter Man Feb 24 '15 at 14:54
  • 1
    @FitterMan: This only applies to OS X, not iOS; `valueWithRect:` isn't available on iOS. But even on OS X, you shouldn't do that: `valueWithRect:` expects an `NSRect`, not a `CGRect`. In 64-bit builds, `NSRect` is a typedef of `CGRect` so it works there without problems, but for 32-bit builds without the `NS_BUILD_32_LIKE_64` define being set to `1`, you'll get a warning/error since they're defined as two separate structs there. Even though their memory representation is the same, the compiler (correctly) treats them as being two different things there. – DarkDust Feb 24 '15 at 15:15
22
[lineRactangle addObject:[NSValue valueWithCGRect:lineRect]];
Luke
  • 11,426
  • 43
  • 60
  • 69
Kirby Todd
  • 11,254
  • 3
  • 32
  • 60
7

Use NSValue to wrap CGRect thus store them in NSArrays.

For example:

CGRect r = CGRectMake(1,2,3,4);
NSValue *v = [NSValue valueWithCGRect:rect];
NSArray *a = [NSArray arrayWithObject:v];
CGRect r2 = [[a lastObject] CGRectValue];

See documentation for the other supported structures.

freespace
  • 16,529
  • 4
  • 36
  • 58
2

Actually, I don't think any of the answers thus far really address the question ajay asked. The short answer is: You need to supply CGRectMake with the intValue, rather than the floatValue of the dictionary item. If you need to do this for several CGRects, here's a suggested method:

- (CGRect) NSArrayToCGRect: (NSDictionary *) attributeDict
{
    int x = [[attributeDict objectForKey:@"x"] intValue];
    int y = [[attributeDict objectForKey:@"y"] intValue];
    int w = [[attributeDict objectForKey:@"width"] intValue];
    int h = [[attributeDict objectForKey:@"height"] intValue];

    return CGRectFromString([NSString stringWithFormat: @"{{%d,%d},{%d,%d}}", x, y, w, h]);
}

There may be a more elegant way to accomplish this, but the above code does work.

mpemburn
  • 2,776
  • 1
  • 35
  • 41
2

If your object can be set with ".frame" you could use:

// {{CGFloat x,CGFloat y}, {CGFloat width,CGFloat height}}
NSString *objectCoords = @"{{116,371},{85,42}}";
myObject.frame = CGRectFromString(objectcoords);

or for multiple objects:

NSArray *objectCoords = [NSArray arrayWithObjects:
                         @"{{116,371},{85,42}}",
                         @"{{173,43},{85,42}}",
                         @"{{145,200},{85,42}}",
                         nil];

myObject1.frame = CGRectFromString([objectCoords objectAtIndex:0]);
myObject2.frame = CGRectFromString([objectCoords objectAtIndex:1]);
myObject3.frame = CGRectFromString([objectCoords objectAtIndex:2]);
0

CGRect is a struct you cannot put it in an NSArray. You can only add objects to it.

Nick Weaver
  • 47,228
  • 12
  • 98
  • 108
0

or something more 'extreme'... creating an array that holds arrays of CGRect(s)

movPosTable = [[NSArray alloc] initWithObjects:
            [[NSArray alloc] initWithObjects: [NSValue valueWithCGRect:[GridAB frame]], [NSValue valueWithCGRect:[GridBA frame]], [NSValue valueWithCGRect:[GridBB frame]], nil],
            [[NSArray alloc] initWithObjects: [NSValue valueWithCGRect:[GridAA frame]], [NSValue valueWithCGRect:[GridAC frame]], [NSValue valueWithCGRect:[GridBA frame]], [NSValue valueWithCGRect:[GridBB frame]], [NSValue valueWithCGRect:[GridBC frame]], nil],
            [[NSArray alloc] initWithObjects: [NSValue valueWithCGRect:[GridAB frame]], [NSValue valueWithCGRect:[GridBB frame]], [NSValue valueWithCGRect:[GridBC frame]], nil], nil];

where 'GridAA', 'GridAB' etc. correspond to UIViews

geolos
  • 1
  • This addendum should have better been added as a comment to the referred answer because one could not see it as a solution and it seems to be a comment. – iOS Oct 28 '12 at 09:59