I have a chart with 3 SChartLineSeries. I would like to show 3 simultaneous target points (from the crosshair that corresponds to his serie) at the same time.
Like I have on this picture with dataPoints, but I would like have it on the same X position with Crosshairs.
How can I achieve that? I tried to recognize the 'long press' event in the delegate method, but I didn't figure out how to do it.
With crosshairs, I only have this:
EDIT:
Ive tried:
- (void)sChart:(ShinobiChart *)chart toggledSelectionForPoint:(SChartDataPoint *)dataPoint inSeries:(SChartSeries *)series atPixelCoordinate:(CGPoint)pixelPoint
{
for (SChartLineSeries *serie in chart.series) {
for (SChartDataPoint *dp in serie.dataSeries.dataPoints){
if (dp.xValue == dataPoint.xValue){
dp.selected = YES;
serie.crosshairEnabled = YES;
serie.style.selectedPointStyle.color = [UIColor blackColor];
serie.style.selectedPointStyle.radius = [NSNumber numberWithInt:8];
serie.style.selectedPointStyle.showPoints = YES;
}
}
}
}
And subclassing the object 'SChartCrosshair' I recognize the long press event on 'crosshairChartGotLongPressAt'.
I already managed it, with the help of ShinobiControls team support.
On the 'SChartCrosshair' I subclassed, I had to loop over the points that matched with the 'Crosshair' X position touched, and adding the ones that mathed to an array for using them in 'drawCrosshairLines'. For that, I used, the pixel version of the point, through a method that understands the SChartAxis object: 'pixelValueForDataValue'.
I draw a vertical line on the chart as well.
#implementation CustomCrossHair
-(void)drawCrosshairLines
{
CGContextRef context = UIGraphicsGetCurrentContext();
SChartDataPoint *firstDataPixelPoint = arrayPixelPoints[0];
double xFirst = [firstDataPixelPoint.xValue doubleValue];
CGContextMoveToPoint(context,xFirst, 0.f);
CGContextAddLineToPoint(context,xFirst, self.chart.canvas.glView.frame.size.height);
for (SChartDataPoint *dataPixelPoint in arrayPixelPoints){
double yVal = [dataPixelPoint.yValue doubleValue];
double xVal = [dataPixelPoint.xValue doubleValue];
CGRect rectangle = CGRectMake(xVal,yVal, 7, 7);
CGContextAddEllipseInRect(context, rectangle);
}
CGContextStrokePath(context);
}
-(void)moveToPosition:(SChartPoint)coords andDisplayDataPoint:(SChartPoint)dataPoint fromSeries:(SChartCartesianSeries *)series andSeriesDataPoint:(id<SChartData>)dataseriesPoint
{
[super moveToPosition:coords andDisplayDataPoint:dataPoint fromSeries:series andSeriesDataPoint:dataseriesPoint];
arrayPixelPoints = [NSMutableArray array];
SChartDataPoint *dataSPoint = dataseriesPoint;
int i = 0;
for (SChartLineSeries *serie in self.chart.series){
i++;
for (SChartDataPoint *dp in serie.dataSeries.dataPoints){
if ([dp.xValue doubleValue] == [dataSPoint.xValue doubleValue]){
float yPixel = [self.chart.yAxis pixelValueForDataValue:dp.yValue];
float xPixel = [self.chart.xAxis pixelValueForDataValue:dp.xValue];
SChartDataPoint *dataPointPixel = [[SChartDataPoint alloc] init];
dataPointPixel.xValue = [NSNumber numberWithFloat:xPixel];
dataPointPixel.yValue = [NSNumber numberWithFloat:yPixel];
[arrayPixelPoints addObject:dataPointPixel];
}
}
}
if (i == self.chart.series.count) [self drawCrosshairLines];
}
Related
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]
In the iOS game flappy bird, there are pipes that generate after a certain distance and they generate at random heights
I am also trying to make flappy bird pipes (I called it a tree branch in my code instead of pipe). Except the pipes are moving vertically instead of horizontally because it is a vertical scrolling game (it scrolls like the game doodle jump)
This is a drawing of what I want it to be: https://docs.google.com/drawings/d/18bxsVsNOlScCvgi1mwuzD2At7R6xKM3QCh6BfAVMuMo/edit?usp=sharing
(The horizontal lines are the branches)
So this is what I have tried to do so far to make the vertical branches (or pipes)...
in my .h
CCSprite *branch;
NSMutableArray *_branches;
CCSprite *obstacle;
CCNode *previousBranch;
CGFloat previousBranchYPosition;
in my .m
#implementation HelloWorldLayer
static const CGFloat firstBranchPosition = 426.f;
static const CGFloat distanceBetweenBranches = 140.f;
#define ARC4RANDOM_MAX 0x100000000
static const CGFloat minimumXPositionRightBranch = 280.f;
static const CGFloat maximumXPositionLeftBranch = 50.f;
static const CGFloat pipeDistance = 100.f;
static const CGFloat maximumXPositionRightBranch = maximumXPositionLeftBranch - pipeDistance;
setBranchInitialPosition method
/* This is where I am setting the initial position of the branches.
So I am specifying the position of the first branch and the other branches after it so it gets placed every time a certain distance is passed. I have a left branch and a right branch*/
-(void) setBranchInitialPosition {
CGFloat random = ((double)arc4random() / ARC4RANDOM_MAX);
CGFloat range = maximumXPositionRightBranch - minimumXPositionRightBranch;
_rightBranch.position = ccp(minimumXPositionRightBranch + (random * range), _rightBranch.position.y);
_leftBranch.position = ccp(_rightBranch.position.x + pipeDistance, _leftBranch.position.y);
}
spawnNewBranches method
// This is how I want the branches to spawn and I want to add them to an array full of branches
- (void)spawnNewBranches {
previousBranch = [_branches lastObject];
previousBranchYPosition = previousBranch.position.y;
if (!previousBranch) {
// this is the first obstacle
previousBranchYPosition = firstBranchPosition;
}
_rightBranch = [CCSprite spriteWithFile:#"branch.png"];
_leftBranch = [CCSprite spriteWithFile:#"branch.png"];
[_leftBranch addChild:_rightBranch];
[self setBranchInitialPosition];
obstacle = [CCSprite node];
[obstacle addChild:_leftBranch];
obstacle.position = ccp(160, previousBranchYPosition + distanceBetweenBranches);
[self addChild:obstacle];
[_branches addObject:obstacle];
}
scroll method
-(void) scroll:(ccTime)dt
{
// moves the bg
background.position = ccp(screenCenter.x, background.position.y + [[NSUserDefaults standardUserDefaults] integerForKey:#"scrollSpeed"]*dt);
bg2.position = ccp(screenCenter.x, background.position.y-background.contentSize.height);
// it adds the new bg's to the screen before the old bg's move off the screen
if (background.position.y >= screenSize.height*1.5)
{
background.position = ccp(screenCenter.x, (screenCenter.y)-(background.size.height/2));
} else if (bg2.position.y >= screenSize.height*1.5) {
bg2.position = ccp(screenCenter.x, (screenCenter.y)-(bg2.size.height/2));
}
// This is where I want them to appear every certain distance and also move with the brackground
obstacle.position = ccp(obstacle.position.x, obstacle.position.y*[[NSUserDefaults standardUserDefaults] integerForKey:#"scrollSpeed"]*dt);
NSMutableArray *offScreenObstacles = nil;
if (obstacle.position.y >= screenSize.height*1.5) {
[offScreenObstacles addObject:obstacle];
}
for (CCNode *obstacleToRemove in offScreenObstacles) {
[obstacleToRemove removeFromParent];
[_branches removeObject:obstacleToRemove];
// for each removed obstacle, add a new one
[self spawnNewBranches];
}
}
Right now, the branches are appearing, but they stay in the bottom left corner and they dont move or spawn at all. I want to make them move with the background and spawn after a certain distance while also being generated in random heights. I provided you with all my code, do you know how I can make this work? Thanks in advance!
You may want to try placement of the pipes based on a trigonometric curve like sine or cosine (https://en.wikipedia.org/wiki/Trigonometric_functions). It seems like you are placing the pipes within a fairly define random range though if you change this range to an offset from the plot of the trigonometric curve it would take into account the ability of the player to transition between the open gaps better. At least that's my feel. I think the code would be easier to follow as well as I'm a bit confused going through it. You can also easily vary the difficulty of the curve by changing the parameters such as increasing the amplitude or frequency.
I created a copy of Flappy Bird just for fun. I used this code to create the pipes:
-(void)createPipes{
//Create Random
int from = 65;
int max = [[UIScreen mainScreen] bounds].size.height - 124;
int delta = max - from - dy;
int y = from + arc4random() % (delta - from);
//Pipe Bottom
UIImageView *pipeBottom = [[UIImageView alloc] init];
[pipeBottom setContentMode:UIViewContentModeTop];
[pipeBottom setImage:[UIImage imageNamed:#"pipeBottom"]];
[pipeBottom setFrame:CGRectMake(320, y+dy, 60, max - y - dy)];
[pipeBottom setClipsToBounds:YES];
//Pipe Top
UIImageView *pipeTop = [[UIImageView alloc] init];
[pipeTop setFrame:CGRectMake(320, 0, 60, y)];
[pipeTop setContentMode:UIViewContentModeBottom];
[pipeTop setImage:[UIImage imageNamed:#"pipeTop"]];
[self.view insertSubview:pipeTop atIndex:1];
[self.view insertSubview:pipeBottom atIndex:1];
if (!self.pipes)
self.pipes = [[NSMutableArray alloc] init];
[self.pipes addObject:pipeBottom];
[self.pipes addObject:pipeTop];
}
and to move them:
-(void)moveArray:(NSMutableArray *)array{
float ds = dv * dt;
NSMutableArray *trash = [NSMutableArray array];
for (UIImageView *obj in array) {
CGRect frame = obj.frame;
frame.origin.x -= ds;
if (frame.origin.x < -frame.size.width) {
[obj removeFromSuperview];
[trash addObject:obj];
}else{
obj.frame = frame;
}
}
[array removeObjectsInArray:trash];
}
-(void)movePipes{
[self moveArray:self.pipes];
}
I call this function every 0.01 seconds, to run the game:
-(void)runGame{
_time += dt;
if (_time >= 180.0/dv) {
_time = 0;
[self createPipes];
}
[self movePipes];
[self moveEnemies];
[self moveYoshi];
[self moveBar];
[self verifyScore];
[self verifyCollision];
[self verifyState];
}
I defined dt = 0.01 and dv = 110.
You can see my parody in youtube: (http://www.youtube.com/watch?v=tTcYdpSIKJg)
I hope this help you.
Best, Rafael Castro.
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!
I am new to cocos2d so please help me if u can
I have background moving from right to left, and the background contains small windows with 3 rows of windows
_spaceDust1 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
_spaceDust2 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
CGPoint dustSpeed = ccp(0.1 , 0.1);
CGPoint bgSpeed = ccp(0.05 , 0.05);
[_backgroundNode addChild:_spaceDust1 z:0 parallaxRatio:dustSpeed positionOffset:ccp(0,winSize.height / 2)];
[_backgroundNode addChild:_spaceDust2 z:0 parallaxRatio:dustSpeed positionOffset:ccp(_spaceDust1.contentSize.width , winSize.height / 2)];
Now add enemies wich also move from right to left with same speed
_robbers = [[CCArray alloc] initWithCapacity:kNumAstroids];
for (int i = 0; i < kNumAstroids; ++i) {
CCSprite *asteroid = [CCSprite spriteWithSpriteFrameName:#"robber.png"];
asteroid.visible = NO;
[_batchNode addChild:asteroid];
[_robbers addObject:asteroid];
}
in update method:
double curTime = CACurrentMediaTime();
if (curTime > _nextRunemanSpawn) {
float randSecs = [self randomValueBetween:0.20 andValue:1.0];
_nextRunemanSpawn = randSecs + curTime;
float randY = 80.0;
float randY1 = 185.0;
float randY2 = 293.0;
float randDuration = [self randomValueBetween:5.2 andValue:5.2];
float randDuration1 = [self randomValueBetween:1.0 andValue:1.0];
CCSprite *asteroid = [_robbers objectAtIndex:_nextRobber];
_nextRobber++;
if (_nextRobber >= _robbers.count) {
_nextRobber = 0;
}
//[asteroid stopAllActions];
int winChoice = arc4random() % 3;
if (winChoice == 0) {
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY);
asteroid.visible = YES;
}
else if(winChoice == 1){
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY1);
asteroid.visible = YES;
}else {
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY2);
asteroid.visible = YES;
}
[asteroid runAction:[CCSequence actions:[CCMoveBy actionWithDuration:randDuration position:ccp(-winSize.width-asteroid.contentSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:#selector(setInvisible:)],nil]];
All is going well but i want to set this enemies in to window and in random position
so how can i set x-argument of enemies so it can be fix in to window of the background?
For some reason I cannot comment so this is written as an answer. What exactly are you trying to do? I am a bit confused. It sounds like you want the X position to be set so that it is in one of 3 random positions, is that correct?
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