skshapenode adding two nodes? - ios

I'm not sure whether there's an issue with the implementation or the way I'm using it.
However, it's presenting over 2,400 nodes when it should be ~ 1,250
-(void)drawWeb {
//get distance of 50 across
int distanceMargin = _background.frame.size.width/50;
NSLog(#"%i", distanceMargin);
__block int xCounter = distanceMargin;
__block int yCounter = 0;
NSArray *alphabet = [[NSArray alloc] initWithObjects:#"A",#"B",#"C",#"D",#"E",#"F",#"G",#"H",#"I",#"J",#"K",#"L",#"M",#"N",#"O",#"P",#"Q",#"R",#"S",#"T",#"U",#"V",#"W",#"X",#"Y",#"Z", nil];
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 50; j++) {
webPoint *shape = [webPoint shapeNodeWithCircleOfRadius:1];
shape.position = CGPointMake(xCounter, _background.frame.size.height - yCounter);
shape.fillColor = [UIColor blackColor];
shape.alpha = 1;
shape.webPoint = [NSString stringWithFormat:#"%#%i", alphabet[i], j];
shape.positionX = xCounter;
shape.positionY = yCounter;
shape.name = #"webPoint";
[_background addChild:shape];
xCounter = xCounter + distanceMargin;
}
xCounter = distanceMargin;
yCounter = yCounter + distanceMargin;
}
}

By default when creating SKShapeNode, strokeColor is already set and it requires 1 node + 1 draw call. In your example you are setting fillColor too, which requires additional node and additional draw pass.
SKShapeNode is not performant solution in many cases (it should be used sparingly), and in your case, if you enable debugging labels you will probably see that there are a lot of draw calls required for scene rendering. Debugging labels can be enabled in view controller (showsDrawCount = YES)
Draw calls are directly affecting on performance in SpriteKit applications and you should try to keep that number as low as possible. One way would be using SKSpriteNode and texture atlases.

Related

How to add physicsBody to tiles generated by JSTileMap and change them into one physicsBody

I use JSTileMap to draw a level. I changed it a little bit to add SKPhysicsBody to every tile but sometimes when I apply some impulse to main character and he hits the wall/ground/ceiling he is acting weird. He reflects from surfaces with the way that is contrary to the principles of physics. I think that it happens because the player hits the point where two physics bodies (for example two physics bodies of the ground) connects.
SKPhysicsBody class provides a method to create one physics body from different physics bodies + (SKPhysicsBody *)bodyWithBodies:(NSArray *)bodies; Can I use this method to create one physics body from ale the tiles physics bodies?
Here's a method from JSTileMap where I add physics bodies:
+(id) layerWithTilesetInfo:(NSArray*)tilesets layerInfo:(TMXLayerInfo*)layerInfo mapInfo:(JSTileMap*)mapInfo
{
TMXLayer* layer = [TMXLayer node];
layer.map = mapInfo;
layer.tilesByColumnRow = [NSMutableDictionary dictionary];
// basic properties from layerInfo
layer.layerInfo = layerInfo;
layer.layerInfo.layer = layer;
layer.mapTileSize = mapInfo.tileSize;
layer.alpha = layerInfo.opacity;
layer.position = layerInfo.offset;
// recalc the offset if we are isometriic
if (mapInfo.orientation == OrientationStyle_Isometric)
{
layer.position = CGPointMake((layer.mapTileSize.width / 2.0) * (layer.position.x - layer.position.y),
(layer.mapTileSize.height / 2.0) * (-layer.position.x - layer.position.y));
}
NSMutableDictionary* layerNodes = [NSMutableDictionary dictionaryWithCapacity:tilesets.count];
//MY CODE
NSMutableArray *arrayOfSprites = [[NSMutableArray alloc] init];
SKNode *theSprite;
//----||----
// loop through the tiles
for (NSInteger col = 0; col < layerInfo.layerGridSize.width; col++)
{
for (NSInteger row = 0; row < layerInfo.layerGridSize.height; row++)
{
// get the gID
NSInteger gID = layerInfo.tiles[col + (NSInteger)(row * layerInfo.layerGridSize.width)];
// mask off the flip bits and remember their result.
bool flipX = (gID & kTileHorizontalFlag) != 0;
bool flipY = (gID & kTileVerticalFlag) != 0;
bool flipDiag = (gID & kTileDiagonalFlag) != 0;
gID = gID & kFlippedMask;
// skip 0 GIDs
if (!gID)
continue;
// get the tileset for the passed gID. This will allow us to support multiple tilesets!
TMXTilesetInfo* tilesetInfo = [mapInfo tilesetInfoForGid:gID];
[layer.tileInfo addObject:tilesetInfo];
if (tilesetInfo) // should never be nil?
{
SKTexture* texture = [tilesetInfo textureForGid:gID];
SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:texture];
sprite.name = [NSString stringWithFormat:#"%ld",(long)(col + row * layerInfo.layerGridSize.width)];
// make sure it's in the right position.
if (mapInfo.orientation == OrientationStyle_Isometric)
{
sprite.position = CGPointMake((layer.mapTileSize.width / 2.0) * (layerInfo.layerGridSize.width + col - row - 1),
(layer.mapTileSize.height / 2.0) * ((layerInfo.layerGridSize.height * 2 - col - row) - 2) );
}
else
{
sprite.position = CGPointMake(col * layer.mapTileSize.width + layer.mapTileSize.width/2.0,
(mapInfo.mapSize.height * (tilesetInfo.tileSize.height)) - ((row + 1) * layer.mapTileSize.height) + layer.mapTileSize.height/2.0);
}
// flip sprites if necessary
if(flipDiag)
{
if(flipX)
sprite.zRotation = -M_PI_2;
else if(flipY)
sprite.zRotation = M_PI_2;
}
else
{
if(flipY)
sprite.yScale *= -1;
if(flipX)
sprite.xScale *= -1;
}
// add sprite to correct node for this tileset
SKNode* layerNode = layerNodes[tilesetInfo.name];
if (!layerNode) {
layerNode = [[SKNode alloc] init];
layerNodes[tilesetInfo.name] = layerNode;
}
//adding physicsbody to every tile
//sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(sprite.frame.size.width, sprite.frame.size.height)];
//sprite.physicsBody.dynamic = NO;
//sprite.physicsBody.categoryBitMask = mapCategory;
//[arrayOfSprites addObject:sprite.physicsBody];
[layerNode addChild:sprite];
NSUInteger indexes[] = {col, row};
NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2];
[layer.tilesByColumnRow setObject:sprite forKey:indexPath];
//MY CODE
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size];
sprite.physicsBody.dynamic = NO;
[arrayOfSprites addObject:sprite.physicsBody];
//-----||------
#ifdef DEBUG
// CGRect textRect = [texture textureRect];
// NSLog(#"atlasNum %2d (%2d,%2d), gid (%d,%d), rect (%f, %f, %f, %f) sprite.pos (%3.2f,%3.2f) flipx%2d flipy%2d flipDiag%2d", gID+1, row, col, [tilesetInfo rowFromGid:gID], [tilesetInfo colFromGid:gID], textRect.origin.x, textRect.origin.y, textRect.size.width, textRect.size.height, sprite.position.x, sprite.position.y, flipX, flipY, flipDiag);
#endif
}
}
}
//MY CODE
NSArray *array = [arrayOfSprites copy];
theSprite = [SKNode node];
theSprite.physicsBody = [SKPhysicsBody bodyWithBodies:array];
theSprite.physicsBody.dynamic = NO;
theSprite.position = CGPointMake(layer.position.x+16, layer.position.y+16);
[layer addChild:theSprite];
//-----||------
// add nodes for any tilesets that were used in this layer
for (SKNode* layerNode in layerNodes.allValues) {
if (layerNode.children.count > 0) {
[layer addChild:layerNode];
}
}
[layer calculateAccumulatedFrame];
return layer;
}
After adding physics bodies to every tile and adding those physics bodies to NSMutableArray I assign a copy of this NSMutableArray to the NSArray and try to create one physics body out of it like this:
//MY CODE
NSArray *array = [arrayOfSprites copy];
theSprite = [SKNode node];
theSprite.physicsBody = [SKPhysicsBody bodyWithBodies:array];
theSprite.physicsBody.dynamic = NO;
theSprite.position = CGPointMake(layer.position.x+16, layer.position.y+16);
[layer addChild:theSprite];
//-----||------
In result, one physics body with the height and width of one tile is added.
If you want to use [SKPhysicsBody bodyWithBodies:array], then you need to make sure that all the bodies in the array are relative to the parent node.
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size]; means that your physics body position is relative to the sprite node. You need the body relative to the parent node.
The only way I know how to do this is with center:
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size center:sprite.position];
This should place the SKPhysicsBody at the location of the sprite when it is added to the parent node.

CCScrollView scroll and touch events never firing

I can't find any helpful tutorials or explanation on how to use a CCScrollView. I have a grid-layout of sprites and labels (listing achievements for an iOS game). There are more than can fit on the screen so I want the user to be able to scroll.
To scroll, the user would swipe/pan upwards, to reveal the sprites etc which are lower.
I've found a few code samples and they seem to indicate you just need to add your content node to the scroll node and it will take care of the rest.
It doesn't seem to work. There's no scroll, and the pan/touch events on the scroll layer never seem to fire. The close button I have at the same child (sibling to the scroll view) no longer works as well.
I'm not using SpriteBuilder.
// Node to hold all sprites/labels
scrollContents = [CCNode node];
// I add a bunch of sprites/labels in a grid view
for( NSString *key in badgeKeys ){
// logic to load the sprite would be here
CCSprite *badge = [CCSprite spriteWithSpriteFrame:frame];
badge.positionType = CCPositionTypeNormalized;
badge.position = ccp(xPos,yPos);
[scrollContents addChild:badge];
// some logic to increment x/y position logic, for grid layout
}
// Scroll view
scrollView = [[CCScrollView alloc] initWithContentNode:scrollContents];
scrollView.horizontalScrollEnabled = NO;
scrollView.verticalScrollEnabled = YES;
[scrollView setBounces:NO];
// My sprites never even show unless I manually set this
scrollContents.contentSize = CGSizeMake(self.contentSize.width,960);
NSLog(#"scrollContents contentsize: %f %f", scrollContents.contentSize.width,scrollContents.contentSize.height);
[self addChild:scrollView];
ok, here is a working example (i deconstructed part of my code to give you a fully working code sample) of a scrolling menu with 'live' buttons inside. I just tested this 'deconstruction' , it works
- (void) scrollingMenuWithCharmsTest {
// setup something to scroll
GameInventory *gi = [GameInventory sharedGameInventory];
while (gi.armorCharms.count < 20) {
[gi addArmorCharm:[ArmorCharm createRandomArmorCharm]];
}
CCNode *contentNode = [self charmsContentNodeFor:gi.armorCharms
showEquiped:NO
spacingBetweenMenuItems:8
target:self
selector:#selector(onArmorCharmSelected:)];
// setup a clipping node to crop out the CCScrollingMenu
CCNodeColor *ccn = [CCNodeColor nodeWithColor:[CCColor blackColor] width:180 height:200];
ccn.anchorPoint = ccp(0, 0);
CCClippingNode *cn = [CCClippingNode clippingNodeWithStencil:ccn];
cn.alphaThreshold = 0.05f;
[self addChild:cn];
cn.inverted = NO;
cn.positionInPointsV = ccp(50, 50);
cn.anchorPoint = ccp(0, 0);
cn.contentSizeInPoints = CGSizeMake(180, 200);
// setup scrolling menu
CCScrollView * bsm = [[CCScrollView alloc] initWithContentNode:contentNode];
bsm.contentSize=CGSizeMake(180,200);
[cn addChild:bsm];
bsm.position = ccp(0, 0);
bsm.bounces = YES;
bsm.pagingEnabled = NO;
bsm.verticalScrollEnabled = YES;
bsm.horizontalScrollEnabled = NO;
bsm.contentSizeInPoints = CGSizeMake(180, 200); // inPoints only after the node has a parent
for (CharmAbstractBoxMenuItem *lmi in bsm.contentNode.children) {
TRACE(#"item %# is at %#", lmi.item.description, NSStringFromCGPoint(lmi.positionInPointsV));
}
TRACE(#"number of pages : %i", bsm.numVerticalPages);
}
- (CCNode *)charmsContentNodeFor:(NSDictionary *)keyedItems
showEquiped:(BOOL)isShowEquiped
spacingBetweenMenuItems:(float)inSpacing
target:(id)inTarget
selector:(SEL)inSelector {
NSSortDescriptor *sortOrder = [NSSortDescriptor sortDescriptorWithKey:#"self" ascending:YES];
NSArray *sortedKeys = [[keyedItems allKeys] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortOrder]];
float initialY = 0;
float currentY = initialY;
NSUInteger itemNumber = 0;
CGFloat width = 0;
CGFloat height = 0;
CCNode *contentNode = [CCNode node];
for (NSUInteger loopi = 0; loopi < [sortedKeys count]; loopi++) {
NSString *key = [sortedKeys objectAtIndex:loopi];
CharmAbstract *ci = [keyedItems objectForKey:key];
if (ci) {
CharmAbstractBoxMenuItem *cmi = [CharmAbstractBoxMenuItem itemBoxFor:ci
target:inTarget
selector:inSelector
];
cmi.toolTip = ci.toolTip;
cmi.position = ccp(deviceOffset(0), currentY);
cmi.key = key;
[contentNode addChild:cmi z:0 name:[NSString stringWithFormat:#"%li", (long) itemNumber]];
currentY += cmi.contentSizeInPoints.height + inSpacing;
itemNumber++;
if (cmi.contentSize.width > width) width = cmi.contentSize.width;
height += cmi.contentSize.height;
if (loopi < sortedKeys.count - 1) height += inSpacing;
}
else {
MPLOG(#"*** Key [%#] yielded no items.", key);
}
}
contentNode.contentSizeType = CCSizeTypePoints;
contentNode.contentSize = CGSizeMake(width, height);
return contentNode;
}
some notes :
i gave you my 'build content node' routine so you know the ins and outs of positions and sizes.
my charmBoxMenuItemss derive from 'CCButton' and are hot ... In the full version of this code snippet, i extended CCScrollView to prevent the buttons from being 'hot' outside the crop area (although they are cropped out from view, they are still 'visible' by default, and could respond when a random tap occurs above or below the crop area).
For clipping node with stencil, you need to add this in your setupCocos2dWithOptions line:
CCSetupDepthFormat : [NSNumber numberWithUnsignedInt:GL_DEPTH24_STENCIL8_OES]

IOS Adding random nodes in different position

I am trying to make an iPhone game and I am trying to add random images in different position.
Here is what I want to do
There is 6 different color oval nodes (enemy)
When the game starts I want there to be 5 enemy nodes.
When the player node contacts enemy node, enemy node will disappear and then right away another
enemy node will be added in different location.
But some times some nodes appear in same location so it looks like there is 4 nodes instead of 5.
If there is a node already in a specific location how can I not add another node there but some other location?
Below I added a part of the code I wrote.
It might be something very easy but I am new to programming and I could not figure that out.
Thank you,
-(void) addWaterBall {
for (int i = 0; i < 5; i++) {
NSUInteger randomWaterBall = [Util randomWithMin:0 max:8];
WaterBall *waterBall = [WaterBall waterBallOfType:randomWaterBall];
float y = self.frame.size.height - ((((self.frame.size.height/2)-10)/10) * [Util randomWithMin:1 max:10]);
float x = (self.frame.size.width/10) * [Util randomWithMin:1 max:10];
waterBall.position = CGPointMake(x, y);
waterBall.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:waterBall.size.width/2];
waterBall.physicsBody.dynamic = YES;
waterBall.physicsBody.affectedByGravity = NO;
waterBall.physicsBody.categoryBitMask = waterBallCategory;
waterBall.physicsBody.contactTestBitMask = sharkCategory ;
//waterBall.physicsBody.collisionBitMask = ;
[self addChild:waterBall];
}
}
What I would do is stick in a [self enumerateChildNodesWithName…….] after generating the random co-ordinates and compare the random x and y co-ordinates with those on each enumerated node, if they are the same or too close then generate new random co-ordinates. This is probably best done in a while loop.
-(void)addWaterBall
{
NSUInteger randomWaterBall = [Util randomWithMin:0 max:8];
WaterBall *waterBall = [WaterBall waterBallOfType:randomWaterBall];
waterBall.name = #"WaterBall";
[self enumerateChildNodesWithName:#"WaterBall" usingBlock:^(SKNode *node, BOOL *stop) {
float y = self.frame.size.height - ((((self.frame.size.height/2)-10)/10) * [Util randomWithMin:1 max:10]);
float x = (self.frame.size.width/10) * [Util randomWithMin:1 max:10];
node.position = CGPointMake(x, y);
node.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:waterBall.size.width/2];
node.physicsBody.dynamic = YES;
node.physicsBody.affectedByGravity = NO;
node.physicsBody.categoryBitMask = waterBallCategory;
node.physicsBody.contactTestBitMask = sharkCategory ;
//waterBall.physicsBody.collisionBitMask = ;
[self addChild:waterBall];
}];
}

'CALayer position contains NaN: [nan nan]' when working with Bouncing Ball Array in C4

So I'm trying to just recreate a bouncing ball app using xcode and C4. What I'm trying to do right now is re-create some of the examples in Dan Shiffmans Nature of Code book in C4.
So far I've managed to create my own class of "Mover" objects that bounce around the screen. That's all working great. The next step was to start "applying some forces" to the movers and play around with that.
I seem to be getting an exception thats crashing the program now that I've added methods to apply force. I put on exception breakpoints and this line in C4Control.m is where it stops:
if(_animationDuration == 0.0f) super.center = center;
Looking into this more, I notice that neither super.center or center properties have a value assigned to them (both of NaN). I'm not sure what's causing this? This seemed to start once I decided to set each Movers initial velocity/accel to 0 and only assign "fores" (C4Vectors for wind and gravity) to create motion in these objects. When I comment out the 2 lines that apply each force, the problem goes away.
This is the implementation of my mover class:
//Init Method
- (id)initWithPoint:(CGPoint)point andCanvasController:(C4CanvasController *)canvasController {
self = [super init];
if (self) {
canvasFrame = CGPointMake(canvasController.canvas.width,
canvasController.canvas.height);
diam = [C4Math randomInt:70];
mass = diam * 0.5f;
CGRect ellipseFrame = CGRectMake(0, 0, diam, diam);
[self ellipse:ellipseFrame];
self.lineWidth = 0.0f;
self.fillColor = [UIColor colorWithRed:[C4Math randomInt:60]/60
green:[C4Math randomInt:100]/100.0f
blue:0.9f
alpha:0.3f];
int foo = arc4random() % (int)([canvasController.canvas height]);
int bar = arc4random() % (int)([canvasController.canvas width]);
self.location = [C4Vector vectorWithX:foo
Y:bar
Z:0.0f];
self.origin = self.location.CGPoint;
self.accel = [C4Vector vectorWithX:0.00f Y:0.0f Z:0.0f];
self.vel = [C4Vector vectorWithX:0.00f Y:0.0f Z:0.0f];
}
return self;
}
//Method to calculate updated mover position
-(void)update{
[self.vel add:self.accel];
[self.location add:self.vel];
self.origin = self.location.CGPoint;
[self.accel multiplyScalar:0];
[self checkBounds];
}
//Checking if mover has reached edge of screen
-(void)checkBounds{
if (self.origin.x > canvasFrame.x - diam){
self.location.x = canvasFrame.x - diam;
self.vel.x *= -1;
}
else if (self.origin.x < 0){
self.vel.x *= -1;
self.location.x = 0;
}
if(self.origin.y > canvasFrame.y - diam){
self.location.y = canvasFrame.y - diam;
self.vel.y *= -1;
}
else if(self.origin.y < 0){
self.vel.y *= -1;
self.location.y = 0;
}
}
//Start C4Timer to run update every 1/60th of a second
- (void) run{
updateTimer = [C4Timer automaticTimerWithInterval:1/60.0f
target:self
method:#"update"
repeats:YES];
}
//Calculate a force to apply to mover
- (void)applyForce:(C4Vector *)force{
[force divideScalar:mass];
[self.accel add:force];
}
In my C4WorkSpace.m file:
#import "C4Workspace.h"
#import "Mover.h"
#implementation C4WorkSpace{
Mover *testMover;
NSMutableArray *moverArray;
}
-(void)setup {
C4Vector *gravity = [C4Vector vectorWithX:0.01f Y:0.0 Z:0.0f];
C4Vector *wind = [C4Vector vectorWithX:0 Y:0.4 Z:0];
moverArray = [[NSMutableArray alloc]init];
//Not using foo/bar variables right now, but usually use them to create CGPoint p
//int foo = arc4random() % (int)([self.canvas height]);
//int bar = arc4random() % (int)([self.canvas width]);
for (int i = 0; i < 500; i++) {
CGPoint p = CGPointMake(0,0);
Mover *m = [[Mover alloc]initWithPoint:p andCanvasController:self];
[moverArray addObject:m];
}
for (Mover *m in moverArray){
[self addShape:m];
[m run];
[m applyForce:gravity];
[m applyForce:wind];
}
}
#end
Sorry for how long this was, any and all help is much appreciated!

Putting labels in slices of donut style CPTPiePlot, they don't line up right

Im trying to draw a donut pie chart on ios with core-plot, with one chart inside of another. Each slice has the same width, but the slices are color coded. I'm trying to place a colored dot in the center of each slice, but I can't figure out how to get the position of the dots correct.
I've managed to get them roughly centered using the labelOffset property of the pie chart.(currently im using a label offset of -35, determined by trial and error). But I can't mange to get them exactly centered, and in the innermost graph they can end up far enough off that they are in the wrong slice.
I have tried setting the rectAnchor property of the text layer, and the padding, but neither one seems to have any effect.
Code for creating the plots:
CGFloat startAngle = [self degreesToRadians:90 + ((360 / [self.dangerRoseData numberOfSectors]) /2.0)];
NSInteger levels = [self.dangerRoseData numberOfLevels];
for(int i = 0; i < levels; i++){
CGFloat radiusD = (i + 1.0)/ levels;
CGFloat radius = MIN(radiusD * (graphHostingView.frame.size.height - 2 * graph.paddingLeft) / 2.8,
radiusD * (graphHostingView.frame.size.width - 2 * graph.paddingTop) / 2.8);
CPTPieChart *lastPie = [plots lastObject];
CGFloat innerRadius;
if(lastPie != nil)
innerRadius = lastPie.pieRadius;
else
innerRadius = 0;
CPTPieChart *piePlot = [[CPTPieChart alloc] init];
piePlot.backgroundColor = [UIColor clearColor].CGColor;
piePlot.dataSource = self;
piePlot.delegate = self;
piePlot.pieRadius = radius;
piePlot.pieInnerRadius = innerRadius;
piePlot.identifier = [self.dangerRoseData nameForLevel:i];
piePlot.borderLineStyle = lineStyle;
piePlot.startAngle = startAngle;
piePlot.sliceDirection = CPTPieDirectionClockwise;
piePlot.labelOffset = -35;//brute force trial and error
piePlot.labelRotationRelativeToRadius = NO;//new property after 1.0
[graph addPlot:piePlot];
[plots addObject:piePlot];
}
and for the labels:
-(CPTLayer *)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)index{
....
CPTMutableTextStyle *textStyle = [[CPTMutableTextStyle alloc] init];
textStyle.color = color;
textStyle.fontSize = 36;
CPTTextLayer *label = [[CPTTextLayer alloc] initWithText:#"•" style:textStyle];
return label;
}
I Tried updating to the newest version of core-plot, which adds a new property to CPTPiePlot labelRotationRelativeToRadius which seems like it ought to be the answer to this problem, but it's not. With the new version im not able to get the dots positioned anywhere close to the correct spot.
Am I missing something with positioning the labels?
Am I using totally the wrong approach to putting the dots in the slices?
Well, I decided to give a try using images instead of text labels, and the positioning works just fine, it doesn't seem to be at all erratic like text labels.
In order to be able to fit the dots in the inner most pie I also had to change the sizes, so instead of taking up equal amounts of the total radius, the inner pie takes twice the space of the rest.
Code for generating the plots:
NSInteger levels = [self.dangerRoseData numberOfLevels];
for(int i = 0; i < levels; i++){
CPTPieChart *lastPie = [plots lastObject];
CGFloat innerRadius;
if(lastPie != nil)
innerRadius = lastPie.pieRadius;
else
innerRadius = 0;
CGFloat ringThickness;
ringThickness = ( (graphHostingView.frame.size.width - 2 * graph.paddingTop) / ((levels + 1) * 2) ) * .85;
if(i == 0)//make the inner circle twice the thickness of the rings
ringThickness += ringThickness;
CGFloat radius = innerRadius + ringThickness;
int labelOffset = -14;
if(i == 0)
labelOffset = -15;
CPTPieChart *piePlot = [[CPTPieChart alloc] init];
piePlot.backgroundColor = [UIColor clearColor].CGColor;
piePlot.dataSource = self;
piePlot.delegate = self;
piePlot.pieRadius = radius;
piePlot.pieInnerRadius = innerRadius;
piePlot.identifier = [self.dangerRoseData nameForLevel:i];
piePlot.borderLineStyle = lineStyle;
piePlot.startAngle = startAngle;
piePlot.sliceDirection = CPTPieDirectionClockwise;
piePlot.labelOffset = labelOffset;
[graph addPlot:piePlot];
[plots addObject:piePlot];
}
and for the labels:
-(CPTLayer *)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)index
{
...
CPTImageLayer *layer = [[CPTImageLayer alloc] initWithImage:[UIImage imageNamed:filename]];
layer.anchorPoint = CGPointMake(0.5, 0.5);
return layer;
}
Note that the CPTImageLayer is an addition to the standard core-plot to reduce the complexity of using image

Resources